Added public files

Roughly added all public files. Probably missed some, though.
diff --git a/d/seher/haeuser/moebel/schluesselbrett.c b/d/seher/haeuser/moebel/schluesselbrett.c
new file mode 100644
index 0000000..93bace6
--- /dev/null
+++ b/d/seher/haeuser/moebel/schluesselbrett.c
@@ -0,0 +1,679 @@
+//---------------------------------------------------------------------------
+// Name des Objects:    Schluesselbrett
+// Letzte Aenderung:    02.06.2001
+// Original:            Swift
+// Neue Version:        Seleven
+// Übernommen:          23.01.2006
+//---------------------------------------------------------------------------
+/* Changelog
+   * 21.06.2007, Zesstra
+     im inherit abs. Pfad durch LADEN(x) ersetzt.
+   * 01.07.2007, Zesstra
+     GetOwner() definiert.
+   * 11.05.2015, Arathorn
+     weitraeumig ueberarbeitet zur Behebung von Fehlfunktionen
+*/
+#pragma strong_types, rtt_checks
+
+#include "/d/seher/haeuser/moebel/schrankladen.h"
+#include <ansi.h>
+inherit  LADEN("swift_std_container");
+
+#define VERSION_OBJ "3"
+// Zur Indizierung des Mappings <haken>
+#define HOOK_OB    0
+#define HOOK_LABEL 1
+
+private varargs string KeyDescription(int keynum);
+private int IsValidKey(object ob);
+private int GetNextFreeSlot();
+private object GetKeyForLabel(string label);
+private int GetHookNumberForKey(object ob);
+private void ConsolidateInventory();
+
+// Struktur: ([ haken_nr : objekt; beschriftung ])
+private mapping haken=([:2]);
+private int hooklist_long = 0;
+
+protected void create() {
+  if (!clonep(TO)) {
+    set_next_reset(-1);
+    return;
+  }
+  ::create();
+  
+  foreach(int i: 1..16) {
+    m_add(haken, i);
+  }
+
+  // DEBUG! Sonst funktioniert's ausserhalb von Seherhaeusern nicht.
+  SetProp("test",1);
+  SetProp(P_SHORT, "Ein Schluesselbrett");
+  Set(P_LONG, function string () {
+    string text = BS(
+      "Ein kleines Brett aus Holz, an dem in 2 Reihen je 8 Haken befestigt "
+      "sind, an die man insgesamt 16 Schluessel haengen kann. Um die "
+      "Uebersicht nicht zu verlieren, befindet sich ueber jedem der Haken "
+      "ein kleines Schildchen aus Schiefer, das sich beschriften laesst. "
+      "Auf diese Weise geht der Sinn und Zweck des Schluessels, der an "
+      "diesem Haken ruht, nicht verloren. Praktischerweise haengt an einer "
+      "Schnur ein Stueck Kreide von dem Brett herab, mit dem Du die "
+      "Schildchen beschriften kannst.");
+    text += KeyDescription(0);
+    return text; 
+  }, F_QUERY_METHOD);
+
+  AddId(({"schluesselbrett","brett"}));
+  SetProp("cnt_version_obj", VERSION_OBJ);
+  SetProp(P_CNT_STATUS, 0);
+  SetProp(P_NAME, "Schluesselbrett");
+  SetProp(P_GENDER, NEUTER);
+  SetProp(P_WEIGHT, 1000);         // Gewicht 1 Kg
+  SetProp(P_MAX_WEIGHT, 1000000);  // Es passen fuer 1000 kg Sachen rein.
+  SetProp(P_WEIGHT_PERCENT, 100);  // Dafuer macht er auch nix leichter :)
+  SetProp(P_MAX_OBJECTS, 16);
+  // Man soll nichts ueber den normalen Mechanismus reinstecken oder 
+  // rausnehmen koennen. Wird von put_and_get.c geprueft.
+  SetProp(P_CONTAINER, 0);
+  SetProp(P_NOINSERT_MSG, BS("An das Schluesselbrett kannst Du nur "
+    "Schluessel haengen.\nSyntax: 'haenge <schluessel> an brett'."));
+  SetProp(P_NOGET, "Das geht nicht. "+Name(WER,1)+
+    " laesst sich nicht bewegen.\n");
+  SetProp(P_MATERIAL, ({MAT_OAK, MAT_MISC_MAGIC}) );
+  SetProp(P_INFO, "Versuch's mal mit: \"skommandos schluesselbrett\".\n");
+
+  AD(({"holz"}), BS("Echte, robuste Eiche."));
+
+  RemoveCmd(({"schliesse","schliess","oeffne"}));
+
+  AddCmd(({"unt", "untersuche", "betrachte", "betracht", "betr",
+           "schaue", "schau"}), "unt_haken");
+  AddCmd(({"haeng", "haenge"}), "schluessel_an_haken_haengen");
+  AddCmd(({"nimm"}), "schluessel_von_haken_nehmen");
+  AddCmd("sbeschrifte", "cmd_beschriften");
+  AddCmd("sliste", "cmd_sliste");
+}
+
+/***** KOMMANDOFUNKTIONEN *****/
+
+// Konfiguration der Schluesselliste, entweder "alle" anzeigen oder nur die
+// belegten Haken.
+static int cmd_sliste(string str) {
+  notify_fail("Syntax: 'sliste alle|belegte'.\n");
+  if ( !stringp(str) || !sizeof(str) )
+    return 0;
+
+  if ( str == "alle" ) {
+    if ( !hooklist_long ) {
+      tell_object(TP, BS("Ok, es werden jetzt immer alle Haken "
+        "aufgelistet."));
+      hooklist_long = 1;
+    }
+    else 
+      tell_object(TP, BS("Die Einstellung war schon 'alle'."));
+  }
+  else if ( str == "belegte" ) {
+    if ( hooklist_long ) {
+      tell_object(TP, BS("Ok, es werden jetzt nur die belegten Haken "
+        "aufgelistet."));
+      hooklist_long =0;
+    }
+    else 
+      tell_object(TP, BS("Die Einstellung war schon 'belegte'."));
+  }
+  else 
+    return 0;
+  return 1;
+}
+
+// Haken untersuchen. Ohne Angabe der Nummer werden alle ausgegeben,
+// ansonsten der gewuenschte, sofern es diesen gibt.
+static int unt_haken(string str) {
+  if ( !stringp(str) || !sizeof(str) || strstr(str, "haken") == -1 )
+    return 0;
+  
+  int hakennr;
+  if ( sscanf(str, "haken %d", hakennr) != 1 ) {
+    string det = KeyDescription(hakennr);
+    tell_object(TP, sizeof(det) ? det : "Die Haken sind alle leer.\n");
+    return 1;
+  }
+
+  notify_fail("Welchen Haken moechtest Du untersuchen? Es gibt nur die "
+    "Haken 1 bis 16.\n");
+  if( hakennr<1 || hakennr > 16 )
+    return 0;
+
+//  ausgabe_haken(hakennr);
+  tell_object(TP, KeyDescription(hakennr));
+  return 1;
+}
+
+// Schluessel aufhaengen.
+static int schluessel_an_haken_haengen(string str) {
+  notify_fail("Was willst Du woran haengen?\n");
+  if(!stringp(str) || !sizeof(str))
+    return 0;
+  
+  notify_fail("Das darfst Du nicht.\n");
+  if(!erlaubt())
+    return 0;
+
+  string was, woran;
+  notify_fail("Folgende Syntax versteht Dein Schluesselbrett:\n"
+    "  \"haenge <schluesselname> an haken [nr]\" oder\n"
+    "  \"haenge <schluesselname> an [schluessel]brett\".\n");
+  if ( sscanf(str, "%s an %s", was, woran) != 2 )
+    return 0;
+
+  notify_fail("Was moechtest Du an das Schluesselbrett haengen?\n");
+  if ( !stringp(was) ) 
+    return 0;
+
+  notify_fail("Woran willst Du das denn haengen?\n");
+  if ( strstr(woran, "haken", 0)==-1 && !id(woran) )
+    return 0;
+
+  // Test, ob es Objekte dieses Namens gibt.
+  object *obs = TP->find_obs(was, PUT_GET_NONE);
+  // Aus der Liste der gefundenen Objekte alle rausfiltern, die keine
+  // Schluessel sind. Als Schluessel gelten hier alle Objekte, die die
+  // ID "schluessel" haben oder die mittels QueryDoorKey() einer bestimmten
+  // Tuer zugeordnet sind.
+  obs = filter(obs, #'IsValidKey);
+  notify_fail("Keine(n) Schluessel mit dem Namen gefunden.\n");
+  if ( !sizeof(obs) )
+    return 0;
+
+  int nr = GetNextFreeSlot();
+  notify_fail("Am Schluesselbrett ist kein Haken mehr frei.\n");
+  if ( !nr )
+    return 0;
+
+  // Wenn nur ein Schluessel gefunden wurde, wird genauer geschaut, ob
+  // der Spieler gesagt hat, wo der hinsoll.
+  if ( sizeof(obs) == 1 ) {
+    if ( woran != "haken" || !id(woran) )
+      sscanf(woran, "haken %d", nr);
+  }
+
+  notify_fail("Es gibt nur die Haken 1 bis 16.\n");
+  if ( nr < 1 || nr > 16 )
+    return 0;
+
+  notify_fail("Der Haken ist nicht mehr frei.\n");
+  if ( objectp(haken[nr,HOOK_OB]) )
+    return 0;
+
+  // Alle gefundenen Schluesselobjekte ans Brett haengen.
+  foreach( object key : obs ) {
+    switch (key->move(ME, M_PUT)) {
+      // Die Kapazitaet ist ueber P_MAX_OBJECTS begrenzt. Alle
+      // Schleifendurchlaeufe, die die Kapazitaetsgrenze erreichen, landen
+      // in default und beenden damit die Schleife. Daher muessen sie 
+      // nicht gesondert behandelt werden.
+      case MOVE_OK :
+        // Falls das Brett voll sein sollte, aber faelschlicherweise 
+        // dennoch ein weiterer Schluessel hineinbewegt wurde, ist nr==0,
+        // und es wird ein Fehler ausgeloest, damit nicht hinterher ein
+        // Schluessel an Haken 0 haengt.
+        if ( !nr ) {
+          raise_error("Fehler: Haken 0 darf nicht belegt werden.");
+          break;
+        }
+        tell_object(TP, BS("Du haengst "+obs[0]->name(WEN,1)+" an Haken "+
+          nr+"."));
+        // Nur wenn ein Schluessel erfolgreich drangehaengt wurde, wird er
+        // in die Liste eingetragen. Etwaige Beschriftungen bleiben bestehen,
+        haken[nr, HOOK_OB] = key;
+        // und nur dann wird der naechste Slot probiert. Wenn nur ein 
+        // Schluessel verarbeitet wird, ist das hier unschaedlich, weil es 
+        // nicht nochmal ausgewertet wird.
+        nr = GetNextFreeSlot();
+        // Schluessel wurde verarbeitet und Meldung ausgegeben
+        obs -= ({key});
+        break;
+      case ME_CANT_LEAVE_ENV :
+      case ME_CANT_BE_INSERTED :
+      case ME_CANT_BE_DROPPED :
+        // Schluessel wurde nicht erfolgreich drangehaengt.
+        tell_object(TP, BS(key->Name(WER,1)+" weigert sich, ans "
+          "Schluesselbrett gehaengt zu werden."));
+        // Meldung wurde ausgegeben, nichts weiter zu tun.
+        obs -= ({key});
+        break;
+      // TOO_MANY_OBJECTS wird nicht hier behandelt, sondern weiter unten,
+      // um eine Auflistung der Schluesselnamen ausgeben zu koennen, statt
+      // eine Zeile pro Schluessel. Die anderen Rueckgabewerte von move() 
+      // sind bei dieser Art der Bewegung nicht zu erwarten.
+      default: break;
+    }
+  }
+  // Fuer alle uebrigen war wohl TOO_MANY_OBJECTS der Rueckgabewert, denn
+  // das Schluesselbrett erlaubt nur P_MAX_OBJECTS = 16.
+  if ( sizeof(obs) ) {
+    tell_object(TP, BS("Fuer "+CountUp(obs->name(WEN))+" ist leider kein "
+      "Haken mehr am Schluesselbrett frei."));
+  }
+  return 1;
+}
+
+// Schluessel abnehmen
+static int schluessel_von_haken_nehmen(string str) {
+  if( !stringp(str) || !sizeof(str) ) 
+    return 0;
+
+  string was, woraus;
+  if ( sscanf(str, "%s aus %s", was, woraus) != 2 && 
+       sscanf(str, "%s von %s", was, woraus) != 2 )
+    return 0;
+
+  notify_fail("Das darfst Du nicht!\n");
+  if(!erlaubt()) 
+    return 0;
+ 
+  notify_fail("Woraus willst Du das denn nehmen?\n");
+  if ( !id(woraus) && strstr(woraus, "haken", 0) == -1 ) 
+    return 0;
+
+  notify_fail("Was willst Du denn vom Schluesselbrett nehmen?\n");
+  if( !stringp(was) ) 
+    return 0;
+  
+  notify_fail("Das Schluesselbrett ist doch aber leer.\n");
+  if ( !sizeof(all_inventory(ME)) )
+    return 0;
+
+  int hakennr;
+  object *keys = ({});
+  // Es muessen auch Schluessel von bestimmten Haken abgenommen
+  // werden koennen, nicht nur jeweils der erste mit der angegebenen ID.
+  // Dies hat auch Prio gegenueber der allgemeinen Begriffssuche mit
+  // find_obs().
+  if ( sscanf(woraus, "haken %d", hakennr) == 1 ) {
+    notify_fail("Es gibt nur die Haken 1 bis 16.\n");
+    if ( hakennr < 1 || hakennr > 16 ) 
+      return 0;
+    notify_fail("An diesem Haken haengt aber doch kein Schluessel.\n");
+    if ( !objectp(haken[hakennr,HOOK_OB]) )
+      return 0;
+    
+    keys += ({ haken[hakennr,HOOK_OB] });
+  }
+
+  // Keiner gefunden? Vielleicht ist ja einer mit einer bestimmten 
+  // Beschriftung gemeint?
+  if ( !sizeof(keys) ) {
+    // Schluessel mit dem angegebenen Namen suchen
+    keys = TP->find_obs(was+" in brett in raum");
+    // Hakennummer ermitteln, wenn eine Beschriftung angegeben wurde.
+    string wanted_desc;
+    sscanf(was, "schluessel fuer %s", wanted_desc);
+    object wanted_key = GetKeyForLabel(wanted_desc);
+    if ( objectp(wanted_key) )
+      keys += ({wanted_key});
+  }
+  // Immer noch nix gefunden? Dann muss sich der Spieler was anderes 
+  // einfallen lassen.
+  notify_fail("Am Schluesselbrett haengt kein solcher Schluessel.\n");
+  if ( !sizeof(keys) )
+    return 0;
+
+  // Alle gefundenen Schluessel verarbeiten. Alle, die nicht bewegt werden
+  // koennen, verbleiben in der Liste.
+  foreach( object key : keys ) {
+    int current_hook = GetHookNumberForKey(key);
+    if ( key->move(TP,M_GET) == MOVE_OK ) {
+      keys -= ({ key });
+      tell_object(TP, BS("Du nimmst "+key->name(WEN,1)+" von Haken "+
+        current_hook+" ab."));
+      // Schluessel austragen; bestehende Beschriftungen bleiben erhalten
+      haken[current_hook, HOOK_OB] = 0;
+    }
+  }
+  // Von allen nicht vom Haken genommenen Schluesseln die Namen ermitteln
+  // und auflisten.
+  if ( sizeof(keys) ) {
+    tell_object(TP, BS(capitalize(CountUp(keys->name(WER,1)))+" "+
+      (sizeof(keys)==1?"konnte":"konnten")+" nicht vom Schluesselbrett "
+      "abgenommen werden."));
+  }
+  return 1;
+}
+
+// Verfuegbare Kommandos auflisten.
+varargs int skommandos(string str) {
+  notify_fail( "Fehler: Dieser Befehl benoetigt eine gueltige Objekt-Id als "
+    +"Parameter.\nBeispiel: skommandos [schluessel]brett\n");
+  
+  if( !id(str) ) return 0;
+
+  write(
+    "==================================================================="
+    "==========\n"
+    "Aktuelle Version: "+QueryProp("cnt_version_std")+
+      QueryProp("cnt_version_obj")+"\n"+
+    BS(Name(WER,1)+" kann nur in diesem Seherhaus verwendet werden, "
+    "da "+QueryPronoun(WER)+" speziell dafuer gekauft wurde. "+
+    CAP(QueryPronoun(WER))+" verfuegt ueber folgende Kommandos:")+
+    "----------------------------------------------------------------------"
+    "-------\n"
+    "serlaube [schluessel]brett [spielername|\"hausfreunde\"|\"zweities\"]\n"
+    "  Erlaubt Personen, "+name(WEN,1)+" mitzubenutzen.\n"
+    "  serlaube ohne Angabe eines Arguments listet alle Personen mit Zugriff "
+      "auf\n  "+name(WEN,1)+" auf.\n\n"
+    "verschiebe [schluessel]brett nach [ausgang]\n"
+    "  Damit kannst Du "+name(WEN,1)+" innerhalb Deines Seherhauses "
+    "verschieben.\n\n"
+    "sverstecke [schluessel]brett\n"
+    "  Damit machst Du "+name(WEN,1)+" unsichtbar.\n"
+    "shole [schluessel]brett hervor\n"
+    "  Damit machst Du "+name(WEN,1)+" wieder sichtbar.\n"
+    "zertruemmer [schluessel]brett\n"
+    "  Damit zerstoerst Du das Schluesselbrett.\n"
+    "sbeschrifte haken <nr> mit <text>|nichts\n"
+    "  Eine Beschriftung an einem Haken anbringen oder loeschen.\n"
+    "sliste lang|kurz\n"
+    "  Schluesselanzeige konfigurieren: alle oder nur belegte auflisten.\n"
+    "haenge <schluesselname> an haken [nr]\n"
+    "haenge <schluesselname> an [schluessel]brett\n"
+    "  Damit haengst Du einen Schluessel an das Brett.\n"
+    "nimm schluessel von haken <nr>\n"
+    "nimm <schluesselname> von [schluessel]brett\n"
+    "nimm schluessel fuer <beschriftung> von [schluessel]brett.\n"
+    "  Damit nimmst Du einen Schluessel vom Schluesselbrett.\n"
+    "==================================================================="
+    "==========\n");
+  return 1;
+}
+
+static int erlaubnis_liste()
+{
+  if( QueryHausbesitzer() != QueryTP() && !QueryProp("test") )
+  {
+    tell_object(TP, BS("Nur "+QueryHausbesitzer()+" darf Berechtigungen "
+      +name(WESSEN,1)+" abfragen!"));
+    return 1;
+  }
+  
+  string *strs;
+  string str = 
+    "==================================================================="
+    "==========\n";
+  if ( hauserlaubnis ) {
+    strs = SHVERWALTER->HausProp(LOWER(QueryHausbesitzer()), 2);
+    str += "Folgende Freunde Deines Hauses duerfen "+name(WEN,1)+
+      " mitbenutzen:\n";
+    if(sizeof(strs)) {
+      str += BS(CountUp(strs));
+    }
+    else {
+      str += BS("Du hast keiner Person Zugriff auf Dein Haus gewaehrt...");
+    }
+  }
+  else {
+    str+= BS("Die Freunde Deines Hauses duerfen "+name(WEN,1)+
+      " nicht mitbenutzen.");
+  }
+
+  str += 
+    "-------------------------------------------------------------------"
+    "----------\n";
+
+  if( zweitieerlaubnis!="" ) {
+    if( zweitieerlaubnis==geteuid(TP) )
+      str += BS("Alle Deine Zweities duerfen "+name(WEN,1)+" mitbenutzen.");
+    else
+      str += BS("Alle Zweities von "+CAP(zweitieerlaubnis)+" duerfen "
+        +name(WEN,1)+" mitbenutzen.");
+    str +=
+      "-------------------------------------------------------------------"
+      "----------\n";
+  }
+  
+  strs=QueryProp("cnt_erlaubnis");
+
+  if(sizeof(strs)) {
+    str += BS("Folgende sonstige Personen duerfen "+name(WEN,1)+
+      " mitbenutzen:");
+    str += CountUp(strs);
+  }
+  else {
+    str += BS("Es gibt keine sonstigen Personen, die "+name(WEN,1)+
+      " mitbenutzen duerfen.");
+  }
+  str +=
+    "==================================================================="
+    "==========\n";
+  
+  return 1;
+}
+
+// Haken beschriften.
+static int cmd_beschriften(string str) {
+  string nfm = "Syntax: sbeschrifte haken <hakennummer> mit "
+    "<text>|nichts\n"
+    "Fuer <text> kannst Du einen beliebigen Text mit bis zu 30 Zeichen "
+    "angeben,\n"
+    "'nichts' loescht die Beschriftung eines Schildes.\n";
+
+  notify_fail("Das darfst Du nicht!\n");
+  if(!erlaubt()) 
+    return 0;
+  
+  string text;
+  int hnr;
+  str = TP->_unparsed_args(0);
+  notify_fail(nfm);
+  if ( !stringp(str) || !sizeof(str) ) 
+    return 0;
+  if ( sscanf(str,"haken %d mit %s", hnr, text)!=2 )
+    return 0;
+
+  notify_fail("Welchen Haken moechtest Du beschriften?\n"+nfm);
+  if(!hnr) 
+    return 0;
+  
+  notify_fail("Es gibt nur die Haken 1 bis 16.\n");
+  if ( hnr < 1 || hnr > 16 ) 
+    return 0;
+
+  notify_fail("Womit moechtest Du den Haken beschriften?\n"+nfm);
+  if( !stringp(text) || !sizeof(text) )
+    return 0;
+
+  notify_fail("Der Text sollte nicht laenger als 30 Zeichen sein.\n");
+  if(sizeof(text) > 30) 
+    return 0;
+
+  if ( lower_case(text) == "nichts" ) {
+    if ( stringp(haken[hnr,HOOK_LABEL]) ) {
+      tell_object(TP, BS("Sorgfaeltig wischst Du die Beschriftung des "
+        "Hakens "+hnr+" weg."));
+      haken[hnr,HOOK_LABEL]=0;
+    }
+    else {
+      tell_object(TP, BS("Du musst Dich geirrt haben. Haken "+hnr+" ist "
+        "gar nicht beschriftet."));
+    }
+  }
+  else if ( lower_case(text) == "schluessel" ) {
+    tell_object(TP, BS(
+      "Du solltest Dir etwas Aussagekraeftigeres einfallen lassen."));
+  }
+  else {
+    tell_object(TP, BS("Du beschriftest das Schildchen ueber Haken "+hnr+
+      " mit "+text+"."));
+    tell_room(environment(), BS(
+      TP->Name(WER)+" schreibt etwas ans Schluesselbrett."), ({TP}));
+    haken[hnr,HOOK_LABEL] = text;
+  }
+  return 1;
+}
+
+/***** HILFSFUNKTIONEN *****/
+
+// Beschreibung eines Hakens erzeugen. Wenn <keynum> angegeben wird, 
+// wird nur der gesuchte Haken angezeigt, egal ob leer oder nicht. 
+// Ansonsten werden die belegten Haken angezeigt, ausser wenn 
+// <hooklist_long> gesetzt ist. Dann werden alle Haken aufgelistet.
+private varargs string KeyDescription(int keynum) {
+  string desc="";
+  mapping keylist=([:2]);
+
+  // Liste derHaken auf den/die gesuchten einschraenken.
+  // Nur ein Haken soll angeschaut werden. Dies hat immer Prioritaet,
+  // d.h. auch bei <sliste alle> wird nur ein Haken gelistet.
+  if ( keynum ) {
+    m_add(keylist, keynum, m_entry(haken, keynum)...);
+  }
+  // Wenn <sliste alle> gesetzt ist, alle ausgeben.
+  else if ( hooklist_long ) {
+    keylist = haken;
+  }
+  // Ansonster nur die belegten.
+  else {
+    keylist = filter(haken, 
+      function int (int num, <string|object>* values) {
+        return objectp(values[0]);
+      });
+  }
+
+  // Ueber das Mapping mit den selektierten Haken laufen und Ausgaben
+  // erzeugen.
+  foreach(int num : 1..16) {
+    // Hier wird der aktuelle Wert uebersprungen, wenn er nicht enthalten 
+    // ist, da die Zuweisung in <key> und <keydesc> auch dann eine 0 ergeben
+    // wuerde, und somit Keys mit leeren Values nicht von nicht existierenden
+    // Keys zu unterscheiden waeren.
+    if ( member(m_indices(keylist), num) == -1 )
+      continue;
+    string keydesc = keylist[num, HOOK_LABEL];
+    object key = keylist[num, HOOK_OB];
+    desc += "Haken " + num + (stringp(keydesc)?" ["+keydesc+"]: " : ": ");
+    desc += (objectp(key) ? key->short() : "leer\n");
+  }
+  return desc;
+}
+
+// Rueckgabewert: 1, wenn es sich um ein Schluesselobjekt handelt, sonst 0.
+// Schluessel werden anhand der ID "schluessel" erkannt oder daran, dass
+// QueryDoorKey() einen Wert zurueckgibt, d.h. dass dieses Objekt eine
+// Standard-Doormaster-Tuer oeffnet.
+private int IsValidKey(object ob) {
+  return (objectp(ob) && (ob->id("schluessel") || ob->QueryDoorKey()!=0));
+}
+
+// Gibt die Nummer des naechsten freien Hakens zurueck oder 0, wenn
+// keiner frei ist. Da Mappings nicht sortiert sind, muss von 1-16 numerisch
+// iteriert werden, damit immer der erste freie Haken gefunden wird und nicht 
+// irgendeiner.
+// Rueckgabewert: Hakennummer von 1-16 oder 0, wenn kein Haken frei ist.
+private int GetNextFreeSlot() {
+  int slot;
+  foreach(int i : 1..16) {
+    if ( !objectp(haken[i,HOOK_OB]) ) {
+      slot = i;
+      break;
+    }
+  }
+  return slot;
+}
+
+// Gibt zu einer Schild-Beschriftung die zugehoerige Hakenposition aus.
+// Es wird die kleingeschriebene Variante gesucht, so dass Spieler zwar
+// Grossschreibung in den Beschriftungen verwenden koennen, diese aber
+// nicht beruecksichtigt wird. Ansonsten koennte es zu Schwierigkeiten mit
+// Dopplungen wie "Vesray" und "vesray" kommen.
+// Rueckgabewert: Hakennummer von 1-16 oder 0, wenn der Text nicht gefunden
+// wurde.
+private object GetKeyForLabel(string label) {
+  if ( label ) 
+    label = lower_case(label);
+  foreach(int i: 1..16) {
+    if ( haken[i, HOOK_LABEL] && label &&
+         lower_case(haken[i,HOOK_LABEL]) == label )
+      return haken[i,HOOK_OB];
+  }
+  return 0;
+}
+
+// Gibt zu dem uebergebenen Objekt die Nummer des Hakens zurueck, an dem
+// es haengt.
+private int GetHookNumberForKey(object ob) {
+  foreach(int num, object key : haken) {
+    if ( key == ob )
+      return num;
+  }
+  return 0;
+}
+
+// Bereinigt Datenstruktur in <haken> und das Inventar des Brettes, indem
+// a) geprueft wird, ob alle in <haken> eingetragenen Objekte sich auch im 
+//    Brett befinden und
+// b) geprueft wird, ob alle im Inventar befindlichen Objekte auch korrekt in 
+//    <haken> eingetragen sind.
+// Ueberzaehlige Objekte werden rausgeworfen.
+private void ConsolidateInventory() {
+  foreach(int i, object ob : haken) {
+    // Objekt existiert, ist aber nicht im Schluesselbrett? Dann austragen.
+    if (objectp(ob) && environment(ob) != ME) {
+      m_delete(haken, i);
+      // Wird auch vom reset() aus gerufen, dann ist TP == 0.
+      if ( objectp(TP) )
+        tell_object(TP, BS("Hoppla! "+ob->Name(WER,1)+" scheint vom "
+          "Schluesselbrett verschwunden zu sein!"));
+    }
+  }
+  object *resterampe = all_inventory(ME) - m_values(haken, HOOK_OB);
+  if (sizeof(resterampe)) {
+    resterampe->move(environment(),M_PUT);
+    tell_room(environment(), BS("Ploetzlich faellt etwas vom "
+      "Schluesselbrett zu Boden, das Du zuvor gar nicht gesehen hattest."));
+  }
+}
+
+string short() {
+  string sh=QueryProp(P_SHORT);
+
+  // Unsichtbar? Dann gibts nichts zu sehen ...
+  if ( QueryProp(P_INVIS) || !sh )
+    return 0;
+  if ( QueryProp("versteckt") == 1 ) 
+    sh = "";
+  else 
+    sh += ".";
+
+  return process_string(sh)+"\n";
+}
+
+// Im Reset vorsichtshalber auch mal aufraeumen.
+void reset() {
+  ConsolidateInventory();
+  return ::reset();
+}
+
+varargs int PreventInsert(object ob) {
+  // Kein Schluessel? Hat in diesem Container nix verloren!
+  if( !IsValidKey(ob )) {
+    return 1;
+  }
+  ConsolidateInventory();
+  return ::PreventInsert(ob);
+}
+
+// Wenn ein Schluessel sich aufloest, muss die Liste abgeglichen werden.
+void NotifyRemove(object ob) {
+  tell_room(environment(ME), BS("Hoppla! "+ob->Name(WER,1)+" hat sich "
+    "gerade in Wohlgefallen aufgeloest."));
+  // Das Objekt <ob> zerstoert sich erst nach dem Aufruf dieser Funktion,
+  // daher per call_out() aufraeumen.
+  call_out(#'ConsolidateInventory, 1);
+}
+
+// Zesstra, 1.7.07, fuers Hoerrohr
+// Darf ich da Arathorn eintragen? ;-)
+string GetOwner() {
+  return "seleven";
+}
+