Added public files

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