Added public files

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