Added public files

Roughly added all public files. Probably missed some, though.
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]);
+  }
+}