Added public files

Roughly added all public files. Probably missed some, though.
diff --git a/std/room/commands.c b/std/room/commands.c
new file mode 100644
index 0000000..5aa75c3
--- /dev/null
+++ b/std/room/commands.c
@@ -0,0 +1,37 @@
+// MorgenGrauen MUDlib
+//
+// room/commands.c -- room commands handling
+//
+// $Id: commands.c 8201 2012-11-07 17:55:12Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/thing/commands";
+
+//#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <config.h>
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+
+void init() 
+{
+  ::init();
+
+  add_action("imposs", "such");
+  add_action("imposs", "suche");
+}
+
+/* Fuer etwas bessere Fehlermeldungen als 'Wie bitte?' bei einigen */
+/* allgemeinen Kommandos.					   */
+int imposs()
+{
+  _notify_fail("Du suchst, findest aber nichts.\n");
+  return 0;
+}
diff --git a/std/room/description.c b/std/room/description.c
new file mode 100644
index 0000000..60d3b28
--- /dev/null
+++ b/std/room/description.c
@@ -0,0 +1,222 @@
+// MorgenGrauen MUDlib
+//
+// room/description.c -- room description handling
+//
+// $Id: description.c 9468 2016-02-19 21:07:04Z Gloinson $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/container/description";
+
+#define NEED_PROTOTYPES
+
+#include <properties.h>
+#include <defines.h>
+#include <wizlevels.h>
+#include <language.h>
+#include <doorroom.h>
+
+void create()
+{
+  ::create();
+  SetProp(P_NAME, "Raum");
+  SetProp(P_INT_SHORT,"<namenloser Raum>");
+  SetProp(P_INT_LONG,0);
+  SetProp(P_ROOM_MSG, ({}) );
+  SetProp(P_FUNC_MSG, 0);
+  SetProp(P_MSG_PROB, 30);
+  AddId(({"raum", "hier"}));
+}
+
+void init()
+{
+  // Wenn P_ROOM_MSG gesetzt oder P_FUNC_MSG und kein Callout laeuft,
+  // Callout starten.
+  mixed roommsg = QueryProp(P_ROOM_MSG);
+  if( ( (roommsg && sizeof(roommsg)) ||
+        QueryProp(P_FUNC_MSG) ) &&
+      (find_call_out("WriteRoomMessage")==-1))
+    call_out("WriteRoomMessage", random(QueryProp(P_MSG_PROB)));
+}
+
+varargs void AddRoomMessage(string *mesg, int prob, mixed func)
+{
+  if (mesg && !pointerp(mesg))
+    raise_error(sprintf(
+      "AddRoomMessage(): wrong argument type, expected Array or 0, "
+      "got %.20O",mesg));
+
+   SetProp(P_ROOM_MSG, mesg);
+
+  if (prob>0)
+    SetProp(P_MSG_PROB, prob);
+
+  if (func)
+    SetProp(P_FUNC_MSG, func);
+}
+
+static void WriteRoomMessage()
+{
+  int tim, msgid;
+  string *room_msg,func;
+  mixed *func_msg;
+
+  room_msg = (string *)QueryProp(P_ROOM_MSG);
+  func_msg = QueryProp(P_FUNC_MSG);
+  if ((!room_msg || !sizeof(room_msg)) && !func_msg)
+    return;
+
+  if (room_msg&&sizeof(room_msg))
+  {
+    msgid = random(sizeof(room_msg));
+    // Defaultwerte sind fuer Altcode schwierig
+    send_room(this_object(), room_msg[msgid],
+              MT_LOOK|MT_LISTEN|MT_FEEL|MT_SMELL|
+              MSG_DONT_STORE|MSG_DONT_BUFFER|MSG_DONT_WRAP);
+  }
+
+  if (func_msg)
+  {
+    if (stringp(func_msg))
+      func=(string)func_msg;
+    else
+      func=func_msg[random(sizeof(func_msg))];
+    if (func && function_exists(func))
+      call_other (this_object(), func, msgid);
+  }
+
+  while (remove_call_out("WriteRoomMessage")!=-1);
+  tim=QueryProp(P_MSG_PROB);
+  if(this_object() && sizeof(filter(
+       deep_inventory(this_object()), #'interactive))) //')))
+    call_out("WriteRoomMessage", (tim<15 ? 15 : tim));
+}
+
+varargs string int_long(mixed viewer,mixed viewpoint,int flags)
+{
+  string descr, inv_descr;
+
+  flags &= 3;
+  if( IS_LEARNER(viewer) && viewer->QueryProp( P_WANTS_TO_LEARN ) )
+    descr = "[" + object_name(ME) + "]\n";
+  else
+    descr = "";
+
+  descr += process_string(QueryProp(P_INT_LONG)||"");
+  
+  // ggf. Tueren hinzufuegen.
+  if (QueryProp(P_DOOR_INFOS)) {
+    string tmp=((string)call_other(DOOR_MASTER,"look_doors"));
+    if (stringp(tmp) && sizeof(tmp))
+        descr += tmp;
+  }
+  
+  // ggf. Ausgaenge hinzufuegen.
+  if ( viewer->QueryProp(P_SHOW_EXITS) && (!QueryProp(P_HIDE_EXITS) 
+	|| pointerp(QueryProp(P_HIDE_EXITS))) )
+    descr += GetExits(viewer) || "";
+
+  // Viewpoint (Objekt oder Objektarray) sind nicht sichtbar
+  inv_descr = (string) make_invlist(viewer, all_inventory(ME) 
+		  - (pointerp(viewpoint)?viewpoint:({viewpoint})) ,flags);
+
+  if ( inv_descr != "" )
+    descr += inv_descr;
+
+  if(environment() && (inv_descr=QueryProp(P_TRANSPARENT)))
+  {
+    if(stringp(inv_descr)) descr += inv_descr;
+    else                   descr += "Ausserhalb siehst Du:\n";
+            
+    descr += environment()->int_short(viewer,ME);
+  }
+                  
+  return descr;
+}
+
+string int_short(mixed viewer,mixed viewpoint)
+{
+  string descr, inv_descr;
+
+  descr = process_string( QueryProp(P_INT_SHORT)||"");
+  if( IS_LEARNER(viewer) && viewer->QueryProp( P_WANTS_TO_LEARN ) )
+    descr += " [" + object_name(ME) + "].\n";
+  else
+    descr += ".\n";
+
+  if ( ( viewer->QueryProp(P_SHOW_EXITS)
+         || ( environment(viewer) == ME && !viewer->QueryProp(P_BRIEF) ) )
+       && (!QueryProp(P_HIDE_EXITS) || pointerp(QueryProp(P_HIDE_EXITS))) )
+    descr += GetExits(viewer) || "";
+  
+  // Viewpoint (Objekt oder Objektarray) sind nicht sichtbar
+  inv_descr = (string) make_invlist( viewer, all_inventory(ME) 
+		  - (pointerp(viewpoint)?viewpoint:({viewpoint})) );
+
+  if ( inv_descr != "" )
+    descr += inv_descr;
+
+  return descr;
+}
+
+/** Roommessages abschalten, wenn keine Interactives mehr da sind.
+  */
+// TODO: Irgendwann das varargs loswerden, wenn in der restlichen Mudlib
+// TODO::exit() 'richtig' ueberschrieben wird.
+varargs void exit(object liv, object dest) {
+  // fall erbende Objekte das liv nicht uebergeben. Pruefung nur auf
+  // previous_object(). Wenn Magier da noch irgendwelche Spielchen mit
+  // call_other() & Co treiben, haben wir Pech gehabt, macht aber nicht viel,
+  // weil die Raummeldungen dann im naechsten callout abgeschaltet werden.
+  if (!living(liv=previous_object())) return;
+
+  object *interactives = filter(all_inventory(), #'interactive);
+  // liv wurde noch nicht bewegt, ggf. beruecksichtigen.
+  if ( !sizeof(interactives) ||
+      (interactive(liv) && sizeof(interactives) < 2) )
+    while (remove_call_out("WriteRoomMessage")!=-1);
+}
+
+static string _query_int_long() {return Query(P_INT_LONG, F_VALUE);}
+
+
+// Querymethode fuer P_DOMAIN - gibt die Region an, in der Raum liegt, sofern
+// er unter /d/ liegt...
+static string _query_lib_p_domain()
+{
+  string fn = object_name();
+  if (strstr(fn, "/d/") == 0)
+  {
+    return capitalize(explode(fn, "/")[2]);
+  }
+
+  return "unbekannt";
+}
+
+<string|string*>* _set_harbour_name( <string|string*>* desc) 
+{
+  if ( sizeof(desc)!=2 )
+  {
+    raise_error(sprintf("Unacceptable data in P_HARBOUR, sizeof() was %d, "
+      "expected 2.", sizeof(desc)));
+  }
+  else if ( !stringp(desc[0]) )
+  {
+    raise_error("Wrong data type in P_HARBOUR[0]: expected 'string'.");
+  }
+  else if ( pointerp(desc[1]) && sizeof(desc[1])<1 )
+  {
+    raise_error("Insufficient data in P_HARBOUR[1]: expected 'string*', "
+      "got '({})'.");
+  }
+  else if ( stringp(desc[1]) )
+  {
+    desc[1] = ({desc[1]});
+  }
+  return Set(P_HARBOUR, desc, F_VALUE);
+}
+
diff --git a/std/room/doors.c b/std/room/doors.c
new file mode 100644
index 0000000..e05f009
--- /dev/null
+++ b/std/room/doors.c
@@ -0,0 +1,162 @@
+// MorgenGrauen MUDlib
+//
+// room/doors.c -- new doors, managed by doormaster
+//
+// $Id: doors.c 9134 2015-02-02 19:26:03Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+#include <config.h>
+#include <properties.h>
+#include <defines.h>
+#include <language.h>
+#include <doorroom.h>
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <room/exits.h>
+
+protected void create()
+{
+  if (object_name(this_object()) == __FILE__[0..<3])
+  {
+    set_next_reset(-1);
+    return;
+  }
+  SetProp(P_DOOR_INFOS,0);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+varargs int NewDoor(string|string* cmds, string dest, string|string* ids,
+                    mapping|<int|string|string*>* props)
+{
+/*
+  cmds: Befehl(e), um durch die Tuer zu gehen (String oder Array von Strings)
+  dest: Zielraum
+  ids:  Id(s) der Tuer, default "tuer" (String, Array von Strings oder 0)
+  props: besondere Eigenschaften der Tuer (optional)
+  Array mit Paaren Nummer der Eigenschaft, Inhalt
+  definierte Properties sind:
+  D_FLAGS:  Flags wie bei Sir's Tueren
+  default: DOOR_CLOSED | DOOR_RESET_CL
+  Bei Schluesseln wird getestet, ob der String, den
+  QueryDoorKey zurueckliefert, gleich
+  "raumname1:raumname2" ist, wobei raumname1,2 die
+  kompletten Filenamen der beiden Raeume in sortierter
+  Reihenfolge sind.
+  D_LONG:   lange Beschreibung der Tuer
+  default: "Eine Tuer.\n"
+  D_SHORT:  kurze Beschreibung der Tuer, wird an die Raumbeschreibung
+  angefuegt, wobei %s durch geoeffnet bzw. geschlossen
+  ersetzt wird.
+  default: "Eine %se Tuer. "
+  D_NAME:   Name, der beim Oeffnen/Schliessen und bei Fehlermeldungen
+  angezeigt wird.
+  default: "Tuer"
+  D_GENDER: default: FEMALE
+  D_FUNC:   Funktion, die im Raum aufgerufen werden soll, wenn die
+  Tuer erfolgreich durchschritten wird.
+  default: 0
+  D_MSGS:   Falls String: ausgegebene Richtung fuer move
+  Falls Array: ({direction, textout, textin}) fuer move
+  default: 0
+
+  Beispiel:
+  NewDoor("norden","/players/rochus/room/test2","portal",
+  ({D_NAME,"Portal",
+  D_GENDER,NEUTER,
+  D_SHORT,"Im Norden siehst Du ein %ses Portal. ",
+  D_LONG,"Das Portal ist einfach nur gigantisch.\n"
+  }));
+
+*/
+
+  if (!cmds || !dest) return 0;
+  return call_other(DOOR_MASTER,"NewDoor",cmds,dest,ids,props);
+}
+
+void init()
+{
+  mixed *info;
+  string *cmds;
+  int i,j;
+
+  if (!pointerp(info=(mixed *)QueryProp(P_DOOR_INFOS))) return;
+  add_action("oeffnen","oeffne");
+  add_action("schliessen","schliesse");
+  add_action("schliessen","schliess");
+  for (i=sizeof(info)-1;i>=0;i--) {
+    cmds=(string *)(info[i][D_CMDS]);
+    for (j=sizeof(cmds)-1;j>=0;j--)
+      add_action("go_door",cmds[j]);
+    // Befehle IMMER anfuegen, gechecked wird sowieso erst beim Durchgehen.
+  }
+}
+
+void reset()
+{
+  if (QueryProp(P_DOOR_INFOS))
+    call_other(DOOR_MASTER,"reset_doors");
+}
+
+int oeffnen (string str)
+{
+  if (!str || !QueryProp(P_DOOR_INFOS))
+    return 0;
+  return (int) call_other(DOOR_MASTER,"oeffnen",str);
+}
+
+int schliessen (string str)
+{
+  if (!str || !QueryProp(P_DOOR_INFOS))
+    return 0;
+  return (int) call_other(DOOR_MASTER,"schliessen",str);
+}
+
+varargs int
+go_door (string str)
+{
+  if (!QueryProp(P_DOOR_INFOS))
+    return 0;
+  if (call_other(DOOR_MASTER,"go_door",query_verb()))
+    return 1;
+  return 0;
+}
+
+int set_doors(string *cmds,int open)
+{
+  int j;
+  
+  if (!previous_object())
+    return 0;
+  if (object_name(previous_object())!=DOOR_MASTER)
+    return 0;
+  // Andere sollen nicht rumpfuschen.
+  if (!this_player()) return 0;
+  if (environment(this_player())!=this_object())
+    return 0;
+  // Ist sowieso keiner da...
+  if (!pointerp(cmds))
+    return 0;
+  if (open)
+    AddSpecialExit(cmds,"go_door");
+  else
+    RemoveSpecialExit(cmds);
+  for (j=sizeof(cmds)-1;j>=0;j--)
+    add_action("go_door",cmds[j]);
+  return 1;
+}
+
+/* Fuer Tueren, die flexible Langbeschreibungen haben, wird ein 
+ * SpecialDetail eingefuehrt.
+ */
+
+string special_detail_doors(string key){
+  return DOOR_MASTER->special_detail_doors(key);
+}
diff --git a/std/room/exits.c b/std/room/exits.c
new file mode 100644
index 0000000..dc724b8
--- /dev/null
+++ b/std/room/exits.c
@@ -0,0 +1,334 @@
+// MorgenGrauen MUDlib
+//
+// room/exits.c -- room exits handling 
+//
+// $Id: exits.c 9497 2016-02-21 14:20:03Z Zesstra $
+
+/*
+ * Exits of the room (obvious ones, doors, and special ones)
+ * we define the following function for easy reference:
+ * GetExits() - return a string containing an "Obvious Exits" Statement
+ *
+ * The exits are implemented as properties P_EXITS
+ * They are stored locally (_set_xx, _query_xx)
+ * as mapping to speed up the routines in this module.
+ *
+ */
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <moving.h>
+#include <room/exits.h>
+#include <hook.h>
+#include <exploration.h>
+#undef NEED_PROTOTYPES
+
+#include <sys_debug.h>
+#include <config.h>
+#include <properties.h>
+#include <defines.h>
+#include <daemon.h>
+#include <doorroom.h>
+#include <routingd.h>
+
+#define NUMBERS ({ "zwei", "drei", "vier", "fuenf", "sechs", "sieben", "acht" })
+
+
+// Hilfsfunktion, die bei kaputten Exits eine Notrettung betreibt, aber
+// trotzdem auf Debug eine Meldung macht.
+static mapping rescueExit()
+{
+  catch(raise_error(sprintf(
+	  "room/exits.c: Forgotten ::create()? "
+	  "P_EXITS in %O is 0!\n", this_object()));publish); 
+
+  return ([:2]);
+}
+
+
+static mapping _set_exits( mapping map_ldfied ) 
+{
+    if( mappingp(map_ldfied) )
+        return Set( P_EXITS, map_ldfied );
+    return 0;
+}
+
+
+static mapping _query_exits() 
+{
+    if( (!previous_object() || object_name(previous_object()) != DOOR_MASTER)
+        && QueryProp(P_DOOR_INFOS) )
+        call_other( DOOR_MASTER, "init_doors" );
+
+    mapping exits = Query(P_EXITS) || rescueExit();
+
+    return filter(exits, function int (string key, mixed val)
+        {return stringp(val[0]);} );
+}
+
+
+static int _set_special_exits( mapping map_ldfied )
+{
+    return -1;
+}
+
+
+static mapping _query_special_exits() 
+{
+    mapping exits = Query(P_EXITS) || rescueExit();
+
+    return filter(exits, function int (string key, mixed val)
+        {return closurep(val[0]);} );
+}
+
+
+void reset()
+{}
+
+
+protected void create()
+{
+  offerHook(H_HOOK_EXIT_USE, 1);
+  SetProp( P_EXITS, ([:2]) );
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+protected void _AddExit(string|string* cmd, string|closure room,
+                        string message)
+{
+  mapping exita;
+
+  exita = Query(P_EXITS) || rescueExit();
+
+  if ( !closurep(room) )
+  {
+    object router;
+
+    room = _MakePath(room);
+
+    if ( !clonep(this_object()) && objectp(router = find_object(ROUTER)) )
+    {
+      router->RegisterExit( object_name(this_object()), cmd, room );
+    }
+  }
+
+  if( stringp(cmd) )
+  {
+    exita += ([ cmd : room; message ]);
+  }
+  else
+  {
+    foreach(string c : cmd)
+    {
+      if (stringp(c))
+        exita += ([ c : room; message ]);
+    }
+  }
+
+  Set( P_EXITS, exita );
+}
+
+void AddExit(string|string* cmd, closure|string dest)
+{
+  string msg;
+  if ( stringp(dest) )
+  {
+    int s;
+    if( (s = member(dest, '#')) != -1 )
+    {
+      msg  = dest[0..s-1];
+      dest = dest[s+1..];
+    }
+  }
+  _AddExit(cmd, dest, msg);
+}
+
+void RemoveExit(string|string* cmd )
+{
+    mapping exita;
+
+    if ( !cmd ) {
+        SetProp(P_EXITS, ([:2]) );
+        return;
+    }
+
+    if ( stringp(cmd) )
+        cmd = ({ cmd });
+
+    exita = Query(P_EXITS, F_VALUE) || rescueExit();
+    foreach(string c : cmd)
+      m_delete( exita, c );
+
+    Set( P_EXITS, exita, F_VALUE );
+}
+
+
+void AddSpecialExit(string|string* cmd, string|closure functionname )
+{
+
+    if ( stringp(functionname) )
+        functionname = symbol_function( functionname, this_object() );
+
+    if ( !closurep(functionname) )
+    {
+        catch(raise_error(sprintf( "method %O doesn't exist\n",
+                          functionname)); publish);
+        return;
+    }
+
+    AddExit( cmd, functionname );
+}
+
+
+void RemoveSpecialExit(string|string* cmd)
+{
+    RemoveExit( cmd );
+}
+
+
+varargs string GetExits( object viewer ) 
+{
+    string *indices, *hidden;
+    string exits;
+
+    if ( QueryProp(P_DOOR_INFOS) )
+        call_other( DOOR_MASTER, "init_doors" );
+
+    indices = m_indices( Query(P_EXITS) || rescueExit() );
+    
+    if ( pointerp(hidden = QueryProp(P_HIDE_EXITS)) )
+        indices -= hidden;
+
+    int n=sizeof(indices);
+    switch (n) {
+    case 0:
+        return "Es gibt keine sichtbaren Ausgaenge.\n";
+        
+    case 1:
+        return "Es gibt einen sichtbaren Ausgang: " + indices[0] + ".\n";
+        
+    case 2: case 3: case 4: case 5: case 6: case 7: case 8:
+        exits = "Es gibt " + NUMBERS[n-2] + " sichtbare Ausgaenge: ";
+        break;
+        
+    default:
+        exits = "Es gibt viele sichtbare Ausgaenge: ";
+    }
+    exits += CountUp(indices);
+    return break_string( exits+".", 78 );
+}
+
+
+// Richtungsbefehle nur interpretieren, wenn der Spieler *im* Raum steht und
+// nicht davor (Transporter etc.)/o
+void init()
+{
+    if ( environment(this_player()) == this_object() )
+        add_action( "_normalfunction", "", 1 );
+}
+
+
+/* not only normal exits are handled here */
+
+int _normalfunction()
+{
+  int ret;
+  mapping exits = Query(P_EXITS, F_VALUE) || ([:3]);
+  if (!member(exits,query_verb()))
+    return 0;
+
+  string verb = query_verb();
+  string destroom = exits[query_verb(),0];
+  string message = exits[query_verb(),1];
+
+  mixed hres = HookFlow(H_HOOK_EXIT_USE, ({verb, destroom, message}));
+  if(hres && pointerp(hres) && sizeof(hres)>H_RETDATA)
+  {
+    if(hres[H_RETCODE]==H_CANCELLED)
+    {
+      return 1;
+    }
+    else if(hres[H_RETCODE]==H_ALTERED
+            && pointerp(hres[H_RETDATA])
+            && sizeof(hres[H_RETDATA]) >= 3)
+    {
+      <string|closure>* hdata = hres[H_RETDATA];
+      if (!stringp(hdata[0])
+          || (!stringp(hdata[1]) && !closurep(hdata[1]))
+          || (hdata[2] && !stringp(hdata[2])) )
+        raise_error(sprintf("Invalide Daten aus H_HOOK_EXIT_USE: %.150O\n",
+                            hdata));
+      verb = hdata[0];
+      destroom = hdata[1];
+      message = hdata[2];
+    }
+  }
+
+  if( closurep(destroom) )
+  {
+    ret = funcall( destroom, verb );
+
+    if(ret==MOVE_OK)
+    {
+      GiveEP( EP_EXIT, verb );
+    }
+
+    return ret;
+  }
+
+  if (!stringp(message))
+  {
+    if( member( ({ "sueden", "suedwesten", "westen","nordwesten", "norden",
+        "nordosten", "osten","suedosten" }), verb ) != -1 )
+    {
+      message = "nach " + capitalize(verb);
+    }
+    else if ( member( ({ "oben", "unten" }), verb ) != -1 )
+    {
+      message = "nach " + verb;
+    }
+    else
+    {
+      message = verb;
+    }
+  }
+
+  ret = this_player()->move( destroom, M_GO, message );
+
+  if (ret==MOVE_OK)
+  {
+    GiveEP( EP_EXIT, verb );
+  }
+
+  return ret;
+}
+
+static string _MakePath( string str )
+{
+  string *comp;
+
+  comp = explode( object_name(this_object()), "/" ) - ({""});
+  
+   switch( str[0] ){
+   case '.':
+       str = "/" + implode( comp[0..<2], "/" ) + "/" + str;
+       break;
+       
+   case '~':
+       str = "/" + comp[0] + "/" + (comp[0] == "d" ? comp[1] + "/" : "")
+           +REAL_UID(this_object()) + str[1..];
+       break;
+   }
+   
+   return MASTER->_get_path( str, getuid(this_object()) );
+}
+
diff --git a/std/room/items.c b/std/room/items.c
new file mode 100644
index 0000000..1d4005a
--- /dev/null
+++ b/std/room/items.c
@@ -0,0 +1,42 @@
+// MorgenGrauen MUDlib
+//
+// room/items.c -- creating extra items in room
+//
+// $Id: items.c 9538 2016-03-20 23:46:41Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "std/container/items";
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void reset() 
+{
+  ::reset();
+
+  object *inh = all_inventory(this_object());
+  if ( !pointerp(inh) || sizeof(inh) < 10 )
+    return;
+  // nur wenn keine Spieler anwesend sind.
+  if ( !sizeof(inh & users()) )
+    remove_multiple(3);
+}
+
+// Per Default nur an alle Items im Inventar weiterleiten.
+public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
+                              string msg_prefix, object origin)
+{
+  int *res = all_inventory()->ReceiveMsg(msg, msg_type, msg_action,
+                                         msg_prefix,
+                                         origin || previous_object());
+  if (sizeof(res))
+    return min(res);
+  return 0;
+}
+
diff --git a/std/room/kraeuter.c b/std/room/kraeuter.c
new file mode 100644
index 0000000..24f5193
--- /dev/null
+++ b/std/room/kraeuter.c
@@ -0,0 +1,240 @@
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <thing/language.h>
+#include <thing/description.h>
+#include <thing/commands.h>
+#undef NEED_PROTOTYPES
+#include <moving.h>
+#include <items/kraeuter/kraeuter.h>
+#include <defines.h>
+#include <living/combat.h> // Fuer P_FREE_HANDS
+
+// Standardwert von 2 h fuer vollstaendige Regeneration des Krautes. 
+// Einfaches Nachwachsen ist typischerweise die Haelfte davon.
+#define STD_WACHSTUM  7200
+#define BS(x)         break_string(x, 78)
+
+// Struktur (6 Eintraege pro Kraut):
+// ([ filename : ({ Zeit_bis_nachgewachsen, Zeit_bis_komplett_regeneriert,
+//                  Kraut-IDs, Kraut-Adjektive, Kraut->Name(WER,1),
+//                  ID_des_Bewacher-NPCs }) ])
+mapping plantMap = ([]);
+
+// kann benutzt werden um schnell und einfach eine Pflanze in einem Raum
+// hinzuzufuegen. Beispiel: AddPlant(BAERENKLAU);
+// Diese Funktion erzeugt automatisch ein AddCmd() fuer das Pfluecken und
+// (falls noch nicht vorhanden) Details fuer die Pflanze.
+// Rueckgabewerte:
+// 1 -> Erfolg; -1 -> filename ungueltig
+varargs int AddPlant(string filename, string|string* npcId)
+{
+  mixed arr;
+
+  // Dateiname nicht uebergeben? Dann tun wir erstmal gar nix.
+  if (!stringp(filename)) 
+    return -1;
+  object ob=load_object(filename);
+   
+  // Wenn wir zu dem Kraut schon Daten haben (erkennbar an >2 Eintraegen),
+  // werfen wir einen Fehler, damit das beim Laden des Raumes schon
+  // erkannt wird.
+  if (pointerp(arr=plantMap[filename]) && sizeof(arr)>2)
+    raise_error("AddPlant(): "+filename+" already exists.\n");
+
+  // IDs und Adjektive parsen und den Datensatz zusammenstellen
+  string *ids = ob->Query(P_IDS, F_VALUE)-({ "Ding" });
+  string *adj = ob->Query(P_ADJECTIVES, F_VALUE);
+  
+  if (!pointerp(arr) || sizeof(arr)<2) 
+    arr = ({0,0});
+  if ( !npcId )
+    npcId = ({});
+  else if (stringp(npcId))
+    npcId = ({npcId});
+  plantMap[filename]=({arr[0], arr[1], ids, adj, ob->Name(WER, 1), npcId });
+   
+  // Details erzeugen aus Adjektiven und IDs
+  adj = ob->QueryProp(P_NAME_ADJ);
+  
+  // aktuelles Geschlecht zwischenspeichern, wird spaeter wiederhergestellt
+  int gender = Query(P_GENDER, F_VALUE);
+  Set(P_GENDER, ob->Query(P_GENDER, F_VALUE), F_VALUE);
+  
+  // erzeugt fuer jede moegliche Kombination aus Adjektiv im Akkusativ
+  // und ID des Krautes ein Detail.
+  adj = map(adj, #'DeclAdj, WEN, 0);
+
+  string *det=({});
+  foreach(string _id : ids) {
+    foreach(string _adj : adj) {
+      det += ({ _adj + _id });
+    }
+  }
+
+  det += ids;
+  // keine existierenden Details ueberschreiben
+  det -= m_indices(Query(P_DETAILS, F_VALUE) || ([]));
+  if (sizeof(det)) 
+    AddDetail(det, ob->Query(PLANT_ROOMDETAIL, F_VALUE));
+  
+  // Eine Befehlsfunktion brauchen wir natuerlich auch.
+  AddCmd(({"pflueck", "pfluecke", "ernte"}), "_pfluecken");
+  
+  return 1;
+}
+
+// Wenn jemand per Hand das Plantdetail hinzufuegen moechte...
+// z.B. bei Verwendung von GetPlant() anstelle von AddPlant()
+void AddPlantDetail(string filename)
+{
+  // Pfad in Objektpointer wandeln
+  object ob=load_object(filename);
+   
+  // Details erzeugen
+  string *det = ({});
+  string *ids = ob->Query(P_IDS, F_VALUE)-({ "Ding" });
+  string *adj = ob->QueryProp(P_NAME_ADJ);
+  // aktuelles Geschlecht zwischenspeichern, wird spaeter wiederhergestellt
+  int gender=Query(P_GENDER, F_VALUE);
+  Set(P_GENDER, ob->Query(P_GENDER, F_VALUE));
+  // erzeugt fuer jede moegliche Kombination aus Adjektiv im Akkusativ
+  // und ID des Krautes ein Detail.
+  adj = map(adj, #'DeclAdj, WEN, 0);
+  foreach(string _id : ids) {
+    foreach(string _adj : adj) {
+      det += ({ _adj + _id });
+    }
+  }
+  AddDetail(det+ids, ob->Query(PLANT_ROOMDETAIL, F_VALUE));
+  // Geschlecht zuruecksetzen
+  Set(P_GENDER, gender, F_VALUE);
+}
+
+// Prueft, ob die Pflanze zu "filename" in diesem Raum schon nachgewachsen
+// ist.
+protected int CheckPlant(string filename)
+{
+  mixed arr=plantMap[filename];
+  if (!pointerp(arr) || sizeof(arr)<2) {
+    arr=plantMap[filename]=({ 0, 0 });
+  }
+  // Solange die Zeit arr[0] noch nicht erreicht ist, ist das Kraut nicht 
+  // nachgewachsen, dann gibt es gar nix.
+  return (time()>arr[0]);
+}
+
+// Moechte man AddPlant() nicht benutzen, weil man die Pflanze nicht einfach
+// pfluecken, sondern vielleicht abschneiden, oder ausgraben soll, so kann
+// man sich mittels GetPlant(filename) das Objekt erzeugen lassen. Gibt
+// GetPlant() 0 zurueck, ist die Pflanze noch nicht wieder weit genug
+// nachgewachsen.
+object GetPlant(string filename)
+{
+  int *arr=plantMap[filename];
+  if (!pointerp(arr) || sizeof(arr)<2)
+  {
+     arr=plantMap[filename]=({ 0, 0 });
+  }
+  // arr[0] enthaelt den Zeitpunkt, wann das Kraut nachgewachsen ist,
+  int nachgewachsen = arr[0];
+  // arr[1] denjenigen, wann es vollstaendig regeneriert ist.
+  int regeneriert = arr[1];
+
+  // Vor dem Nachgewachsen-Zeitpunkt kann man gar nix ernten.
+  if (time()<nachgewachsen) return 0; // noch nicht nachgewachsen
+
+  // Restzeit bis zur vollstaendigen Regeneration.
+  regeneriert-=time();
+  
+  // Wenn vollstaendig regeneriert, ist STD_WACHSTUM die neue Zeit bis zur
+  // Regeneration. Wenn noch nicht vollstaendig regenriert, Restzeit
+  // verdoppeln und STD_WACHSTUM nochmal drauf addieren.
+  regeneriert = (regeneriert<=0 ? STD_WACHSTUM 
+                                : (regeneriert*2)+STD_WACHSTUM);
+  // nachgewachsen ist die halbe Regenerationszeit
+  arr[0]=nachgewachsen=time()+(regeneriert/2);
+  // Zeit voelliger Regeneration
+  arr[1]=regeneriert+=time();
+  
+  return clone_object(filename);
+}
+
+static int _pfluecken(string str)
+{
+  int res;
+
+  if (!mappingp(plantMap)) return 0;
+  _notify_fail("WAS moechtest Du pfluecken?\n");
+
+  // IDs und Adjektive zwischenspeichern
+  mixed ids = Query(P_IDS, F_VALUE);
+  mixed adj = Query(P_ADJECTIVES, F_VALUE);
+
+  foreach(string key, mixed krautinfo : plantMap) 
+  {
+    if ( sizeof(krautinfo) != 6 )
+      continue;
+ 
+    // IDs und Adjektive des Krautes kopieren 
+    Set(P_IDS, krautinfo[2], F_VALUE);
+    Set(P_ADJECTIVES, krautinfo[3], F_VALUE);
+
+    // Syntaxpruefung wird dann mit id() gemacht.
+    if (id(str)) 
+    {
+      object ob;
+      object bewacher;
+      res=1;
+
+      // Liste der eingetragenen Bewacher-IDs durchlaufen und pruefen, ob
+      // mindestens einer davon anwesend ist.
+      foreach( string npcId : krautinfo[5] )
+      {
+        bewacher = present(npcId, ME);
+        if (objectp(bewacher))
+          break;
+      }
+      
+      if ( !PL->QueryProp(P_FREE_HANDS) ) 
+      {
+        tell_object(PL, BS("Du hast keine Hand frei, um etwas pfluecken "
+          "zu koennen."));
+      }
+      // Ist der Bewacher anwesend? Dann kann man das Kraut nicht pfluecken.
+      else if ( objectp(bewacher) )
+      {
+        tell_object(PL, BS(bewacher->Name(WER, 2)+" laesst Dich "
+          "leider nicht nah genug heran. Irgendwie musst Du Dich wohl "
+          "zunaechst um "+bewacher->QueryPronoun(WEN)+" kuemmern."));
+      }
+      // Wenn GetPlant() ein Objekt liefert, kann was gepflueckt werden.
+      else if ( objectp(ob=GetPlant(key)) ) 
+      {
+        if ( ob->move(PL, M_GET) == MOVE_OK )
+        {
+          write(BS("Vorsichtig pflueckst Du "+ob->name(WEN, 1)+
+            " und nimmst "+ob->QueryPronoun(WEN)+" an Dich."));
+        }
+        else 
+        {
+          write(BS("Vorsichtig pflueckst Du "+ob->name(WEN, 1)+", kannst "+
+            ob->QueryPronoun(WEN)+" aber nicht nehmen."));
+          ob->move(environment(PL), M_GET);
+        }
+      }
+      // Wenn alles nicht, dann ist das Kraut noch nicht wieder da.
+      else 
+      {
+        write(BS(krautinfo[4]+" ist noch nicht reif genug "
+          +"und muss erst noch etwas wachsen."));
+        break;
+      }
+    }
+  }
+   // IDs und Adjektive zuruecksetzen.
+  Set(P_IDS, ids, F_VALUE);
+  Set(P_ADJECTIVES, adj, F_VALUE);
+
+  return res;
+}
+
diff --git a/std/room/kraeuterladen.c b/std/room/kraeuterladen.c
new file mode 100644
index 0000000..cf99793
--- /dev/null
+++ b/std/room/kraeuterladen.c
@@ -0,0 +1,167 @@
+// (c) 2003 by Padreic (padreic@mg.mud.de)
+// 
+// Es kann bestimmte Laeden zum Handeln von Kraeutern geben.
+// Zunaechst einmal gibt es einen in der Dunkelelfengilde.
+// Hier koennen Kraeuter ge- und verkauft werden.
+// Grundsaetzlich kann es beliebig viele kraeuterkundige
+// Haendler geben, eine kurze Absprache waere jedoch von Vorteil.
+
+// Der Laden erweitert die room/shop Funktionen.
+// Zur Verwendung muss dieser noch mit einem std/room kombiniert
+// werden. Dies erlaubt die Verwendung von eigenen Standardraeumen oder
+// Kombination mit Kneipen.
+
+inherit "/std/room/shop";
+
+#define NEED_PROTOTYPES
+
+#include <items/kraeuter/kraeuter.h>
+#include <properties.h>
+
+// Flag, das im reset() das Speichern ausloest, wenn es gesetzt ist.
+static  int need_save;  
+static  int basisvalue=400; // gibt den _Durchschnittswert_ eines Krauts an
+static  int maxvalue=10000; // bestimmt den max. Wert aller Kraeuter
+
+// Enthaelt fuer jede PlantID einen Zaehler, der angibt, wieviel von dem
+// jeweiligen Kraut verkauft wurde.
+// private int *count=({});
+
+// Summe ueber alle Eintraege in count
+// private int sum;
+
+protected void create()
+{
+  if (object_name(this_object()) == __FILE__[0..<3])
+  {
+    set_next_reset(-1);
+    return;
+  }
+  ::create();
+
+  SetProp(P_BUY_ONLY_PLANTS,1);
+  /*
+  seteuid(getuid()); // wird zum speichern benötigt
+  int si=sizeof(count);
+  // fuer jede PlantID einen Eintrag anlegen, d.h. wenn in count noch
+  // nicht genug vorhanden sind, werden fehlende Eintraege ergaenzt.
+  if (si<PLANTCOUNT) 
+    count=count+allocate(PLANTCOUNT-si);
+  */
+}
+
+protected void create_super()
+{
+  set_next_reset(-1);
+}
+
+static string sell_obj_only_plants(object ob, int short)
+{
+   if (!IS_PLANT(ob))
+      return "Tut mir leid, damit kann ich nichts anfangen.";
+   // es werden nur Kraeuter angekauft, die legal geclont wurden,
+   // d.h. im entsprechenden Master ordentlich eingetragen wurden!
+   if (ob->QueryPlantId()<=0)
+      return ob->Name(WER, 2)+" ist illegal auf die Welt gekommen. Ich darf "
+            +ob->QueryPronoun(WEN)+" leider nicht annehmen.";
+   return ::sell_obj(ob, short);
+}
+
+static string sell_obj(object ob, int short)
+{
+   // es werden nur Kraeuter angekauft, die legal geclont wurden,
+   // d.h. im entsprechenden Master ordentlich eingetragen wurden!
+  if (IS_PLANT(ob) && ob->QueryPlantId()<=0)
+    return "Hm, "+ob->Name(WER, 2)+" stammt aber aus einer sehr dubiosen "
+      "Quelle. Ich kann "+ob->QueryPronoun(WEN)+" leider nicht annehmen.";
+  if (QueryProp(P_BUY_ONLY_PLANTS))
+    return sell_obj_only_plants(ob, short);
+  return ::sell_obj(ob,short);
+}
+
+/*
+void reset()
+{
+  ::reset();
+  // Daten sind nicht sooo wichtig, als das bei jeder Aenderung
+  // gesavet werden muesste. Daher nun im reset speichern.
+  if (need_save) {
+    // basisvalue/PLANTCOUNT: Durchschnittswert eines einzelnen Krauts
+    // sum + PLANTCOUNT: Gesamtzahl verkaufter plus Zahl existierender
+    //                   Kraeuter
+    // Wenn also der Wert all dieser Kraeuter > 10k ist, wird jeder
+    // Einzelzaehler auf 90% gestutzt, damit die Werte nicht ins Uferlose
+    // steigen.
+    if (((sum+PLANTCOUNT)*basisvalue/PLANTCOUNT)>maxvalue) {
+      int i, newsum;
+      for (i=sizeof(count)-1; i>=0; i--) {
+        count[i] = count[i]*9/10;
+        newsum+=count[i];
+      }
+      sum=newsum;
+    }
+    need_save=0;
+  }
+}
+
+// Aktualisiert den Datenbestand beim Kaufen oder Verkaufen eines Objekts
+void UpdateCounter(object ob, int num)
+{
+   int id=ob->QueryPlantId();
+   if (id>0 && is_plant(ob)) {
+      // Kauf oder Verkauf von Kraeutern, veraendert den Wert der
+      // Kraeuter
+      // Zaehler in der Liste hochzaehlen
+      count[id]+=num;
+      if (count[id]<0) count[id]=0; // darf aber ansich nich passieren
+      // Gesamtsumme aktualisieren
+      sum+=num;
+      need_save=1;
+   }
+   ::UpdateCounter(ob, num);
+}
+
+// Die Preise die hier im Labor fuer Kraeuter gezahlt und verlangt
+// werden, sind nicht fix sondern haengen von Angebot und Nachfrage ab.
+// Hier weiss man ueber den wahren Wert der einzelnen Kraeuter bescheid.
+static int realValue(object ob, object player)
+{
+   // Preise fuer normale Gueter ganz normal...
+   if (!is_plant(ob))
+       return ob->QueryProp(P_VALUE);
+
+   // jede Kraeuterkategorie bekommt den gleichen Wert zugewiesen.   
+   // val entspricht dem aktuellen "Durchschnittswert" eines Krautes
+   int val=(sum+PLANTCOUNT)*basisvalue/PLANTCOUNT;
+
+   // aber dieser Wert verteilt sich auf unterschiedlich viele Kraeuter
+   // (AN: Dieser Kommentar erschliesst sich mir nicht.)
+   int id=ob->QueryPlantId();
+   if (id<=0) return 0; // illegal geclont -> wertlos
+   // ggf. die Zaehlerliste um die fehlenden Eintraege erweitern.
+   if ((id-1)>sizeof(count)) 
+     count=count+allocate(id-1-sizeof(count));
+   
+   // "mittleren Wert" des abgefragten Krautes errechnen (Division durch
+   // dessen umgesetzte Anzahl)
+   val=val/(count[id]+1);
+   
+   // Wert runden auf naechstniedrigeren glatt durch 10 teilbaren Wert.
+   return val-val%10; 
+}
+
+// gibt den Preis zurück, der zum Ankauf des Objektes verwendet werden soll
+static varargs int QueryValue(object ob, int value, object player)
+{
+   return ::QueryValue(ob, realValue(ob, player), player);
+}
+
+// gibt den Preis an, zu dem das Objekt verkauft werden soll.
+static varargs int QueryBuyValue(mixed ob, object player)
+{
+   if (objectp(ob))
+      return (realValue(ob, player)*QueryBuyFact(player) + 50)/100;
+   // fixed Objekte...
+   return ::QueryBuyValue(ob, player);
+}
+*/
diff --git a/std/room/light.c b/std/room/light.c
new file mode 100644
index 0000000..a372047
--- /dev/null
+++ b/std/room/light.c
@@ -0,0 +1,61 @@
+// MorgenGrauen MUDlib
+//
+// container/light.c -- Lichtsystemkomponenten fuer Raeume
+//
+// $Id: description.c 6986 2008-08-22 21:32:15Z Zesstra $
+
+inherit "/std/container/light";
+
+#pragma strict_types
+#pragma save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+#include <thing/description.h>
+#include <room/description.h>
+#include <container.h>
+
+protected void create()
+{
+  ::create();
+  SetProp(P_LIGHT_ABSORPTION, 1);
+}
+
+protected void create_super() {set_next_reset(-1);}
+
+/*
+   // das wird selten benutzt und ist zur zeit funktionsunfaehig, da die
+   // interne Speicherung der Prop sich geaendert hat.
+static int _set_int_light(int *light)
+{
+   int tmp;
+
+   // zur Sicherheit
+   if (!pointerp(light)) return -1;
+   if (light[0]>QueryProp(P_LIGHT)) {
+      // Licht verlaeuft sich in einem grossen Raum, daher Modifier abfragen...
+      tmp=light[0]-QueryProp(P_LIGHT_ABSORPTION);
+      // wenn sich das Vorzeichen geaendert hat, auf 0 setzen.
+      light[0]=((tmp^light[0]) & 0x80000000 ? 0 : tmp);
+   }
+   if (light[1]<QueryProp(P_LIGHT) && light[1]<0) {
+      // Licht verlaeuft sich in einem grossen Raum, daher Modifier abfragen...
+      tmp=light[1]+QueryProp(P_LIGHT_ABSORPTION);
+      // wenn sich das Vorzeichen geaendert hat, auf 0 setzen.
+      light[1]=((tmp^light[1]) & 0x80000000 ? 0 : tmp);
+   }
+   light[2]=light[0]+light[1];
+   Set(P_INT_LIGHT, light, F_VALUE);
+   // diese Prop setzen kaum Leute (offiziell gehts ja auch gar nicht. Keiner
+   // davon erwartet nen Rueckgabewert. Daher wird hier 0 zurueckgeben, statt
+   // des aufwaendig berechneten QueryProp(P_INT_LIGHT).
+   // Achja. Der Rueckgabewert vom Set() waere ein int*, was nicht geht, weil
+   // diese Funktion nur int zurueckgeben darf.
+   return 0;
+}
+*/
diff --git a/std/room/moving.c b/std/room/moving.c
new file mode 100644
index 0000000..0f7c2b1
--- /dev/null
+++ b/std/room/moving.c
@@ -0,0 +1,17 @@
+// MorgenGrauen MUDlib
+//
+// room/moving.c -- Entfernen von Raeumen
+//
+// $Id: moving.c 8041 2012-03-19 18:38:21Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+public varargs int remove(int silent)
+{
+  destruct( this_object() );
+  return 1;
+}
diff --git a/std/room/para.c b/std/room/para.c
new file mode 100644
index 0000000..4666782
--- /dev/null
+++ b/std/room/para.c
@@ -0,0 +1,18 @@
+// This may look like C code, but it is really -*- C++ -*-
+
+// MorgenGrauen MUDlib
+//
+// room/para.c -- Betreten einer Parallelwelt
+//
+// $Id: para.c 7510 2010-03-25 23:37:19Z Zesstra $
+
+// Veraltet - wird nicht mehr benoetigt.
+// Die Zuordnung der 'richtigen' Welt wird jetzt direkt von move() uebernommen.
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+deprecated int paramove() { return 0; }
diff --git a/std/room/pub.c b/std/room/pub.c
new file mode 100644
index 0000000..c15e33e
--- /dev/null
+++ b/std/room/pub.c
@@ -0,0 +1,989 @@
+//
+// pub.c -- Alles, was eine Kneipe braucht.
+//
+// $Id: pub.c 8778 2014-04-30 23:04:06Z Zesstra $
+// spendiere ueberarbeitet, 22.05.2007 - Miril
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+#define NEED_PROTOTYPES
+#include <thing/commands.h>
+#include <thing/properties.h>
+
+#include <defines.h>
+#include <rooms.h>
+#include <properties.h>
+#include <routingd.h>
+#include <bank.h>
+#include <exploration.h>
+#include <wizlevels.h>
+#include <pub.h>
+
+// Alle nicht-privaten werden in erbenen Objekten verwendet.
+private nosave int     max_list;
+private nosave int     refresh_count;
+private nosave int     sum;
+private nosave mapping refresh_list;
+nosave mapping id_list;
+nosave mapping menu_list;
+nosave object  *waiting;
+
+#define PM_RATE_PUBMASTER  "rate"
+#define PM_DELAY_PUBMASTER "delay"
+
+protected void create()
+{ object router;
+
+  SetProp( P_ROOM_TYPE, QueryProp(P_ROOM_TYPE) | RT_PUB );
+
+  SetProp( P_PUB_NOT_ON_MENU,
+    "Tut mir leid, das fuehren wir nicht! Wir sind ein anstaendiges "+
+    "Lokal!\n" );
+  SetProp( P_PUB_UNAVAILABLE,
+    "Davon ist leider nichts mehr da.\n" );
+  SetProp(P_PUB_NO_MONEY,
+    "Das kostet %d Goldstuecke, und Du hast nicht so viel!\n" );
+  SetProp(P_PUB_NO_KEEPER,
+    "Es ist niemand anwesend, der Dich bedienen koennte.\n");
+
+  AddCmd( "menue","menue" );
+  AddCmd( ({"kauf","kaufe","bestell","bestelle"}),"bestelle" );
+  AddCmd( ({"spendier","spendiere"}),"spendiere" );
+  AddCmd( "pubinit","pubinit" );
+
+  max_list=0;
+  refresh_count=0;
+  waiting = ({ });
+  id_list=([]);
+  menu_list=([]);
+  refresh_list=([]);
+
+  if ( !clonep(ME) && objectp(router=find_object(ROUTER)) )
+    router->RegisterTarget(TARGET_PUB,object_name(ME));
+
+  call_out("add_std_drinks",1);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+/* Zur Syntax:
+ *
+ * menuetext - Der Text steht im Menue
+ *
+ * ids       - Array der Namen, mit denen bestellt werden kann
+ *
+ * minfo     - Mapping mit Eintraegen fuer:
+ *                 P_HP (HP-Heilung),
+ *                 P_SP (SP-Heilung),
+ *                 P_FOOD (Saettigung),
+ *                 P_DRINK (Fluessigkeit)
+ *                 P_ALCOHOL (Alkoholisierung)
+ *                 P_VALUE (Preis)
+ *             Die Eintraege werden ueber eval_anything ausgewertet
+ *             (siehe da)
+ *
+ * rate      - Heilrate (auch ueber eavl_anything) in Punkte / heart_beat()
+ *
+ * msg       - Meldung beim Essen.
+ *             a) closure (wird mit funcall(msg,zahler,empfaenger)
+ *                ausgewertet)
+ *             b) string (wie closure: call_other(this_object...))
+ *             c) array mit 2 strings: 1) fuer Essenden, 2) fuer andere
+ *                siehe auch mess()
+ *
+ * refresh   - Mapping mit den moeglichen Eintraegen:
+ *                 PR_USER : <Kontingent> ; <Update> (pro Spieler)
+ *                 PR_ALL  : <Kontingent> ; <Update> (fuer alle)
+ *             Es wird zunaechst geprueft, ob noch etwas fuer den
+ *             (zahlenden!) Spieler selbst vorhanden ist wenn nein wird
+ *             geschaut, ob das Kontingent fuer alle schon erschoepft ist.
+ *             Sind beide Kontingente erschoepft, kann der Spieler das
+ *             Bestellte nicht bekommen.
+ *             Die Kontingente wird alle <Update> reset()s "aufgefrischt".
+ *             <Kontingent> wird ueber eval_anything() ausgewertet.
+ *             Alternativ kann man einen Int-Wert angeben. Dieser wird wie
+ *             ([ PR_ALL : <wert> ; 1 ]) behandelt.
+ *
+ * delay     - Zahl der Sekunden, um die verzoegert die Heilung eintritt
+ *             z.B. weil das Essen erst zubereitet werden muss.
+ *             Ebenfalls ueber eval_anything()
+ *
+ * d_msg     - Meldung beim bestellen, falls Delay. Wie msg.
+ */
+varargs string AddToMenu(string menuetext, mixed ids, mapping minfo,
+                         mixed rate, mixed msg, mixed refresh,
+                         mixed delay, mixed d_msg)
+{ string ident;
+  int i;
+
+  if ( !stringp(menuetext) || !ids || !mappingp(minfo) )
+    return 0;
+
+  if ( stringp(ids) )
+    ids = ({ ids });
+  else if ( !pointerp(ids) )
+    return 0;
+
+  ident = sprintf("menuentry%d",max_list);
+  max_list++;
+
+  /* Fuer schnelles Nachschlagen ein eigenes Mapping fuer Ids */
+  for( i=sizeof(ids)-1;i>=0;i-- )
+    id_list += ([ ids[i] : ident ]);
+
+  if ( intp(refresh) && (refresh>0) )
+    refresh = ([ PR_ALL : refresh; 1 ]);
+  else if ( !mappingp(refresh) )
+    refresh = 0;
+
+  menu_list += ([ ident : menuetext; minfo; rate; msg; refresh;
+                   delay; d_msg; ids ]);
+  return ident;
+}
+
+// Diese Methode ist nur noch aus Kompatibilitaetsgruenden vorhanden und darf
+// nicht mehr verwendet werden!!!
+void AddFood(string nameOfFood, mixed ids, int price, int heal,
+             mixed myFunction)
+{
+  if ( !nameOfFood || !ids || !price)
+    return; /* Food not healing is ok ! */
+
+  AddToMenu( nameOfFood,ids,
+             ([ P_VALUE : price, P_FOOD : heal, P_HP : heal, P_SP : heal ]),
+             ((heal>5)?5:heal), myFunction, 0,0,0);
+}
+
+// Diese Methode ist nur noch aus Kompatibilitaetsgruenden vorhanden und darf
+// nicht mehr verwendet werden!!!
+void AddDrink(string nameOfDrink, mixed ids, int price, int heal,
+              int strength, int soak, mixed myFunction)
+{
+  if ( !nameOfDrink || !ids || !price )
+    return;
+
+  heal=heal/2;
+  /* Kleine Korrektur, damit man in alten Pubs ueberhaupt trinken kann */
+  AddToMenu(nameOfDrink,ids,
+             ([ P_VALUE : price, P_DRINK : soak, P_ALCOHOL : strength,
+                P_HP : heal, P_SP : heal ]),
+             ((heal>5)?5:heal), myFunction,0,0,0);
+}
+
+int RemoveFromMenu(mixed ids) {
+  int ret;
+
+  if (stringp(ids))
+    ids = ({ids});
+
+  if (pointerp(ids)) {
+    foreach (string id: ids) {
+      // look if the id has a matching ident
+      string ident = id_list[id];
+      if (stringp(ident)) {
+        // remove this ident-entry from the id-list ...
+        m_delete(id_list, id);
+        // ... and remove all others now too (so it won't bug later, if
+        //     the wizard calling this method forgot an id)
+        foreach (string listid: m_indices(id_list))
+          if (id_list[listid] == ident)
+            m_delete(id_list, listid);
+
+        // now remove the ident from the menu_list
+        if (member(menu_list, ident)) {
+          ret++;
+          m_delete(menu_list, ident);
+
+          // decrease the ident-counter, if this entry was the last one
+          int oldnum;
+          if (sscanf(ident, "menuentry%d", oldnum) == 1 &&
+              oldnum == (max_list-1))
+            max_list--;
+        }
+      }
+    }
+  }
+  // return removed entries
+  return ret;
+}
+
+/* Zum Auswerten der Eintraege fuer Preis, Rate, HP...
+ * a) integer-Wert    -> wird direkt uebernommen
+ * b) mapping         -> Wird als RaceModifiere verstanden. Es wird der
+ *                       Eintrag gewaehlt, der der Rasse des Spielers
+ *                       entspricht, falls vorhanden, ansonsten der Eintrag
+ *                       fuer 0.
+ * c) Array           -> In erstem Element (muss Objekt oder dessen Filename
+ *                       sein) wird die Funktion mit dem Namen im 2.Element
+ *                       aufgerufen.
+ * d) String          -> Die genannte Funktion wird in der Kneipe selbst
+ *                       aufgerufen.
+ * e) Closure         -> Die Closure wird ausgewertet.
+ * Alle Funktionsaufrufe bekommen den essenden Spieler (bei Price und Delay
+ * den bestellenden Spieler) als Argument uebergeben. Die Auswertung erfolgt
+ * in der angegebenen Reihenfolge. Am Ende muss ein Intergerwert herauskommen
+ */
+int eval_anything(mixed what, object pl)
+{ mixed re;
+
+  if (intp(what))
+    return what;
+
+  if (mappingp(what) && pl)
+  {
+    re = what[pl->QueryProp(P_RACE)]||what[0];
+  }
+
+  if (re)
+    what=re;
+
+  if ( pointerp(what) && (sizeof(what)>=2)
+      && ( objectp(what[0]) || stringp(what[0]) )
+      && stringp(what[1]) )
+    what = call_other(what[0],what[1],pl);
+
+  if ( stringp(what) && function_exists(what,ME) )
+    what = call_other(ME,what,pl);
+
+  if ( closurep(what) )
+    what = funcall(what,pl);
+
+  if ( intp(what) )
+    return what;
+
+  return 0;
+}
+
+/* Diese Funktion ueberprueft, ob das Kontingent eines Menueeintrags
+ * fuer einen Spieler erschoepft ist.
+ */
+string CheckAvailability(string ident, object zahler)
+{ string uid;
+
+  if ( !stringp(ident) || !member(menu_list,ident) || !objectp(zahler) )
+    return 0;
+  if ( !mappingp(menu_list[ident,PM_REFRESH]) )
+    return PR_NONE;
+
+  if ( !member(refresh_list,ident) )
+    refresh_list += ([ ident : ([ ]) ]);
+
+  if ( query_once_interactive(zahler) )
+    uid=getuid(zahler);
+  else
+    uid=object_name(zahler);
+
+  if ( member(menu_list[ident,PM_REFRESH],PR_USER) )
+  {
+    if ( !member(refresh_list[ident],uid) )
+    {
+      refresh_list[ident] += ([ uid : 0 ; refresh_count ]);
+    }
+
+    /* Kontingent des Zahlenden pruefen */
+    if ( refresh_list[ident][uid,PRV_AMOUNT] <
+          eval_anything(menu_list[ident,PM_REFRESH][PR_USER,PRV_AMOUNT],
+                        zahler) )
+      return uid;
+  }
+
+  if ( member(menu_list[ident,PM_REFRESH],PR_ALL) )
+  {
+    if ( !member(refresh_list[ident],PR_DEFAULT) )
+    {
+      refresh_list[ident] += ([ PR_DEFAULT : 0 ; refresh_count ]);
+    }
+
+    /* Kontingent der Allgemeinheit pruefen */
+    if ( refresh_list[ident][PR_DEFAULT,PRV_AMOUNT] <
+          eval_anything(menu_list[ident,PM_REFRESH][PR_ALL,PRV_AMOUNT],
+                        zahler) )
+      return PR_DEFAULT;
+  }
+
+  return 0;
+}
+
+/* Diese Funktion reduziert das Kontingent des Lebewesens uid beim
+ * Menueeintrag ident um 1
+ */
+void DecreaseAvailability(string ident, string uid)
+{
+  if ( !stringp(ident) || !stringp(uid) )
+    return;
+  refresh_list[ident][uid,PRV_AMOUNT]++;
+}
+
+/* Diese Funktion sorgt dafuer, dass die Kontingente an limitierten
+ * Sachen in regelmaessigen Abstaenden wiederhergestellt werden.
+ */
+static void UpdateAvailability()
+{ int    i1,i2;
+  string *ind1,*ind2,chk;
+
+  /* Keine Refresh-Eintraege, kein Update */
+  if ( !mappingp(refresh_list)
+      || (i1=sizeof(ind1=m_indices(refresh_list)))<1 )
+    return;
+
+  /* Es muss jeder Menueeintrag, der in der refresh_list steht,
+   * durchgegangen werden.
+   */
+  for ( --i1 ; i1>=0 ; i1-- )
+  {
+    if ( !mappingp(refresh_list[ind1[i1]])
+        || (i2=sizeof(ind2=m_indices(refresh_list[ind1[i1]])))<1 )
+      continue;
+
+    /* Fuer jeden Menueeintrag muss jeder Spielereintrag durchgegangen
+     * werden, der in dem entspr. mapping steht.
+     */
+    for ( --i2 ; i2>=0 ; i2-- ) // Alle Spieler
+    {
+      if ( ind2[i2]==PR_DEFAULT )
+        chk = PR_ALL;
+      else
+        chk = PR_USER;
+
+      if ( ( refresh_list[ind1[i1]][ind2[i2],PRV_REFRESH]
+            + menu_list[ind1[i1],PM_REFRESH][chk,PRV_REFRESH] )
+          <= refresh_count )
+      {
+        refresh_list[ind1[i1]][ind2[i2],PRV_AMOUNT]=0;
+        refresh_list[ind1[i1]][ind2[i2],PRV_REFRESH]=refresh_count;
+      }
+    }
+  }
+}
+
+mixed DBG(mixed o) {
+  if(find_player("rumata"))
+    tell_object(
+        find_player("rumata"),
+        sprintf("DBG: %O\n", o)
+    );
+  return 0;
+}
+
+/* Erweitert um die Moeglichkeit, Speise- oder Getraenke-Karte zu sehen. */
+string menue_text(string str)
+{ int i,sdr,sfo;
+  string ident,res;
+  string *fo=({}),*dr=({});
+
+  if ( !max_list )
+    return "Hier scheint es derzeit nichts zu geben.\n";
+
+  if ( !stringp(str) || str=="" )
+    str="alles";
+
+  /* Fuers Menue entscheiden ob Drink oder Food */
+  foreach(string id, string menuetext, mapping minfo: menu_list)
+  {
+    if (eval_anything(minfo[P_FOOD],this_player()))
+      fo+=({ id });
+    else
+      dr+=({ id });
+  }
+
+  sdr = sizeof(dr);
+  sfo = sizeof(fo);
+
+  if ( member(({"alle","alles"}),str)!=-1)
+  {
+    if ( !sfo )
+      str="drinks";
+    else if ( !sdr )
+      str="speise";
+    else
+    {
+      /* Gemischte Karte */
+      res = "Getraenke                    Preis alc | "+
+            "Speisen                          Preis\n"+
+            "---------------------------------------+-"+
+            "--------------------------------------\n";
+
+      for ( i=0 ; ( i<sdr || i<sfo ) ; i++ )
+      {
+        if ( i<sdr )
+          res += sprintf("%-29.29s%5.5d  %c  | ",
+                   menu_list[dr[i],PM_TEXT],
+                   eval_anything(menu_list[dr[i],PM_INFO][P_VALUE],PL),
+                   (eval_anything(menu_list[dr[i],PM_INFO][P_ALCOHOL],PL)>0) ? 'J' : 'N');
+        else
+          res += "                                       | ";
+
+        if ( i<sfo )
+          res += sprintf("%-33.33s%5.5d",
+                   menu_list[fo[i],PM_TEXT],
+                   eval_anything(menu_list[fo[i],PM_INFO][P_VALUE],PL));
+
+        res += "\n";
+      }
+
+      return res;
+    }
+  }
+
+  /* Reine Getraenkekarte */
+  if ( member(({"getraenke","drinks","getraenk","trinken"}),str)!=-1 )
+  {
+    if ( !sdr )
+      return "Hier gibt es leider nichts zu trinken.\n";
+
+    res = "Getraenke                    Preis alc | "+
+          "Getraenke                    Preis alc\n"+
+          "---------------------------------------+-"+
+          "--------------------------------------\n";
+
+    for ( i=0 ; i<sdr ; i++ )
+    {
+      ident = dr[i];
+
+      if ( !eval_anything(menu_list[ident,PM_INFO][P_FOOD], PL) )
+        res += sprintf("%-29.29s%5.5d  %c%s",
+                 menu_list[ident,PM_TEXT],
+                 eval_anything(menu_list[ident,PM_INFO][P_VALUE],PL),
+                 (eval_anything(menu_list[ident,PM_INFO][P_ALCOHOL],PL)>0) ? 'J' : 'N',
+                 ((i%2)?"\n":"  | "));
+    }
+
+    if ( res[<1..<1]!="\n" )
+      res += "\n";
+
+    return res;
+  }
+
+  /* Reine Speisekarte */
+  if ( member(({"speise","speisen","essen"}),str)!=-1 )
+  {
+    if ( !sfo )
+      return "Hier gibt es leider nichts zu essen.\n";
+
+    res = "Speisen                          Preis | "+
+          "Speisen                          Preis\n"+
+          "---------------------------------------+-"+
+          "--------------------------------------\n";
+
+    for ( i=0 ; i<sfo ; i++ )
+    {
+      ident = fo[i];
+      if (eval_anything(menu_list[ident,PM_INFO][P_FOOD],PL) )
+        res += sprintf("%-33.33s%5.5d%s",
+                 menu_list[ident,PM_TEXT],
+                 eval_anything(menu_list[ident,PM_INFO][P_VALUE],PL),
+                 ((i%2)?"\n":" | "));
+    }
+
+    if ( res[<1..<1]!="\n" )
+      res += "\n";
+
+    return res;
+  }
+
+  return 0;
+}
+
+int menue(string str)
+{ string txt;
+
+  _notify_fail("Welchen Teil des Menues moechtest Du sehen?\n");
+  if ( !stringp(txt=menue_text(str)) || sizeof(txt)<1 )
+    return 0;
+  write(txt);
+  return 1;
+}
+
+/* Diese Funktion kann/soll bei Bedarf ueberladen werden, um simultane
+ * Aenderungen des Mappings zu ermoeglichen (zu Beispiel wie es in guten
+ * Tagen groesser Portionen gib, Hobbits per se mehr kriegen, ...
+ */
+mapping adjust_info(string ident, mapping minfo, object zahler,
+                    object empfaenger)
+{
+  return 0;
+}
+
+/* Hier hats jede Menge neue Platzhalter */
+string mess(string str,object pl)
+{
+  string dummy1, dummy2;
+
+  if ( !pl )
+    pl=PL;
+
+  if ( !stringp(str) || str=="" )
+    return str;
+
+  str=implode(explode(str,"&&"),pl->name(WER,2));
+  str=implode(explode(str,"&1&"),pl->name(WER,2));
+  str=implode(explode(str,"&2&"),pl->name(WESSEN,2));
+  str=implode(explode(str,"&3&"),pl->name(WEM,2));
+  str=implode(explode(str,"&4&"),pl->name(WEN,2));
+  str=implode(explode(str,"&1#"),capitalize(pl->name(WER,2)));
+  str=implode(explode(str,"&2#"),capitalize(pl->name(WESSEN,2)));
+  str=implode(explode(str,"&3#"),capitalize(pl->name(WEM,2)));
+  str=implode(explode(str,"&4#"),capitalize(pl->name(WEN,2)));
+  str=implode(explode(str,"&!"),pl->QueryPronoun(WER));
+  str=implode(explode(str,"&5&"),pl->QueryPronoun(WER));
+  str=implode(explode(str,"&6&"),pl->QueryPronoun(WESSEN));
+  str=implode(explode(str,"&7&"),pl->QueryPronoun(WEM));
+  str=implode(explode(str,"&8&"),pl->QueryPronoun(WEN));
+  str=implode(explode(str,"&5#"),capitalize(pl->QueryPronoun(WER)));
+  str=implode(explode(str,"&6#"),capitalize(pl->QueryPronoun(WESSEN)));
+  str=implode(explode(str,"&7#"),capitalize(pl->QueryPronoun(WEM)));
+  str=implode(explode(str,"&8#"),capitalize(pl->QueryPronoun(WEN)));
+
+  return break_string(capitalize(str),78,"", BS_LEAVE_MY_LFS);
+}
+
+protected void _copy_menulist_values(mapping entryinfo, string ident) {
+  /* Kopieren aller Werte ins minfo-Mapping, um Problemen bei Loeschung
+     aus dem Weg zu gehen. Slow and dirty */
+  entryinfo[PM_TEXT]      = deep_copy(menu_list[ident, PM_TEXT]);
+  // PM_INFO is already flat in entryinfo
+  entryinfo[PM_RATE_PUBMASTER]
+                          = deep_copy(menu_list[ident, PM_RATE]);
+  entryinfo[PM_SERVE_MSG] = deep_copy(menu_list[ident, PM_SERVE_MSG]);
+  entryinfo[PM_REFRESH]   = deep_copy(menu_list[ident, PM_REFRESH]);
+  // PM_DELAY is already evaluated in entryinfo
+  entryinfo[PM_DELAY_MSG] = deep_copy(menu_list[ident, PM_DELAY_MSG]);
+  entryinfo[PM_IDS]       = deep_copy(menu_list[ident, PM_IDS]);
+}
+
+int do_deliver(string ident, object zahler, object empfaenger,
+               mapping entryinfo) {
+  waiting -= ({ empfaenger,0 });
+
+  /* Empfaenger muss natuerlich noch da sein */
+  if ( !objectp(empfaenger) || !present(empfaenger) )
+    return 0;
+
+  /* Zahler wird nur wegen der Abwaertskompatibilitaet gebraucht */
+  if ( !objectp(zahler) )
+    zahler = empfaenger;
+
+  // alte Pubs, die do_deliver irgendwie selbst aufrufen, sollten
+  // mit der Zeit korrigiert werden
+  if(!mappingp(entryinfo))
+    raise_error("Pub ruft do_deliver() ohne sinnvolle Argumente auf.\n");
+  if(!member(entryinfo, PM_RATE_PUBMASTER)) {
+    if(!member(menu_list, ident))
+      raise_error("Pub ruft do_deliver() mit geloeschtem Getraenk und "
+                  "teilweisen Argumenten auf!\n");
+
+    _copy_menulist_values(entryinfo, ident);
+    call_out(#'raise_error, 1,
+             "Pub ruft do_deliver() nur mit teilweisen Argumenten auf.\n");
+  }
+
+  entryinfo[PM_RATE_PUBMASTER] = eval_anything(entryinfo[PM_RATE_PUBMASTER],
+                                               empfaenger);
+  entryinfo[P_HP]              = eval_anything(entryinfo[P_HP], empfaenger);
+  entryinfo[P_SP]              = eval_anything(entryinfo[P_SP], empfaenger);
+
+  /* Ueberpruefen, ob Heilmoeglichkeit legal */
+  if ( query_once_interactive(empfaenger)
+      && ((PUBMASTER->RegisterItem(entryinfo[PM_TEXT], entryinfo))<1) ) {
+     tell_object(empfaenger,
+       "Mit diesem Getraenk/Gericht scheint etwas nicht in Ordnung "+
+       "zu sein.\nVerstaendige bitte den Magier, der fuer diesen "+
+       "Raum verantwortlich ist.\n");
+     return -4;
+  }
+
+  if ( QueryProp(P_NPC_FASTHEAL) && !query_once_interactive(empfaenger) ) {
+    entryinfo[H_DISTRIBUTION] = HD_INSTANT;
+  }
+  else {
+    entryinfo[H_DISTRIBUTION] = entryinfo[PM_RATE_PUBMASTER];
+  }
+  empfaenger->consume(entryinfo);
+
+  /* Meldung ausgeben */
+  /* Hinweis: Da die ausfuehrenden Funktionen auch ident und minfo
+   * uebergeben bekommen, kann man hier auch ueber adjust_info oder
+   * an anderer Stelle zusaetzliche Informationen uebergeben...
+   */
+  if (closurep(entryinfo[PM_SERVE_MSG]) )
+    funcall(entryinfo[PM_SERVE_MSG], zahler, empfaenger, ident, entryinfo);
+  else if (stringp(entryinfo[PM_SERVE_MSG]) &&
+           function_exists(entryinfo[PM_SERVE_MSG],ME))
+    call_other(ME, entryinfo[PM_SERVE_MSG], zahler, empfaenger, ident,
+                   entryinfo);
+  else if (pointerp(entryinfo[PM_SERVE_MSG]) &&
+           sizeof(entryinfo[PM_SERVE_MSG])>=2)  {
+    if (stringp(entryinfo[PM_SERVE_MSG][0]) &&
+        sizeof(entryinfo[PM_SERVE_MSG][0]))
+      tell_object(empfaenger,
+        mess(entryinfo[PM_SERVE_MSG][0]+"\n", empfaenger));
+    if (stringp(entryinfo[PM_SERVE_MSG][1]) &&
+        sizeof(entryinfo[PM_SERVE_MSG][1]))
+      tell_room(environment(empfaenger)||ME,
+        mess(entryinfo[PM_SERVE_MSG][1]+"\n",empfaenger),
+        ({empfaenger}) );
+  }
+
+  return 1;
+}
+
+/* Testet, ob genug Geld zur Verfuegung steht.
+ * Falls die Bonitaet anderen Beschraenkungen unterliegt, als
+ * dass der Zahler genug Geld dabei hat, muss diese Methode
+ * ueberschrieben werden.
+ * Rueckgabewerte:
+ * -2 : Out of Money
+ *  0 : Alles OK.
+ * Rueckgabewerte != 0 fuehren zu einem Abbruch der Bestellung
+ */
+int CheckSolvency(string ident, object zahler, object empfaenger,
+                   mapping entryinfo)
+{
+  if ( (zahler->QueryMoney())<entryinfo[P_VALUE] )
+  {
+    string res;
+    if ( !stringp(res=QueryProp(P_PUB_NO_MONEY)) )
+      res = "Das kostet %d Goldstuecke, und Du hast nicht so viel!\n";
+    tell_object(zahler,sprintf(res, entryinfo[P_VALUE]));
+    return -2;
+  }
+  return 0;
+}
+
+/* Fuehrt die Bezahlung durch.
+ * Falls die Bezahlung anders erfolgt, als durch Abzug des Geldes vom Zahler,
+ * muss diese Methode ueberschrieben werden
+ * Rueckgabewerte:
+ * Anzahl der Muenzen, die im Pub landen und bei Reset in die Zentralbank
+ * eingezahlt werden
+ */
+int DoPay(string ident, object zahler, object empfaenger, mapping entryinfo)
+{
+  zahler->AddMoney(-entryinfo[P_VALUE]);
+  return entryinfo[P_VALUE];
+}
+
+/* Rueckgabewerte:
+ * -6 : Nicht vorraetig
+ * -5 : Wirt nicht anwesend
+ * -4 : Illegales Getraenk/Gericht. Ausgabe verweigert.
+ *      Nur bei sofortiger Lieferung...
+ * -3 : Empfaenger bereits voll
+ * -2 : Out of Money
+ * -1 : spendieren ignoriert
+ *  0 : Empfaenger ausgeflogen (sollte eigentlich nicht passieren)
+ *  1 : Alles OK.
+ */
+int consume_something(string ident, object zahler, object empfaenger) {
+  if ( !objectp(zahler) )
+    zahler=PL;
+
+  if ( !objectp(empfaenger) )
+    empfaenger=PL;
+
+  /* Die Abfrage auf anwesenden Wirt erfolgt NUR an dieser Stelle, damit */
+  /* kein Spieler darunter leiden muss, wenn jemand anderes zwischen     */
+  /* Bestellung und Lieferung den Wirt meuchelt.                         */
+  if ( stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER), ME))
+  {
+    string res = QueryProp(P_PUB_NO_KEEPER);
+    if ( !stringp(res) ) {
+      res = "Es ist niemand anwesend, der Dich bedienen koennte.\n";
+    }
+    tell_object(zahler,res);
+    return -5;
+  }
+
+  /* Spendiert und ignoriert? */
+  if ( zahler!=empfaenger )
+  {
+    mixed res = ({"spendiere"});
+    if ( eval_anything(menu_list[ident,PM_INFO][P_DRINK],empfaenger) )
+      res += ({"spendiere.getraenke"});
+    if ( eval_anything(menu_list[ident,PM_INFO][P_FOOD],empfaenger) )
+      res += ({"spendiere.essen"});
+    if ( eval_anything(menu_list[ident,PM_INFO][P_ALCOHOL],empfaenger) )
+      res += ({"spendiere.alkohol"});
+    if ( empfaenger->TestIgnoreSimple(res) )
+    {
+      tell_object(zahler,
+        empfaenger->Name(WER)+" will das nicht.\n");
+      return -1;
+    }
+  }
+
+  /* Hier kann das Info-Mapping erst mal als ganzes angepasst werden. */
+  mapping xinfo;
+  mapping entryinfo = deep_copy(menu_list[ident, PM_INFO]);
+  if ( (xinfo=adjust_info(ident,entryinfo,zahler,empfaenger)) &&
+       mappingp(xinfo) )
+    entryinfo += xinfo;
+
+  /* Genug Geld vorhanden? */
+  entryinfo[P_VALUE] = eval_anything(entryinfo[P_VALUE], zahler);
+  {
+    int res = CheckSolvency(ident, zahler, empfaenger, entryinfo);
+    if (res != 0) return res;
+  }
+
+  string avb;
+  if ( !stringp(avb=CheckAvailability(ident, zahler)) )
+  {
+    string res = QueryProp(P_PUB_UNAVAILABLE);
+    if ( !stringp(res) )
+      res = "Davon ist leider nichts mehr da.\n";
+    tell_object(zahler,res);
+    return -6;
+  }
+
+  /* Textausgabe beim spendieren */
+  if ( empfaenger!=zahler)
+  {
+    tell_room(environment(empfaenger)||ME,
+      zahler->Name(WER)+" spendiert "+empfaenger->name(WEM)+" etwas.\n",
+      ({zahler, empfaenger}) );
+    tell_object(empfaenger,
+      zahler->Name(WER)+" spendiert Dir etwas.\n");
+    tell_object(zahler,
+      "Du spendierst "+empfaenger->name(WEM)+" etwas.\n");
+  }
+
+  /* Testen, ob mans noch essen / trinken kann */
+  /* Die int-Werte werden in minfo uebernommen fuer die Auswertung */
+  /* im Pubmaster. */
+  entryinfo[P_FOOD]    = eval_anything(entryinfo[P_FOOD],   empfaenger);
+  entryinfo[P_ALCOHOL] = eval_anything(entryinfo[P_ALCOHOL],empfaenger);
+  entryinfo[P_DRINK]   = eval_anything(entryinfo[P_DRINK],  empfaenger);
+
+  int result = empfaenger->consume(entryinfo, 1);
+  if (result < 0) {
+    if (result & HC_MAX_FOOD_REACHED)
+      tell_object(empfaenger,
+                  "Du bist zu satt, das schaffst Du nicht mehr.\n");
+    else if (result & HC_MAX_DRINK_REACHED)
+      tell_object(empfaenger,
+                  "So viel kannst Du im Moment nicht trinken.\n");
+    else if (result & HC_MAX_ALCOHOL_REACHED)
+      tell_object(empfaenger,
+                  "Soviel Alkohol vertraegst Du nicht mehr.\n");
+    return -3;
+  }
+
+  /* Gezahlt wird auch sofort */
+  sum += DoPay(ident, zahler, empfaenger, entryinfo);
+
+  /* FPs gibts auch sofort */
+  if (zahler == empfaenger)
+    GiveEP(EP_PUB,menu_list[ident,PM_IDS][0]);
+
+  /* Falls die Anzahl des Bestellten beschraenkt ist, muss diese natuerlich
+   * angepasst werden.
+   */
+  if ( avb!=PR_NONE )
+    DecreaseAvailability(ident,avb);
+
+  /* Gibt es eine Zeitverzoegerung zwischen Bestellen und Servieren? */
+  entryinfo[PM_DELAY_PUBMASTER] = eval_anything(menu_list[ident, PM_DELAY], zahler);
+
+  // alle fuer einen Drink notwendigen Werte kopieren
+  _copy_menulist_values(entryinfo, ident);
+
+  if (entryinfo[PM_DELAY_PUBMASTER]<=0)
+    return do_deliver(ident,zahler,empfaenger,entryinfo);
+
+  /* Bestell-Meldung ausgeben */
+  if (closurep(entryinfo[PM_DELAY_MSG]))
+    funcall(entryinfo[PM_DELAY_MSG], zahler, empfaenger,ident, entryinfo);
+  else if (stringp(entryinfo[PM_DELAY_MSG]) &&
+           function_exists(entryinfo[PM_DELAY_MSG],ME))
+    call_other(ME, entryinfo[PM_DELAY_MSG], zahler, empfaenger, ident,
+               entryinfo);
+  else if (pointerp(entryinfo[PM_DELAY_MSG]) &&
+           sizeof(entryinfo[PM_DELAY_MSG])>=2) {
+    if (stringp(entryinfo[PM_DELAY_MSG][0]) &&
+       sizeof(entryinfo[PM_DELAY_MSG][0]))
+      tell_object(empfaenger,
+        mess(entryinfo[PM_DELAY_MSG][0]+"\n",empfaenger));
+    if (stringp(entryinfo[PM_DELAY_MSG][1]) &&
+        sizeof(entryinfo[PM_DELAY_MSG][1]))
+      tell_room(environment(empfaenger)||ME,
+        mess(entryinfo[PM_DELAY_MSG][1]+"\n",empfaenger),
+        ({empfaenger}) );
+  }
+
+  waiting += ({ empfaenger });
+  call_out("do_deliver", entryinfo[PM_DELAY_PUBMASTER],
+           ident, zahler, empfaenger, entryinfo);
+
+  return 1;
+}
+
+/* Rueckgabewere: 0: Nicht im Menue gefunde, 1 sonst */
+int search_what(string str,object zahler,object empfaenger)
+{ string ident;
+
+  if ( member(waiting,empfaenger)!=-1 )
+  {
+    if ( PL==empfaenger )
+      write("Du wartest doch noch auf etwas!\n");
+    else
+      write(empfaenger->Name(WER,2)+" wartet noch auf etwas.\n");
+    return 1;
+  }
+
+  str = lower_case(str);
+  if ( ident=id_list[str] )
+  {
+    consume_something(ident,zahler,empfaenger);
+    return 1;
+  }
+
+  return 0;
+}
+
+// Neue Version von Mesi:
+int spendiere(string str)
+{
+   _notify_fail("spendiere <spieler> <drink>\n");
+
+   if ( !stringp(str) || str=="" )
+     return 0;
+
+   string who,what;
+   int whoidx;
+
+   if (sscanf(str,"%s %d %s",who,whoidx,what)!=3
+    && sscanf(str,"%s %s",who,what)!=2)
+      return 0;
+  object target=present(who, whoidx);
+  if(!target && this_player()) target=present(who, whoidx, this_player());
+   if ( !target || !living(target) )
+   {
+     write("Das Lebewesen ist nicht hier...\n");
+     return 1;
+   }
+
+   notify_fail((string)QueryProp(P_PUB_NOT_ON_MENU)||"So etwas gibt es hier nicht!\n");
+
+   return search_what(what,PL,target);
+}
+
+int bestelle(string str)
+{
+  notify_fail((string)QueryProp(P_PUB_NOT_ON_MENU));
+
+  if ( !stringp(str) )
+    return 0;
+
+  return search_what(str,PL,PL);
+}
+
+int pubinit()
+{ string  *liste,ident,fn;
+  int     si,erg,max;
+  mapping minfo,xinfo;
+
+  if ( !PL || !IS_WIZARD(PL) )
+    return 0;
+
+  si=sizeof(liste=sort_array(m_indices(menu_list),#'<));
+  if ( si<1 )
+    return notify_fail("Keine Gerichte/Getraenke vorhanden.\n"),0;
+
+  fn=old_explode(object_name(ME),"#")[0];
+  printf("\n%'_'|30s %3s %3s %3s %5s %2s %2s %3s %3s %4s %3s\n",
+    "ITEM","ALC","DRI","FOO","VALUE","RT","DL","_HP","_SP","TEST","MAX");
+  for ( --si ; si>=0 ; si-- )
+  {
+    ident=liste[si];
+    minfo=deep_copy(menu_list[ident,PM_INFO]);
+
+    if ( (xinfo=adjust_info(ident,minfo,PL,PL)) && mappingp(xinfo) )
+      minfo+=xinfo;
+
+    minfo[P_VALUE]   = eval_anything(minfo[P_VALUE],            PL);
+    minfo[P_FOOD]    = eval_anything(minfo[P_FOOD],             PL);
+    minfo[P_ALCOHOL] = eval_anything(minfo[P_ALCOHOL],          PL);
+    minfo[P_DRINK]   = eval_anything(minfo[P_DRINK],            PL);
+    minfo[PM_DELAY_PUBMASTER]
+                     = eval_anything(menu_list[ident,PM_DELAY], PL);
+    minfo[PM_RATE_PUBMASTER]
+                     = eval_anything(menu_list[ident,PM_RATE],  PL);
+    minfo[P_HP]      = eval_anything(minfo[P_HP],               PL);
+    minfo[P_SP]      = eval_anything(minfo[P_SP],               PL);
+    erg=PUBMASTER->RegisterItem(menu_list[ident,0],minfo);
+    max=PUBMASTER->CalcMax(minfo,fn);
+
+    printf("%-'..'30.30s %3d %3d %3d %5d %2d %2d %3d %3d %|4s %3d\n",
+      menu_list[ident,PM_TEXT],
+      minfo[P_ALCOHOL], minfo[P_DRINK], minfo[P_FOOD],
+      minfo[P_VALUE],
+      minfo[PM_RATE_PUBMASTER],
+      minfo[PM_DELAY_PUBMASTER],
+      minfo[P_HP], minfo[P_SP],
+      ( erg ? "OK" : "FAIL" ),max);
+  }
+  write("Done.\n");
+  return 1;
+}
+
+void reset()
+{
+  if ( sum>0 )
+    ZENTRALBANK->PayIn(sum);
+  sum=0;
+  refresh_count++;
+  UpdateAvailability();
+}
+
+void add_gluehwein()
+{
+  if ( ctime(time())[4..6]=="Dec" )
+    AddToMenu( "Gluehwein",({"gluehwein"}),([
+      P_VALUE   : 80,
+      P_DRINK   :  5,
+      P_ALCOHOL : 20,
+      P_HP      : 15,
+      P_SP      : 15 ]),2,({
+      ("Du trinkst ein Glas Gluehwein, an dem Du Dir beinahe die Zunge "+
+       "verbrennst.\n"),
+      ("&& bestellt ein Glas Gluehwein und verbrennt sich beim\n"+
+       "Trinken beinahe die Zunge.\n") }), 0, 0, 0);
+}
+
+void add_std_drinks()
+{
+  if ( QueryProp(P_NO_STD_DRINK) )
+    return ;
+  add_gluehwein();
+}
+
+mapping query_menulist()
+{
+  return deep_copy(menu_list);
+}
+
+string *query_drinks()
+{
+  string *dr=({});
+  foreach( string ident, string menuetext, mapping minfo: menu_list) {
+    if (eval_anything(minfo[P_DRINK], 0))
+      dr += ({ ident });
+  }
+  return dr;
+}
+
+string *query_food()
+{
+  string *fo=({});
+  foreach( string ident, string menuetext, mapping minfo: menu_list) {
+    if (eval_anything(minfo[P_FOOD], 0))
+      fo += ({ ident });
+  }
+  return fo;
+}
diff --git a/std/room/restrictions.c b/std/room/restrictions.c
new file mode 100644
index 0000000..aa50c9e
--- /dev/null
+++ b/std/room/restrictions.c
@@ -0,0 +1,61 @@
+// MorgenGrauen MUDlib
+//
+// room/restrictions.c -- weight property handling for rooms
+//
+// $Id: restrictions.c 9020 2015-01-10 21:49:41Z Zesstra $
+
+inherit "std/container/restrictions";
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+//#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <properties.h>
+
+static int _set_weight(int w);
+static int _query_weight();
+static int _set_total_weight(int w);
+static int _query_total_weight();
+
+void create()
+{
+  Set(P_WEIGHT,PROTECTED,F_MODE);
+  Set(P_TOTAL_WEIGHT,PROTECTED,F_MODE);
+}
+
+int MayAddWeight(int w)
+{
+  return 0;
+}
+
+int MayAddObject(object ob)
+{  return 1;  }
+
+int PreventInsert(object ob)
+{
+  return 0;
+}
+
+static int _set_weight(int w)
+{
+  return 0;
+}
+
+static int _query_weight()
+{
+  return 0;
+}
+
+static int _set_total_weight(int w)
+{
+  return 0;
+}
+
+static int _query_total_weight()
+{
+  return 0;
+}
diff --git a/std/room/shop.c b/std/room/shop.c
new file mode 100644
index 0000000..c429127
--- /dev/null
+++ b/std/room/shop.c
@@ -0,0 +1,1110 @@
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/trading_price";
+
+#define NEED_PROTOTYPES
+#include <thing/commands.h>
+#include <thing/description.h>
+#undef NEED_PROTOTYPES
+
+#include <defines.h>
+#include <properties.h>
+#include <rooms.h>
+#include <language.h>
+#include <moving.h>
+#include <routingd.h>
+#include <bank.h>
+#include <combat.h>
+#include <input_to.h>
+#include <unit.h>
+#include <money.h>
+
+// TODO: pruefen, um die Variablen private sein koennen.
+
+// allgemein benoetige Variablen
+nosave string storage; // Filename des Stores in dem die Objekte lagern...
+nosave mapping ob_anz; // wie oft ist ein Objekt im Laden vorhanden?
+
+// Jetzt Variablen fuer staendig verfuegbare Objekte:
+nosave string *fixed_obj; // Liste der staendig verfuegbaren Objekte
+nosave mapping fixed_value; // Preise bei Sonderangeboten
+nosave mapping fixed_ids;   // Ids
+
+varargs void AddFixedObject(string str, int val, string|string* ids)
+{
+  int i;
+
+  // Achtung, bei solchen Objekten muss die Blueprint initialisiert werden!
+  if (!str) return;
+  if (!val) val=str->QueryProp(P_VALUE);
+  if (!ids)
+  {
+    if (str->QueryProp("u_ids")) // units haben keine P_IDS
+      ids=str->QueryProp("u_ids")[0];
+    else
+      ids=str->QueryProp(P_IDS);
+  }
+  if (!pointerp(ids))
+  {
+    if (stringp(ids))
+      ids=({ids});
+    else
+      ids=({});
+  }
+
+  fixed_obj += ({str});
+  fixed_value[str] = val;
+  // Alle IDs entfernen, die Sonderzeichen enthalten. Die koennte ein Spieler
+  // bei "kaufe" ohnehin nicht eingeben.
+  ids -= regexp(ids, "[\b\n\r\t]");
+  foreach(string id : ids)
+  {
+    // Nur IDs aufnehmen, die keine Grossbuchstaben enthalten, da Spieler
+    // diese ebenfalls nicht eingeben koennte.
+    if ( lowerstring(id) == id )
+      fixed_ids[id]=str;
+  }
+}
+
+void RemoveFixedObject(string filename)
+{
+  if( !stringp(filename) || !sizeof(fixed_obj))
+    return;
+  if( member(fixed_obj, filename)==-1 )
+    return;
+
+  fixed_obj -= ({ filename });
+  m_delete(fixed_value, filename);
+
+  foreach(string id, string file : fixed_ids)
+  {
+    if ( file == filename )
+      m_delete(fixed_ids, id);
+  }
+}
+
+static string SetStorageRoom(string str)
+{
+  if (str && stringp(str)) return storage=str;
+  return 0;
+}
+
+string QueryStorageRoom()
+{   return storage;   }
+
+protected void create()
+{
+  object router;
+  
+  if (object_name(this_object()) == __FILE__[0..<3])
+  {
+    set_next_reset(-1);
+    return;
+  }
+
+  trading_price::create();
+
+  SetProp( P_NAME, "Haendler" );
+  SetProp( P_GENDER, MALE );
+  SetProp( P_ROOM_TYPE, QueryProp(P_ROOM_TYPE) | RT_SHOP );
+
+  AddCmd("zeige","list");
+  AddCmd(({"kauf","kaufe","bestell","bestelle"}),"buy");
+  AddCmd(({"verkauf","verkaufe","verk"}),"sell");
+  AddCmd(({"versetz","versetze"}),"force_sell");
+  AddCmd(({"schaetz","schaetze"}),"evaluate");
+  AddCmd(({"untersuche","unt"}), "show_obj");
+
+  SetTradingData(50000,300,3);
+
+  ob_anz=([]);
+  fixed_obj=({});fixed_value=([]);fixed_ids=([]);
+
+  AddFixedObject(BOERSE, 80,({ "boerse","geldboerse"}));
+
+  if (!clonep(ME) && objectp(router=find_object(ROUTER)))
+    router->RegisterTarget(TARGET_SHOP,object_name(ME));
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// Legacy-Version von GetShopItems() fuer die erbenden Laeden, die auf
+// die Funktion in dieser Form angewiesen sind.
+static mixed *GetList()
+{
+  object store = load_object(storage);
+  store->_register_shop(ME);
+
+  mixed *output=({});
+  if (!objectp(store))
+    return output;
+
+  mixed tmp = map(fixed_obj, #'load_object)+all_inventory(store);
+  mapping tmp2 = ([]);
+  mixed str;
+  string comp;
+  int i;
+  int s=1;
+
+  for (i=sizeof(tmp)-1 ; i>=0 ; i--)
+  {
+    str = ({ ({ sprintf("%-25.25s%7.7d",
+                        (tmp[i]->short()||"???")[0..<3],
+                        QueryBuyValue(tmp[i], PL)),
+                tmp[i] }) });
+    comp=str[0][0][0..25];
+    if (!tmp2[comp])
+    {
+      tmp2[comp] = s++;
+      output += str;
+    }
+    else output[tmp2[comp]-1][0] = str[0][0];
+  }
+  return output;
+}
+
+// Legacy-Version von PrintList() fuer die erbenden Laeden, die auf
+// die Funktion in dieser Form angewiesen sind.
+static int DoList(string query_fun)
+{
+  mixed* output=GetList();
+  int si = sizeof(output);
+  if (!si)
+  {
+    write("Im Moment sind wir leider VOELLIG ausverkauft!\n");
+    return 1;
+  }
+  string out="";
+  int i;
+  int indent;
+  for (i=0; i<si; i++)
+  {
+    if (call_other(ME, query_fun, output[i][1]))
+    {
+      indent = !indent;
+      out += sprintf("%3d. %s", i+1, output[i][0]);
+      if (!indent)
+        out += "\n";
+      else out += " | ";
+    }
+  }
+  if (indent)
+    out+="\n";
+  PL->More(out);
+  return 1;
+}
+
+// Liefert eine Liste der Objekte zurueck, die gerade im Storage liegen,
+// pro Blueprint jeweils eins.
+protected object* GetShopItems()
+{
+  object store = load_object(storage);
+  store->_register_shop(ME);
+  object* output = ({});
+  object* all_items = all_inventory(store);
+
+  // Wir brauchen eine Liste, die von jeder Blueprint nur einen Clone
+  // enthaelt. Daher werden die Ladenamen der Objekte als Keys im Mapping
+  // <items> verwendet und jeweils der aktuelle Clone als Value zugeordnet.
+  mapping items = m_allocate(sizeof(all_items));
+  foreach(object ob: all_items)
+  {
+    items[load_name(ob)] = ob;
+  }
+  // Die Fixed Objects werden ans Ende angehaengt, damit in dem Fall, dass
+  // ein Clone eines solchen Objektes im Lager liegt, dieser zuerst verkauft
+  // wird und nicht immer wieder ein neuer erstellt wird.
+  return m_values(items) + map(fixed_obj, #'load_object);
+}
+
+#define LIST_LONG  1
+#define LIST_SHORT 0
+
+// Kuemmert sich um die Listenausgabe fuer den Befehl "zeige"
+varargs protected int PrintList(string filter_fun, int liststyle)
+{
+  // Alle Items im Lager holen. Wenn keine vorhanden, tut uns das leid.
+  object *items_in_store = GetShopItems();
+  if ( !sizeof(items_in_store) ) {
+    write("Im Moment sind wir leider VOELLIG ausverkauft!\n");
+    return 1;
+  }
+
+  // Das Listenformat ist von der Spielereingabe abhaengig. Wurde "lang"
+  // angefordert, geben wir einspaltig aus mit groesserer Spaltenbreite.
+  // Die Spaltenbreite wird dabei von dem Item mit dem laengsten Namen im
+  // gesamten Shop-Inventar bestimmt, damit nicht bei jeder Teilliste
+  // (Waffen, Ruestungen, Verschiedenes) unterschiedliche Breiten rauskommen.
+  //
+  // Der erste Parameter enthaelt die Katalognummer des Items, der zweite
+  // die Kurzbeschreibung, der dritte den Preis.
+  string listformat = "%3d. %-25.25s %6.6d";
+  if ( liststyle == LIST_LONG )
+  {
+    string* names = sort_array(
+                      items_in_store->short(),
+                      function int (string s1, string s2) {
+                        return (sizeof(s1) < sizeof(s2));
+                      });
+    // Wenn einspaltig ausgegeben wird, soll die Liste nicht beliebig breit
+    // werden. Daher wird die Short auf 65 Zeichen limitiert.
+    int len = 65;
+    if ( sizeof(names) )
+      len = min(len, sizeof(names[0]));
+    listformat = "%3d. %-"+len+"."+len+"s %6.6d";
+  }
+
+  string out="";
+  // Variablen, die innerhalb der Schleife benoetigt werden.
+  string kurz;
+  int indent, preis;
+  object item;
+  // Ueber die Liste laufen. <i> wird benoetigt, um die ausgegebene Liste
+  // konsistent numerieren zu koennen, damit kaufe <nr> funktioniert.
+  foreach(int i : sizeof(items_in_store))
+  {
+    item = items_in_store[i];
+    if ( call_other(ME, filter_fun, item) )
+    {
+      // Kurzbeschreibung und Preis ermitteln. Items ohne Short werden
+      // als "?" angezeigt.
+      kurz = (item->short() || "???")[0..<3];
+      preis = QueryBuyValue(item, PL);
+      // Beschreibung des Items anfuegen.
+      out += sprintf(listformat, i+1, kurz, preis);
+      indent = !indent;
+      // Wenn indent gesetzt ist, handelt es sich um das linke Item in der
+      // zweispaltigen Liste, dann fuegen wir einen Spaltentrenner an,
+      // ansonsten ist es das rechte, dann brechen wir um.
+      // Gilt natuerlich nur fuer kurze Listen.
+      out += ((indent && liststyle==LIST_SHORT)? " | " : "\n");
+    }
+  }
+  // Wenn die Liste eine ungerade Zahl Items enthaelt, ist in der letzten
+  // Zeile links ein Item aufgefuehrt, daher muss danach umbrochen werden.
+  // Gilt natuerlich nur fuer kurze Listen
+  if (indent && liststyle==LIST_SHORT)
+    out+="\n";
+
+  // Vor den Listen wird eine Info-Zeile ausgegeben, um gefilterte Listen
+  // kenntlich zu machen. Wird nach der Filterung des Inventars erzeugt,
+  // um eine leere Meldung ausgeben zu koennen, wenn nach Filterung nichts
+  // mehr uebrigbleibt.
+  string was;
+  switch(filter_fun)
+  {
+    case "IsArmour": was = "Ruestungen"; break;
+    case "IsWeapon": was = "Waffen"; break;
+    case "NoWeaponNoArmour":
+         was = (out==""?"sonstigen Waren":"sonstige Waren"); break;
+    default: was = "Waren"; break;
+  }
+  // <out> ist ein Leerstring, wenn keine Waren da sind, die dem Filterkri-
+  // terium entsprechen. Dann gibt's eine entsprechende Meldung.
+  if ( out == "" )
+    out = sprintf("Leider sind momentan keine %s im Angebot.\n", was);
+  else
+    out = sprintf("Folgende %s kannst Du hier kaufen:\n",was) + out;
+
+  PL->More(out);
+  return 1;
+}
+
+// Hilfsfunktionen zum Filtern des Ladeninventars fuer den "zeige"-Befehl
+static int AlwaysTrue(object ob)
+{   return 1;   }
+
+static string IsWeapon(object ob)
+{  return ob->QueryProp(P_WEAPON_TYPE);  }
+
+static string IsArmour(object ob)
+{  return ob->QueryProp(P_ARMOUR_TYPE);  }
+
+static int NoWeaponNoArmour(object ob)
+{ return (!ob->QueryProp(P_WEAPON_TYPE) && !ob->QueryProp(P_ARMOUR_TYPE)); }
+
+
+// Diese Funktion ist oeffentlich, falls Magier abfragen wollen, ob ein laden
+// ein Objekt zerstoeren wuerde. Aber: Benutzung auf eigenes Risiko! Es wird
+// nicht garantiert, dass diese Funktion bzw. ihr Interface sich nicht
+// aendert.
+public int CheckForDestruct(object ob) // Pruefen, ob zerstoert werden soll
+{
+  string str;
+  /*
+   * P_NOBUY - Objekte auf jeden Fall zerstoeren
+   */
+  if(ob->QueryProp(P_NOBUY)) return 1;
+  /*
+   * Beschaedigte Objekte werden ebenfalls zerstoert
+   */
+  if(ob->QueryProp(P_DAMAGED)) return 1;
+  /*
+   * Ruestungen wenn sie a) eine DefendFunc definiert haben oder
+   *                     b) ueber der in KEEP_ARMOUR_CLASS definierten AC
+   *                        liegen (siehe /sys/combat.h)
+   */
+  if(str = IsArmour(ob))
+  {
+    if(objectp(ob->QueryProp(P_DEFEND_FUNC))) return 1;
+    if(ob->QueryProp(P_AC) >= KEEP_ARMOUR_CLASS[str]) return 1;
+    return 0;
+  }
+  /*
+   * Waffen wenn sie a) 1-haendig sind und eine WC > 120 besitzen oder
+   *                 b) 2-haendig sind und eine WC > 150 besitzen oder aber
+   *                 c) eine HitFunc definiert haben
+   */
+  if(str = IsWeapon(ob))
+  {
+    if(ob->QueryProp(P_NR_HANDS) > 1 && ob->QueryProp(P_WC) > 150) return 1;
+    if(ob->QueryProp(P_NR_HANDS) < 2 && ob->QueryProp(P_WC) > 120) return 1;
+    if(objectp(ob->QueryProp(P_HIT_FUNC))) return 1;
+    return 0;
+  }
+  return 0;
+}
+
+static int list(string str)
+{
+  _notify_fail(
+    "Bitte 'zeige', 'zeige waffen', 'zeige ruestungen' oder\n"
+    "'zeige verschiedenes' eingeben. Wenn Du das Schluesselwort 'lang'\n"
+    "oder '-1' anhaengst, wird die Liste einspaltig ausgegeben.\n");
+
+  if (!stringp(str) || !sizeof(str) )
+    return PrintList("AlwaysTrue");
+  if ( str == "lang" || str == "-1" )
+    return PrintList("AlwaysTrue", LIST_LONG);
+
+  string *params = explode(str," ");
+  if (sizeof(params[0])<3)
+    return 0;
+
+  int liststyle = LIST_SHORT;
+  if ( sizeof(params)>1 && params[1] == "lang" )
+    liststyle = LIST_LONG;
+
+  str=params[0][0..2];
+  if (str=="waf")
+    return PrintList("IsWeapon", liststyle);
+  if (str=="ver")
+    return PrintList("NoWeaponNoArmour", liststyle);
+  if (str=="rue")
+    return PrintList("IsArmour", liststyle);
+  return 0;
+}
+/*
+static varargs int QueryBuyValue(mixed ob, object client)
+{
+  if (objectp(ob))
+    return trading_price::QueryBuyValue(ob, client);
+  return (fixed_value[ob]*QueryBuyFact(client))/100;
+}
+*/
+
+static varargs int QueryBuyValue(object ob, object client)
+{
+  int fprice = fixed_value[load_name(ob)];
+
+  return (fprice>0) ? 
+         fprice*QueryBuyFact(client)/100 : 
+         trading_price::QueryBuyValue(ob, client);
+}
+
+static void UpdateCounter(object ob, int num)
+{
+  string tmp;
+
+  if (!num || !objectp(ob)) return;
+  tmp=BLUE_NAME(ob);
+  if (tmp[0..2]!="/d/" && tmp[0..8]!="/players/")
+    tmp=ob->short()+tmp;
+  ob_anz[tmp] += num;
+  if (ob_anz[tmp] <= 0)
+    m_delete(ob_anz,tmp);
+}
+
+protected object FindInStore(string|int x)
+{
+  object* list = GetShopItems();
+  if ( intp(x) && x>0 && x<=sizeof(list) ) {
+    return list[x-1];
+  }
+  if (stringp(x))
+  {
+    if ( fixed_ids[x] )
+      return load_object(fixed_ids[x]);
+    list = filter_objects(list, "id", x);
+    if ( sizeof(list) )
+      return list[0];
+    // Wenn nix im Store gefunden (das schliesst eigentlicht schon die BPs der
+    // fixen Objekte ein, aber nicht, wenn diese nicht konfiguriert sind. D.h.
+    // diese Pruefung ist fuer nicht-konfigurierte BPs), Liste der
+    // FixedObjects pruefen unde so die eventuell manuell in
+    // AddFixedObject() angegebene Liste von IDs beruecksichtigen.
+    else if ( fixed_ids[x] )
+      return load_object(fixed_ids[x]);
+  }
+  return 0;
+}
+
+static string buy_obj(mixed ob, int short)
+{ return 0; }
+
+private void really_buy(int val, object pl, object ob, int u_req)
+{
+  // Staendig verfuegbare Objekte (fixed_obj) sind daran erkennbar, dass sie
+  // nicht im Lager liegen. Daher hier einen Clone erstellen, der dann 
+  // stattdessen rausgegeben wird.
+  if ( !present(ob, find_object(storage)) )
+    ob = clone_object(ob);
+
+  // In Unitobjekten U_REQ (wieder) setzen (wegen input_to (bei dem sich das
+  // Kommandoverb aendert und deswegen U_REQ geloescht wird), und wegen
+  // Kaufens von Fixed-Objekt-Unitobjekten (bei diesen muss U_REQ _nach_ dem
+  // Clonen im Clone gesetzt werden, nicht automagisch in der BP durch den
+  // Aufruf von id() weiter vorher).
+  if (u_req>0)
+  {
+    // Das QueryProp() ist nicht unnoetig. Bei der Abfrage von U_REQ wird
+    // U_REQ genullt, wenn das aktuelle query_verb() != dem letzten ist.
+    // Bei der ersten Abfrage wuerde als das hier gesetzt U_REQ wieder
+    // geloescht. Daher muss das jetzt hier als erstes einmal abgefragt
+    // werden...
+      ob->QueryProp(U_REQ);
+      ob->SetProp(U_REQ, u_req);
+  }
+
+  pl->AddMoney(-val);
+  _add_money(val);
+
+  if (ob->move(pl,M_GET) != MOVE_OK) // Kann der Spieler das Objekt tragen?
+  {
+    write(break_string("Du kannst "+ob->name(WEN,1)+" nicht mehr tragen. "
+     "Ich lege "+ob->QueryPronoun(WEN)+" hier auf den Boden.",78,
+     Name(WER)+" sagt: "));
+    ob->move(ME,M_NOCHECK);                         // Nein :-)
+  }
+  else
+  {
+    // Falls das Objekt sich vereinigt hat, muss jetzt wieder U_REQ
+    // restauriert werden.
+    ob->SetProp(U_REQ, u_req);
+    write(break_string("Du kaufst "+ob->name(WEN,1)+".", 78));
+  }
+
+  say(break_string(PL->Name(WER)+" kauft "+ob->name(WEN)+".",78), ({PL}));
+  UpdateCounter(ob,-1);
+}
+
+static void ask_buy(string str, int val, object pl, object ob, int u_req)
+{
+  _notify_fail(break_string("Gut, Du kannst es Dir ja jederzeit "
+   "noch anders ueberlegen.",78,Name(WER)+" sagt: "));
+
+  if(!str || !stringp(str) || str == "nein" || str == "n")
+  {
+    return;
+  }
+  if(str != "ja" && str != "j")
+  {
+    return;
+  }
+  really_buy(val, pl, ob, u_req);
+}
+
+static int buy(string str)
+{
+  int i, val, par, dex;
+  mixed ob, res;
+  string dummy;
+
+  if (!str) {
+    write("Was willst Du kaufen?\n");
+    return 1;
+  }
+
+  if (stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER), ME)) {
+    write("Es ist niemand da, der Dich bedienen koennte.\n");
+    return 1;
+  }
+
+  _notify_fail(break_string("Das kann ich in meinem Lager nicht finden.",78,
+   Name(WER)+" sagt: "));
+
+  // Um auch Teile von Unit-Stacks kaufen zu koennen, z.B. "kaufe 5 phiolen",
+  // darf hier zusaetzlich <dummy> nur ein Leerstring sein, sonst verzweigt
+  // die Syntaxpruefung hierhin und es wird das 5. Item der Liste gekauft.
+  if (sscanf(str,"%d%s",i,dummy)>0 && i>0 && !sizeof(dummy)) {
+    ob=FindInStore(i);
+  }
+  else ob=FindInStore(str);
+
+  if (!ob) return 0;
+
+  if (str = buy_obj(ob,0)) 
+  {
+    write(break_string(str,78,Name(WER)+" sagt: "));
+    return 1;
+  }
+
+  val = QueryBuyValue(ob,PL);
+
+  if (PL->QueryMoney() < val)
+  {
+    write(break_string(capitalize(ob->QueryPronoun(WER))+" wuerde "+val+
+     " Muenzen kosten, und Du hast nur "+PL->QueryMoney()+".",78,
+     Name(WER)+" bedauert: "));
+    return 1;
+  }
+
+  // Anzahl der angeforderten Einheiten vor dem Bewegen zwischenspeichern.
+  // Weil dabei im Fall von Units eine Vereinigung mit bereits im Inventar
+  // befindlichen Einheiten stattfindet, muss das ggf. nach Bewegen
+  // zurueckgesetzt werden.
+  int u_req = ob->QueryProp(U_REQ);
+
+  if ((res = ob->QueryProp(P_RESTRICTIONS)) && mappingp(res) &&
+      (res = call_other("/std/restriction_checker",
+                        "check_restrictions",PL,res)) &&
+      stringp(res))
+  {
+    _notify_fail(break_string("Du koenntest "+ob->name(WEN,2)+" nicht "
+     "verwenden. Grund: "+res+"Moechtest Du "+ob->QueryPronoun(WEN)+
+     " dennoch kaufen?",78,Name(WER)+" sagt: "));
+
+    input_to("ask_buy",INPUT_PROMPT, "(ja/nein) ", val,PL,ob,u_req);
+    return 0;
+  }
+
+  par = (int)ob->QueryProp(P_PARRY);
+  dex = (int)PL->QueryAttribute(A_DEX);
+
+  if ((((par < PARRY_ONLY) && ((dex + 8) * 10) < ob->QueryProp(P_WC)) ||
+       ((par > PARRY_NOT)  && ((dex + 5) *  2) < ob->QueryProp(P_AC))) &&
+      VALID_WEAPON_TYPE(ob))
+  {
+    _notify_fail(break_string("Du koenntest "+ob->name(WEN,2)+" nicht "
+     "zuecken, da Deine Geschicklichkeit dafuer nicht ausreicht. Moechtest "
+     "Du "+ob->QueryPronoun(WEN)+" dennoch kaufen?",78,
+     Name(WER)+" sagt: "));
+
+    input_to("ask_buy",INPUT_PROMPT, "(ja/nein) ", val,PL,ob,u_req);
+    return 0;
+  }
+
+  really_buy(val, PL, ob, u_req);
+
+  return 1;
+}
+
+private void give_money(int value)
+// Geld gutschreiben...
+{
+  if (!value) return;
+  write(break_string(Name(WER, 1)+" zahlt Dir "+value+" Goldstueck"
+                    +(value==1?".":"e."), 78));
+  if ((PL->AddMoney(value))<=0) {
+     object mon;
+
+     write("Du kannst das Geld nicht mehr tragen!\n");
+     mon=clone_object(GELD);
+     mon->SetProp(P_AMOUNT,value);
+     mon->move(ME,M_MOVE_ALL|M_NOCHECK);
+  }
+}
+
+static int make_to_money(object ob, int value)
+// Interne Funktion, die ob versucht in das Lager zu ueberfuehren und das
+// Geld das dabei fuer den Spieler abfaellt zurueckliefert.
+{
+  string str;
+  int ret;
+
+  if (!objectp(ob) || environment(ob)==find_object(storage)) {
+    write(break_string(Name(WER, 1)+" wundert sich ueber Dich.", 78));
+    return 0;
+  }
+  if (value>QueryProp(P_CURRENT_MONEY)) {
+    write(break_string("Ich hab das Geld leider nicht mehr.", 78,
+                       Name(WER, 1)+" sagt: "));
+    return 0;
+  }
+  // U_REQ merken, falls sich Objekte vereinigen. Sonst stimmt nicht nur der
+  // Name, sondern es werden ggf. auch die falsche Anzahl zerstoert.
+  //TOOO: Oder Units entsorgen und als Feature deklarieren?
+  int req = ob->QueryProp(U_REQ);
+  if (CheckForDestruct(ob) > 0)  // soll ob zerstoert werden?
+  {
+    ret = ob->move(storage,M_PUT|M_GET);
+    // Falls das Objekt sich vereinigt hat (Units), muessen die gewuenschten
+    // Einheiten restauriert werden.
+    // Problem: falls das verkaufte Objekt Units hat, beschaedigt ist und sich
+    // vereinigt hat, sind jetzt leider alle Einheiten im Lager beschaedigt.
+    // Das ist unschoen - aber mir jetzt zuviel AUfwand, das korrekt zu bauen,
+    // weil es nur sehr selten vorkommt. (Hint: separater Muellraum)
+    ob->SetProp(U_REQ, req);
+    if (ret > 0) // Sonst werden auch Sachen zerstoert, die man nicht
+    {                 // weglegen kann
+      say(break_string(PL->Name()+" verkauft "+ob->name(WEN)+".", 78));
+      if(ob->QueryProp(P_DAMAGED))  // Andere Meldung bei beschaedigten
+      {                             // Objekten ...
+        write(break_string(Name(WER,1)+" findet zwar keinen Gefallen an "
+         +ob->name(WEM,1)+", nimmt "+ob->QueryPronoun(WEN)+" Dir zuliebe "
+         "aber trotzdem.",78));
+      }
+      else
+      {
+        write(break_string(Name(WER, 1)+" findet Gefallen an "
+           +ob->name(WEM, 1) + " und legt "+ob->QueryPronoun(WEN)
+           +" zu "+(QueryProp(P_GENDER)==FEMALE?"ihren":"seinen")
+           +" Privatsachen.", 78));
+      }
+      /* Er zahlt Dir "+value+" Muenze"+(value==1?"":"n")+" dafuer.", 78)); */
+      _add_money(-value);
+      _add_money(value*QueryProp(P_SHOP_PERCENT_LEFT)/100); // Wegen Zerstoerung des Objektes
+      UpdateCounter(ob,1);
+      ob->remove(1);
+      return value;
+    }
+    else if (ret == ME_CANT_BE_DROPPED) {
+      if ((str=ob->QueryProp(P_NODROP)) && stringp(str)) {
+        write(str);
+        return 0;
+      }
+      write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!", 78));
+      return 0;
+    }
+    else
+      write(break_string(ob->Name(WER)+" interessiert mich nicht.", 78,
+               Name(WER, 1)+" sagt: "));
+  }
+  else if ((ret=(ob->move(storage,M_PUT|M_GET)))>0)
+  {
+    // Falls das Objekt sich vereinigt hat (Units), muessen die gewuenschten
+    // Einheiten restauriert werden.
+    ob->SetProp(U_REQ, req);
+    say(break_string(PL->Name(WER)+" verkauft "+ob->name(WEN)+".", 78));
+    _add_money(-value);
+    UpdateCounter(ob,1);
+    return value;
+  }
+  else if (ret == ME_CANT_BE_DROPPED) {
+    if ((str=ob->QueryProp(P_NODROP)) && stringp(str))
+       write(str);
+    else write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!", 78));
+  }
+  else write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!", 78));
+  return 0;
+}
+
+static void ask_sell(string str, object ob, int val, int u_req)
+// Wenn ein einzelnen Stueck unter Wert verkauft werden soll, wird nachgefragt
+// u_req ist bei Unitobjekten die Anzahl an zu verkaufenden Einheiten. Bei
+// normalen Objekten ist u_req 0.
+{
+  str=lower_case(str||"");
+  if (str=="ja"||str=="j")
+  {
+     // In Unitobjekten U_REQ (wieder) setzen.
+     if (u_req>0)
+     {
+       // Das QueryProp() ist nicht unnoetig. Bei der Abfrage von U_REQ wird
+       // U_REQ genullt, wenn das aktuelle query_verb() != dem letzten ist.
+       // Bei der ersten Abfrage wuerde als das hier gesetzt U_REQ wieder
+       // geloescht. Daher muss das jetzt hier als erstes einmal abgefragt
+       // werden...
+         ob->QueryProp(U_REQ);
+         ob->SetProp(U_REQ, u_req);
+     }
+     give_money(make_to_money(ob,val));
+  }
+  else
+     write(break_string("Ok, dann behalts!", 78,
+             Name(WER, 1)+" sagt: "));
+}
+
+static string sell_obj(object ob, int short)
+// Ist der Haendler bereit ob zu kaufen? wenn nein, Rueckgabe einer Meldung,
+// die der Haendler sagen soll.
+{  mixed nosell;
+
+   if (BLUE_NAME(ob)==GELD)
+      return "Das waere ja wohl Unsinn, oder ...?";
+   if (nosell=ob->QueryProp(P_NOSELL))
+   {
+     if (stringp(nosell))
+       return nosell;
+     return ("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!");
+   }
+   if (ob->QueryProp(P_CURSED))
+     return ob->Name(WER,1)
+         +" ist mir irgendwie ungeheuer! Das kannst Du nicht verkaufen!";
+   // man sollte keine COntainer mit Inhalt verkaufen koennen, ggf. kauft sie
+   // dann ein anderer Spieler.
+   if (first_inventory(ob))
+   {
+     return ob->Name(WER, 1) + " ist nicht leer!";
+   }
+   return 0;
+}
+
+static varargs int sell(string str, int f)
+{
+  int i, val, oval, tmp;
+  object *obs;
+
+  if (stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER),ME)) {
+     write("Es ist niemand da, der Dich bedienen koennte.\n");
+     return 1;
+  }
+  
+  if (!str) {
+     notify_fail("Was moechtest Du denn verkaufen?\n");
+     return 0;
+  }
+  
+  /* Ergebnis von find_obs() sollte unifiziert werden, damit ein mehrfach
+     gefundenes Objekt nicht mehrfach versucht wird zu verkaufen.
+     Beispiel: Objekt hat P_NOBUY gesetzt und mehrere IDs gesetzt. Wenn
+     ein Spieler es mit "verkaufe ID1 und ID2" versucht zu verkaufen,
+     wuerde das einen Bug ausloesen. Derselbe Bug entsteht, wenn man mit
+     "verkaufe ID1 und ID1" verkauft. */
+  obs = PL->find_obs(str, PUT_GET_DROP);
+  /* Erst im Inventar schauen, dann im Environment. find_obs() ohne 2.
+     Parameter macht das standardmaessig andersherum.
+     TODO: Aenderung ueberpruefen, sobald das neue put_and_get.c 
+     eingebaut wurde. */
+  if ( !sizeof(obs) )
+    obs = PL->find_obs(str, PUT_GET_TAKE) || ({});
+  obs = m_indices(mkmapping(obs));
+  if (!i=sizeof(obs)) {
+     notify_fail("Was moechtest Du denn verkaufen?\n");
+     return 0;
+  }
+  call_other(storage, "_register_shop", ME);
+  if (i==1) {
+     if (str=sell_obj(obs[0], 0)) {
+        write(break_string(str, 78, Name(WER, 1)+" sagt: "));
+        return 1;
+     }
+     if ((oval=obs[0]->QueryProp(P_VALUE))<=0) {
+        write(break_string(obs[0]->Name(WER)
+              +(obs[0]->QueryProp(P_PLURAL) ? " haben" : " hat")
+              +" keinen materiellen Wert, tut mir leid.", 78,
+              Name(WER, 1)+" sagt: "));
+        return 1;
+     }
+     val=QuerySellValue(obs[0], PL);
+     if (!val) {
+        write(break_string("Ich bin absolut pleite. Tut mir aufrichtig leid.",
+              78, Name(WER, 1)+" sagt: "));
+        return 1;
+     }
+     if (val==oval || f) {
+        give_money(make_to_money(obs[0], val));
+        return 1;
+     }
+     if (str=obs[0]->QueryProp(P_NODROP)) {
+        if (stringp(str))
+           write(str);
+        else write(break_string("Du kannst "+obs[0]->name(WEN,1)
+                               +" nicht verkaufen!", 78));
+        return 1;
+     }
+
+     if (obs[0]->QueryProp(P_DAMAGED))  // Bei beschaedigten Objekten gibt
+     {                                  // es auch hier eine andere Meldung
+       write(break_string("Da "+obs[0]->name(WER)+" beschaedigt "
+        +(obs[0]->QueryProp(P_PLURAL)?"sind":"ist")+", kann ich Dir "
+        "nur "+val+" Muenze"+(val == 1?"":"n")+" dafuer bieten. Und "
+        "damit mache ich noch Verlust! Nimmst Du mein Angebot an? "
+        "(ja/nein)",78,Name(WER,1)+" sagt: "));
+     }
+     else                              // Default
+     {
+       write(break_string(Name(WER, 1)+" sagt: "
+          +"Nach der aktuellen Marktlage kann ich Dir dafuer nur "
+          +val+" Muenze"+(val==1?"":"n")+" bieten, obwohl "
+          +obs[0]->name(WER)+" eigentlich "+oval+" Muenze"
+          +(oval==1?"":"n")+" wert waere. Willst Du "
+          +(QueryProp(P_PLURAL) ? "sie" : "es")
+          +" mir dafuer ueberlassen?", 78));
+     }
+     // in ask_sell() gibt es das query_verb() nicht mehr, weswegen U_REQ in
+     // Unitobjekten zurueckgesetzt wird. Damit geht die info verloren,
+     // wieviele Objekte der Spieler angegeben hat. Diese muss gerettet und
+     // via ask_sell() in make_to_money() ankommen. In normalen Objekten ist
+     // U_REQ 0.
+     input_to("ask_sell",INPUT_PROMPT, "(ja/nein) ",obs[0], val,
+              (obs[0])->QueryProp(U_REQ) );
+     return 1;
+  }
+  for (--i; i>=0 && get_eval_cost()>50000; i--) {
+     if (oval=obs[i]->QueryProp(P_VALUE)) {
+        if (obs[i]->QueryProp(P_KEEP_ON_SELL)==getuid(PL)
+            || obs[i]->QueryProp(P_WORN) || obs[i]->QueryProp(P_WIELDED))
+           write(break_string(obs[i]->Name(WER)+": Du behaeltst "
+                +obs[i]->name(WEN)+".", 78));
+        else if (str=sell_obj(obs[i], 1))
+           write(break_string(obs[i]->Name(WER)+": "+str, 78));
+        else {
+           tmp=QuerySellValue(obs[i], PL);
+           if (!tmp) {
+              write(break_string(
+                    "Ich bin absolut pleite. Tut mir aufrichtig leid.", 78,
+                    Name(WER, 1)+" sagt: "));
+              break;
+           }
+           else if (!f && tmp*10<oval)
+              write(break_string(obs[i]->Name(WER)+": "+Name(WER, 1)
+                    +" bietet Dir aber nur "+tmp+" Goldstueck"
+                    +(tmp>1 ? "e" : "")+" dafuer.", 78));
+           else {
+              str=(obs[i]->Name(WER));
+              if (tmp=make_to_money(obs[i], tmp)) {
+                 write(break_string(str+": "+Name(WER, 1)
+                      +" gibt Dir dafuer "+tmp+" Goldstueck"
+                      +(tmp==1?".":"e."), 78));
+                 val+=tmp;
+              }
+           }
+        }
+     }
+  }
+  if (!val)
+     write(break_string("Hmmm, Du hast aber nicht besonders viel zu bieten...",
+                        78, Name(WER)+" sagt: "));
+  else give_money(val);
+  return 1;
+}
+
+static int force_sell(string str)
+{  return sell(str, 1);  }
+
+static int evaluate(string str)
+{
+  object ob;
+  int val,rval;
+
+  if (!str) return 0;
+  if(stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER), ME)) {
+    write("Es ist niemand da, der Dich bedienen koennte.\n");
+    return 1;
+  }
+
+  ob=present(str, ME);
+  if (!ob) ob=deep_present(str,PL);
+  if (!ob) {
+    write("Hm? "+capitalize(str)+"? Wovon redest Du?\n");
+    return 1;
+  }
+  if (living(ob)) {
+    _notify_fail("Nanu, seit wann werden hier Lebewesen verkauft?\n");
+    return 0;
+  }
+  if (str=sell_obj(ob, 0)) {
+    write(break_string(str, 78, Name(WER)+" sagt: "));
+    return 1;
+  }
+  rval=ob->QueryProp(P_VALUE);
+  if (rval) {
+    val=QuerySellValue(ob, PL);
+    if (rval==val) {
+      tell_object(this_player(),break_string(
+         "Naja, ich denke, " +val+ " Muenze"
+         + (val==1 ? "" : "n")
+         + " waere"+(ob->QueryProp(P_AMOUNT)>1?"n ":" ")
+         + (ob->QueryPronoun(WER))+" schon wert.\n",78));
+    }
+    else if (val) {
+        tell_object(this_player(),break_string(
+          "Oh, nach der aktuellen Marktlage kann ich nur "+val+" Muenze"+
+          (val==1?"":"n")+" bezahlen, obwohl "
+          + (QueryProp(P_PLURAL) ? "sie" : "es")
+          + " eigentlich "+rval
+          + " Muenze"+(rval==1?"":"n")+" wert ist.\n",78));
+    }
+    else write("Ich bin vollkommen pleite. Tut mir leid.\n");
+  }
+  else write("Das ist voellig wertlos.\n");
+  return 1;
+}
+
+static int show_obj(string str)
+{
+  int i;
+  string was;
+  mixed ob;
+
+  if (!str) return 0;
+  if (sscanf(str,"%s im laden",was)>0 || sscanf(str,"%s in laden",was)>0) {
+    _notify_fail("Das kann ich im Lager nicht finden.\n");
+    ob=FindInStore(was);
+  } else if (sscanf(str,"%d",i)>0 && i>0) {
+    _notify_fail("Das kann ich im Lager nicht finden.\n");
+    ob=FindInStore(i);
+  }
+  if (!ob) return 0;
+  write(ob->Name(WER)+":\n"+ob->long()+capitalize(ob->QueryPronoun())
+       +" kostet "+QueryBuyValue(ob,PL)+" Muenzen.\n");
+  return 1;
+}
+
+// benutzt von trading_price::QueryValue(object, int, object)
+static int ObjectCount(object ob)
+{
+  string tmp;
+
+  if (!objectp(ob)) return 0;
+  tmp = BLUE_NAME(ob);
+  if (tmp[0..2]!="/d/" && tmp[0..8]!="/players/") tmp=ob->short()+tmp;
+  return ob_anz[tmp];
+}
+
+// benutzt von trading_price::QuerySellValue(object, object)
+static varargs int QueryValue(object ob, int value, object client)
+{
+  int new_value, mymoney;
+
+  if (!objectp(ob)) return 0;
+  if (Query("laden::compat")) {
+    new_value=(value>1000?1000:value);
+    mymoney = QueryProp(P_CURRENT_MONEY);
+    if (new_value>mymoney)
+      return (mymoney>0?mymoney:0);
+    else return new_value;
+  }
+  return ::QueryValue(ob, value, client);
+}
+
+void reset()
+{
+  mixed *keys;
+  int i;
+
+  trading_price::reset();
+
+  if (!mappingp(ob_anz))
+    return;
+  keys=m_indices(ob_anz);
+  for (i=sizeof(keys)-1;i>=0;i--) {
+    ob_anz[keys[i]]=ob_anz[keys[i]]*7/8;
+    if (!ob_anz[keys[i]])
+       m_delete(ob_anz,keys[i]);
+  }
+}
+
+varargs int CheckFindRestrictions(object ob, mixed restr, closure qp) {
+  return 0;
+}
+
+int EvalWeapon(object ob, closure qp) {
+  int wc,val;
+
+  wc=funcall(qp,P_WC);
+  val=funcall(qp,P_EFFECTIVE_WC);
+  if (val>wc) wc=val;
+  return wc;
+}
+
+varargs object FindBestWeapon(mixed type, int maxmon, int maxw, int hands,
+                              int bestwc, mixed restr) {
+  object bestob,ob;
+  string otype;
+  int wc,bestval,val,w,bestw;
+  closure qp;
+
+  if (!stringp(storage) || !objectp(ob=find_object(storage))) return 0;
+  if (!maxmon) maxmon=100000;
+  if (!maxw) maxw=75000;
+  if (!hands) hands=2;
+  if (val=QueryBuyFact()) maxmon=(maxmon*100)/val;
+  if (type && !pointerp(type) && !mappingp(type)) type=({type});
+
+  for (ob=first_inventory(ob);ob;ob=next_inventory(ob)) {
+    qp=symbol_function("QueryProp",ob);
+    if (!otype=funcall(qp,P_WEAPON_TYPE)) continue;
+    if (type && member(type,otype)<0) continue;
+    wc=EvalWeapon(ob,qp);
+    if (wc<bestwc) continue;
+    if (funcall(qp,P_NR_HANDS)>hands) continue;
+    w=funcall(qp,P_WEIGHT);
+    if (w>maxw) continue;
+    val=funcall(qp,P_VALUE);
+    if (val>maxmon) continue;
+    if (bestob && wc<=bestwc) {
+      if (val>bestval) continue;
+      else if (val==bestval && w>bestw) continue;
+    }
+    if (val>bestval && bestob && wc<=bestwc) continue;
+    if (CheckFindRestrictions(ob,restr,qp)) continue;
+    bestob=ob;
+    bestwc=wc;
+    bestval=val;
+    bestw=w;
+  }
+  return bestob;
+}
+
+int EvalArmour(object ob,closure qp) {
+  int ac,val;
+
+  ac=funcall(qp,P_AC);
+  val=funcall(qp,P_EFFECTIVE_AC);
+  if (val>ac) ac=val;
+  return ac;
+}
+
+varargs mapping FindBestArmoursT(mixed type, int maxmon, int maxw,
+                                 mapping bestac, mixed restr) {
+  object ob;
+  string otype;
+  int ac,val,sum,w,wsum;
+  mapping bestob,bestval,bestw;
+  closure qp;
+
+  if (!stringp(storage) || !objectp(ob=find_object(storage))) return ([]);
+  if (!maxmon) maxmon=100000;
+  if (!maxw) maxw=75000;
+  if (val=QueryBuyFact()) maxmon=(maxmon*100)/val;
+  if (type && !pointerp(type) && !mappingp(type)) type=({type});
+  if (!mappingp(bestac)) bestac=([]);
+  bestob=([]);bestval=([]);bestw=([]);
+
+  for (ob=first_inventory(ob);ob;ob=next_inventory(ob)) {
+    qp=symbol_function("QueryProp",ob);
+    if (!otype=funcall(qp,P_ARMOUR_TYPE)) continue;
+    if (type && member(type,otype)<0) continue;
+    ac=EvalArmour(ob,qp);
+    if (ac<bestac[otype]) continue;
+    w=funcall(qp,P_WEIGHT);
+    if (wsum-bestw[otype]+w>maxw) continue;
+    val=funcall(qp,P_VALUE);
+    if (sum-bestval[otype]+val>maxmon) continue;
+    if (bestob[otype] && ac<=bestac[otype]) {
+      if (val>bestval[otype]) continue;
+      else if (val==bestval[otype] && w>bestw[otype]) continue;
+    }
+    if (CheckFindRestrictions(ob,restr,qp)) continue;
+    sum=sum-bestval[otype]+val;
+    wsum=wsum-bestw[otype]+w;
+    bestob[otype]=ob;
+    bestac[otype]=ac;
+    bestval[otype]=val;
+    bestw[otype]=w;
+  }
+  return bestob;
+}
+
+varargs object *FindBestArmours(mixed type, int maxmon, int maxw,
+                                                mapping bestac, mixed restr) {
+  return m_values(FindBestArmoursT(type,maxmon,maxw,bestac,restr));
+}