Added public files

Roughly added all public files. Probably missed some, though.
diff --git a/std/living/put_and_get.c b/std/living/put_and_get.c
new file mode 100644
index 0000000..4e1a97f
--- /dev/null
+++ b/std/living/put_and_get.c
@@ -0,0 +1,1243 @@
+// MorgenGrauen MUDlib
+//
+// living/put_and_get.c -- taking and putting things
+//
+// $Id: put_and_get.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+/*
+  Grundlegend neu strukturiert von Amynthor im April-Juni 2007
+
+Die eigentlichen Funktionen:
+
+  private string put_or_get(object o, object dest)
+    Bewegt ein einzelnes Objekt mit automatisch bestimmter Method. Gibt im
+    Erfolgsfall 0 zurueck, sonst die auszugebende Fehlermeldung.
+
+  varargs int drop(object o, mixed msg)
+  varargs int put(object o, object dest, mixed msg)
+  varargs int pick(object o, mixed msg)
+  varargs int give(object o, object dest, mixed msg)
+  varargs int show(object o, object dest, mixed msg)
+    Der Spieler nimmt/legt/gibt/zeigt/laesst ein Objekt fallen, wobei die
+    entsprechenden oder optional abweichende (im Format von P_XXX_MSG) oder
+    gar keine (msg == 1) Meldungen ausgegeben werden. Es liegt in der
+    Verantwortung des Rufenden, sinnvolle Werte zu uebergeben; insbesondere
+    wird nicht geprueft, ob sich die Objekte in der Reichweite des Spielers
+    befinden. Gibt 1 zurueck, wenn das Objekt bewegt wurde, sonst 0.
+
+Hilfsfunktionen:
+
+  private object *__find_objects(string *tokens, object env, int is_source)
+  object *find_objects(string what, object env, int is_source)
+    Sucht im Raum und im Spieler (oder alternativ in der angegebenen Umgebung)
+    nach den in tokens/what bezeichneten Objekten. is_source bestimmt die
+    erwartete grammatische Form (0 fuer "topf auf herd" und 1 fuer "topf von
+    herd", siehe Manpage).
+
+  varargs int drop_objects(string str, mixed msg)
+  varargs int put_objects(string str, int casus, string verb, mixed msg)
+  varargs int pick_objects(string str, mixed msg, int flag)
+  varargs int give_objects(string str, mixed msg)
+  varargs int show_objects(string str, mixed msg)
+    Ein Befehl wie "wirf waffen weg" resultiert in einem Aufruf von
+    drop_objects("waffen"). Diese Funktionen sind hauptsaechlich fuer die
+    Behandlung der jeweiligen Kommandos vorgesehen, koennen jedoch auch fuer
+    eigene Befehle verwendet werden. put_objects() erwartet ausserdem den
+    Kasus ("Du kannst nichts an DER Pinwand befestigen.") und das verwendete
+    Verb in der Gundform ("befestigen"). Das Flag fuer pick_objects() gibt
+    an, ob das Objekt auch einfach herumliegen darf ("nimm ...") oder nicht
+    ("hole ..."). Gibt bei Erfolg 1, sonst 0 zurueck.
+
+  object *moved_objects()
+  object moved_where()
+    Gibt die eben fallengelassenen/gesteckten/... Objekte zurueck und wohin
+    sie gesteckt/wem sie gegeben/gezeigt wurden. Fuer den Fall, dass man
+    anschliessend noch etwas mit ihnen machen moechte.
+
+Die einzelnen Kommandos:
+  static int fallenlassen(string str)
+  static int werfen(string str)
+  static int legen(string str)
+  static int stecken(string str)
+  static int holen(string str)
+  static int nehmen(string str)
+  static int geben(string str)
+    Minimale Wrapper fuer XXX_objects(), entfernen "fallen", "weg" bzw. "ab"
+    aus den Argumenten und setzen entsprechende Standard-Fehlermeldungen.
+    
+  protected void add_put_and_get_commands()
+    Registriert obige Funktionen per add_action().
+
+Aus reinen Kompatibilitaetsgruenden weiterhin enthalten:
+
+  object* find_obs(string str, int meth)
+  int pick_obj(object ob)
+  int drop_obj(object ob)
+  int put_obj(object ob, object where)
+  int give_obj(object ob, object where)
+    siehe Manpages
+
+*/
+
+/*
+  21. Okt 1998 komplette neu programmierung von put_and_get.c (Padreic)
+- die Gruppenauswahlen alles, waffen und ruestungen sind jetzt immer moeglich
+  die Gruppen sind sehr leicht erweiterbar und man sollte sich nicht scheuen
+  davon gebrauch zu machen...
+- mit "in mir" und "im raum" kann man den abzusuchenden Raum selbst eingrenzen
+- mit "alle|jede|jeden|jedes <id>" kann man auch ganze objektgruppen auswaehlen
+*/
+
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <language.h>
+#include <thing/description.h>
+#include <thing/properties.h>
+#include <moving.h>
+#include <container.h>
+#undef NEED_PROTOTYPES
+
+#include <defines.h>
+#include <properties.h>
+#include <wizlevels.h>
+
+#define TME(str) tell_object(this_object(), \
+    break_string(str, 78, 0, BS_LEAVE_MY_LFS))
+#define TOB(ob,str) tell_object(ob, break_string(str, 78, 0, BS_LEAVE_MY_LFS))
+#define SAY(str) tell_room(environment(), \
+    break_string(str, 78, 0, BS_LEAVE_MY_LFS), ({this_object()}))
+#define SAY2(obs, str) tell_room(environment(), \
+    break_string(str, 78, 0, BS_LEAVE_MY_LFS), ({this_object()}) + obs)
+#define NF(str) _notify_fail(break_string(str, 78, 0, BS_LEAVE_MY_LFS))
+
+private nosave closure cl;
+private nosave string wen0, wen1, wer0;
+private nosave object *last_moved_objects;
+private nosave object last_moved_where;
+
+
+/*********************** Die eigentlichen Funktionen ************************/
+
+private string put_or_get(object o, object dest)
+
+/* Bewegt ein einzelnes Objekt <o> in das Zielobjekt <dest>. Verwendet dazu
+ * je nach Umstaenden (Ziel ist der Spieler, die Umgebung, ein Lebewesen) den
+ * entsprechenden Wert fuer <method>. Gibt im Erfolgsfall 0 zurueck, erstellt
+ * sonst die auszugebende Fehlermeldung und gibt diese zurueck.
+ */
+
+{
+    int method, ret;
+    string str;
+
+    //if (living(o))
+    //    raise_error(sprintf("Lebendes Argument fuer put_or_get: %O\n", o));
+
+    if (dest == this_object())          /* pick */
+        method = M_GET;
+    else if (dest == environment())     /* drop */
+        method = M_PUT;
+    else if (living(dest))              /* give */
+        method = M_GIVE;
+    else {                              /* put */
+        method = M_PUT | M_GET;
+        if (first_inventory(o))
+            return o->Name(WER, 1) + " ist nicht leer!";
+    }
+
+    if ((ret = o->move(dest, method)) > 0)
+        return 0;
+
+    switch (ret) {
+        case ME_TOO_HEAVY:
+            if (dest == this_object())
+                if (QueryProp(P_GHOST))
+                    return "Als Geist kannst Du nichts mitnehmen.";
+                else
+                    return "Du kannst " + wen1 + " nicht mehr tragen.";
+
+            if (stringp(str = dest->QueryProp(P_TOO_HEAVY_MSG)))
+                return capitalize(replace_personal(str, ({o, dest}), 1));
+
+            if (living(dest)) {
+                if (dest->QueryProp(P_GHOST))
+                    return "Als Geist kann " + dest->name(WER, 1) +
+                           " nichts mitnehmen.";
+                else
+                    return dest->Name(WER, 1) + " kann " +
+                           wen0 + " nicht mehr tragen.";
+            }
+
+            if (dest == environment())
+                return (stringp(str = dest->Name(WER, 1)) && sizeof(str) ?
+                        str : "Der Raum") + " wuerde dann zu schwer werden.";
+
+            return capitalize(wer0 + " passt in " + dest->name(WEN, 1) +
+                              " nicht mehr rein.");
+
+        case ME_CANT_BE_DROPPED:
+            if (o && stringp(str = o->QueryProp(P_NODROP)))
+                return str;
+
+            if (dest == environment())
+                return "Du kannst " + wen1 + " nicht wegwerfen!";
+
+            if (living(dest))
+                return "Du kannst " + wen1 + " nicht weggeben.";
+
+            return "So wirst Du " + wen1 + " nicht los...";
+
+        case ME_CANT_BE_TAKEN:
+            if (o && stringp(str = o->QueryProp(P_NOGET)))
+                return str;
+
+            if (stringp(str = environment(o)->QueryProp(P_NOLEAVE_MSG)))
+                return capitalize(
+                    replace_personal(str, ({o, environment(o)}), 1));
+
+            //if (dest != environment())
+            //    return "Du kannst " + wen1 + " nicht einmal nehmen.";
+
+            return "Du kannst " + wen1 + " nicht nehmen.";
+
+        case ME_CANT_BE_INSERTED:
+            if (stringp(str = dest->QueryProp(P_NOINSERT_MSG)))
+                return capitalize(replace_personal(str, ({o, dest}), 1));
+
+            if (dest == environment())
+                return "Das darfst Du hier nicht ablegen.";
+
+            if (dest == this_object())
+                return "Du kannst " + wen1 + " nicht nehmen.";
+
+            if (living(dest))
+                return "Das kannst Du " + dest->name(WEM, 1) + " nicht geben.";
+
+            return capitalize(wen0 + " kannst Du dort nicht hineinstecken.");
+
+        case ME_CANT_LEAVE_ENV:
+            // ME_CANT_LEAVE_ENV kann nur auftreten, wenn o ein Environment
+            // hat, deshalb kein Check dadrauf
+            if (stringp(str = environment(o)->QueryProp(P_NOLEAVE_MSG)))
+                return capitalize(
+                    replace_personal(str, ({o, environment(o)}), 1));
+
+            if (environment(o) != this_object())
+                return "Du kannst " + wen1 + " nicht nehmen.";
+
+            if (dest == environment())
+                return "Du kannst " + wen1 + " nicht wegwerfen!";
+
+            if (living(dest))
+                return "Du kannst " + wen1 + " nicht weggeben.";
+
+            return "So wirst Du " + wen1 + " nicht los...";
+
+        case ME_TOO_HEAVY_FOR_ENV:
+            if (stringp(str = dest->QueryProp(P_ENV_TOO_HEAVY_MSG)))
+                return capitalize(replace_personal(str, ({o, dest}), 1));
+
+            if (environment(dest) == this_object())
+                return dest->Name(WER, 1) +
+                       " wuerde Dir dann zu schwer werden.";
+
+            return (stringp(str = environment(dest)->Name(WER, 1))
+                        && sizeof(str) ? str : "Der Raum") +
+                    " wuerde dann zu schwer werden.";
+
+        case TOO_MANY_OBJECTS:
+            if (stringp(str = dest->QueryProp(P_TOO_MANY_MSG)))
+                return capitalize(replace_personal(str, ({o, dest}), 1));
+
+            if (dest == this_object())
+                return "Soviele Gegenstaende kannst Du unmoeglich tragen!";
+
+            if (dest == environment())
+                return "Dafuer ist hier nicht mehr genug Platz.";
+
+            if (living(dest))
+                return dest->Name(WER, 1) + " kann "  + wen0 +
+                       " nicht mehr tragen.";
+
+            return "Dafuer ist nicht mehr genug Platz in " +
+                   dest->name(WEM, 1) + ".";
+
+        default:
+            if (dest == this_object())
+                return "Du kannst " + wen1 + " nicht nehmen.";
+
+            if (dest == environment())
+                return "Du kannst " + wen1 + " nicht wegwerfen!";
+
+            if (living(dest))
+                return "Du kannst " + wen1 + " nicht weggeben.";
+
+            return capitalize(wen0 + " kannst Du dort nicht hineinstecken.");
+    }
+    return 0; // NOT REACHED
+}
+
+
+/* varargs int drop(object o, mixed msg)
+ * varargs int put(object o, object dest, mixed msg)
+ * varargs int pick(object o, mixed msg)
+ * varargs int give(object o, object dest, mixed msg)
+ * varargs int show(object o, object dest, mixed msg)
+ *
+ * Der Spieler nimmt/legt/gibt/zeigt/laesst ein Objekt fallen, wobei die
+ * entsprechenden oder optional abweichende (im Format von P_XXX_MSG) oder
+ * gar keine (msg == 1) Meldungen ausgegeben werden. Es liegt in der
+ * Verantwortung des Rufenden, sinnvolle Werte zu uebergeben; insbesondere
+ * wird nicht geprueft, ob sich die Objekte in der Reichweite des Spielers
+ * befinden. Gibt 1 zurueck, wenn das Objekt bewegt wurde, sonst 0.
+ */
+
+varargs int drop(object o, mixed msg)
+{
+    string str;
+
+    // vorher speichern, falls das Objekt zerstoert wird
+    cl = symbol_function("name", o);
+    wen0 = funcall(cl, WEN, 0);
+    wen1 = funcall(cl, WEN, 1);
+    wer0 = 0;
+
+    if (!msg)
+        msg = o->QueryProp(P_DROP_MSG);
+
+    if (str = put_or_get(o, environment())) {
+        TME(str);
+        return 0;
+    }
+
+    if (!msg) {
+        TME("Du laesst " + wen1 + " fallen.");
+        SAY(Name(WER,1) + " laesst " + wen0 + " fallen.");
+    } else if (pointerp(msg))
+        switch (sizeof(msg)) {
+          // Wenn es zwei Strings gibt, geht die 2. ans Environment
+          case 2:
+            SAY(replace_personal(msg[1], ({this_object(), o||wen0}), 1));
+          case 1:
+            TME(replace_personal(msg[0], ({this_object(), o||wen1}), 1));
+            break;
+          default:
+            raise_error(sprintf(
+                "Falsches Format fuer P_DROP_MSG: %O\n", o||wen1));
+        }
+
+    return 1;
+}
+
+varargs int put(object o, object dest, mixed msg)
+{
+    string str;
+
+    // Falls das jemand von aussen ruft und Schrott uebergibt...
+    //if (living(dest))
+    //    raise_error(sprintf("Lebendes Ziel fuer put(): %O\n", dest));
+    if (dest == environment())
+        raise_error("Ziel fuer put() ist Umgebung des Spielers\n");
+
+    // vorher speichern, falls das Objekt zerstoert wird
+    cl = symbol_function("name", o);
+    wen0 = funcall(cl, WEN, 0);
+    wen1 = funcall(cl, WEN, 1);
+    wer0 = funcall(cl, WER, 0);
+
+    if (!msg)
+        msg = o->QueryProp(P_PUT_MSG);
+
+    if (str = put_or_get(o, dest)) {
+        TME(str);
+        return 0;
+    }
+
+  
+    if (!msg) {
+        TME("Du steckst " + wen1 + " in " + dest->name(WEN, 1) + ".");
+        if (environment())
+	  SAY(Name(WER, 1) + " steckt " + wen0 +
+	      " in " + dest->name(WEN, 0) + ".");
+    }
+    else if (pointerp(msg)) {
+        switch (sizeof(msg)) {
+          case 2:
+            if (environment())
+	      SAY(replace_personal(msg[1], ({this_object(), o||wen0, dest}), 1));
+          case 1:
+            TME(replace_personal(msg[0], ({this_object(), o||wen1, dest}), 1));
+            break;
+          default:
+            raise_error(sprintf(
+                "Falsches Format fuer P_PUT_MSG: %O\n",o||wen1));
+        }
+    }
+
+    return 1;
+}
+
+varargs int pick(object o, mixed msg)
+{
+    string str;
+
+    // vorher speichern, falls das Objekt zerstoert wird
+    cl = symbol_function("name", o);
+    wen0 = 0;
+    wen1 = funcall(cl, WEN, 1);
+    wer0 = 0;
+
+    if (!msg)
+        msg = o->QueryProp(P_PICK_MSG);
+
+    if (str = put_or_get(o, this_object())) {
+        TME(str);
+        return 0;
+    }
+
+    if (!msg) {
+        TME("Du nimmst " + wen1 + ".");
+        SAY(Name(WER, 1) + " nimmt " + wen1 + ".");
+    } else if (pointerp(msg))
+        switch (sizeof(msg)) {
+          case 2:
+            SAY(replace_personal(msg[1], ({this_object(), o||wen1}), 1));
+          case 1:
+            TME(replace_personal(msg[0], ({this_object(), o||wen1}), 1));
+            break;
+          default:
+            raise_error(sprintf(
+                "Falsches Format fuer P_PICK_MSG: %O\n", o||wen1));
+        }
+
+    return 1;
+}
+
+varargs int give(object o, object dest, mixed msg)
+{
+    string zname, gname;
+    string str;
+
+    // Falls das jemand von aussen ruft und Schrott uebergibt...
+    if (!living(dest))
+        raise_error(sprintf("Totes Ziel fuer give(): %O\n", dest));
+
+    zname = dest->name(WEM, 1);
+    gname = Name(WER, 1);
+
+    // vorher speichern, falls das Objekt zerstoert wird
+    cl = symbol_function("name", o);
+    wen0 = funcall(cl, WEN, 0);
+    wen1 = funcall(cl, WEN, 1);
+    wer0 = 0;
+
+    if (!msg)
+        msg = o->QueryProp(P_GIVE_MSG);
+
+    if (str = put_or_get(o, dest)) {
+        TME(str);
+        return 0;
+    }
+
+    if (!msg) {
+        TME("Du gibst " + zname + " " + wen1 + ".");
+        TOB(dest, gname + " gibt Dir " + wen0 + ".");
+        SAY2(({dest}), gname + " gibt " + zname + " " + wen0 + ".");
+    } else if (pointerp(msg))
+        switch (sizeof(msg)) {
+          case 3:
+            TOB(dest, replace_personal(
+                msg[2], ({this_object(), o||wen0, dest||zname}), 1));
+          case 2:
+            SAY2(({dest, this_object()}), replace_personal(
+                 msg[1], ({this_object(), o||wen0, dest||zname}), 1));
+          case 1:
+            TME(replace_personal(
+                msg[0], ({this_object(), o||wen1, dest||zname}), 1));
+            break;
+          default:
+            raise_error(sprintf(
+                "Falsches Format fuer P_GIVE_MSG: %O\n", o||wen1));
+        }
+
+    if (!query_once_interactive(dest))
+        dest->give_notify(o);
+
+    return 1;
+}
+
+varargs int show(object o, object whom, mixed msg)
+{
+    string zname, gname;
+    string wen0, wen2, long;
+
+    zname = whom ? whom->name(WEM, 1) : "allen";
+    gname = Name(WER, 1);
+
+    if (!msg)
+        msg = o->QueryProp(P_SHOW_MSG);
+
+    if (environment(o) == this_object() ||
+        environment(environment(o)) == this_object()) {
+        wen0 = o->name(WEN, 0);
+
+        /* Der Akkusativ muss mit dem unbestimmten Artikel gebildet werden,
+         * damit eventuelle Adjektive die richtige Endung besitzen.
+         * (ein kleines Schwert -> kleines Schwert -> Dein kleines Schwert)
+         */
+
+        if (o->QueryProp(P_ARTICLE) && member(wen0, ' ') >= 0) {
+            int obgender = o->QueryProp(P_GENDER);
+            int obnum = o->QueryProp(P_AMOUNT) > 1 ? PLURAL : SINGULAR;
+
+            // Wichtig: P_AMOUNT ist 0 fuer Objekte, die nicht von unit erben.
+            // Da unit.c kein P_AMOUNT==0 zulaesst, nehmen wir diesen Fall als
+            // singular an. *Rumata
+
+            wen2 = wen0[member(wen0, ' ')..];
+            wen0 = QueryPossPronoun(o, WEN, obnum) + wen2;
+
+            if (obnum == PLURAL || obgender == FEMALE)
+                wen2 = "Deine" + wen2;
+            else if (obgender == MALE)
+                wen2 = "Deinen" + wen2;
+            else
+                wen2 = "Dein" + wen2;
+        } else
+            wen2 = wen0;
+    } else
+        wen2 = wen0 = o->name(WEN, 1);
+
+    // vorher speichern, falls das Objekt im catch_tell() zerstoert wird
+    long = o->long(4);
+
+    if (!msg) {
+        TME("Du zeigst " + zname + " " + wen2 + ".");
+        if (!whom)
+            SAY(gname + " zeigt Dir " + wen0 + ".");
+        else {
+            TOB(whom, gname + " zeigt Dir " + wen0 + ".");
+            SAY2(({whom}), gname + " zeigt " + zname + " " + wen0 + ".");
+        }
+    } else if (pointerp(msg))
+        switch (sizeof(msg)) {
+          case 3:
+            if (whom)
+                TOB(whom, replace_personal(
+                    msg[2], ({this_object(), o||wen0, whom||zname}), 1));
+            else
+                SAY(replace_personal(
+                    msg[2], ({this_object(), o||wen0, whom||zname}), 1));
+          case 2:
+            if (whom)
+                SAY2(({whom, this_object()}), replace_personal(
+                     msg[1], ({this_object(), o||wen0, whom||zname}), 1));
+          case 1:
+            TME(replace_personal(
+                msg[0], ({this_object(), o||wen2, whom||zname}), 1));
+            break;
+          default:
+            raise_error(sprintf(
+                "Falsches Format fuer P_SHOW_MSG: %O\n", o||wen0));
+        }
+
+    if (!whom)
+        SAY(long);
+    else {
+        TOB(whom, long);
+        if (!query_once_interactive(whom))
+            whom->show_notify(o);
+    }
+
+    return 1;
+}
+
+
+/***************************** Hilfsfunktionen *****************************/
+
+/* private object *__find_objects(string *tokens, object env, int is_source);
+ * object *find_objects(string what, object env, int is_source);
+ *
+ * Sucht im Raum und im Spieler (oder alternativ in der angegebenen Umgebung)
+ * nach den in tokens/what bezeichneten Objekten. is_source bestimmt die
+ * erwartete grammatische Form (0 fuer "topf auf herd" und 1 fuer "topf von
+ * herd", siehe Manpage).
+ */
+ 
+private object *__find_objects(string *tokens, object env, int is_source)
+{
+    object ob, *obs;
+
+    // is_source == 0: Objekt soll nicht bewegt werden ("topf auf herd")
+    //              1: Objekt soll bewegt werden ("topf von herd")
+    //              2: intern
+
+    if (!env && sizeof(tokens) > 1 && tokens[<1] == "hier") {
+        tokens = tokens[..<2];
+        env = environment();
+    }
+    else if (!env && sizeof(tokens) > 2 && tokens[<2] == "in")
+    {
+        if (tokens[<1] == "mir" ||
+            tokens[<1] == "dir") {
+            tokens = tokens[..<3];
+            env = this_object();
+        }
+        else if (tokens[<1] == "raum") {
+            tokens = tokens[..<3];
+            env = environment();
+        }
+    }
+
+    for (int i = sizeof(tokens)-1; i > 1; i--) {
+        if (env)
+            ob = present(implode(tokens[i..], " "), env);
+        else
+            ob = present(implode(tokens[i..], " "), environment()) ||
+                 present(implode(tokens[i..], " "), this_object());
+
+        if (!ob)
+            continue;
+
+        if (living(ob)) {
+            NF("Aber " + ob->name(WER, 1) + " lebt doch!");
+            continue;
+        }
+
+        if (ob->QueryProp(P_CNT_STATUS)) {
+            NF("Aber " + ob->name(WER, 1) + " ist doch geschlossen!");
+            continue;
+        }
+
+        if (is_source != 0 &&
+            tokens[i-1] == ob->QueryProp(P_SOURCE_PREPOSITION))
+            return ob->present_objects(implode(tokens[..i-2], " "));
+
+        if (tokens[i-1] == ob->QueryProp(P_PREPOSITION))
+            return __find_objects(tokens[..i-2], ob, is_source ? 2 : 0);
+
+        NF("Du kannst nichts " + tokens[i-1] + " " +
+           ob->name(WEM, 1) + " nehmen.");
+    }
+
+    if (is_source == 2)
+        return ({});
+
+    if (env)
+        return env->present_objects(implode(tokens, " "));
+
+    if (environment() &&
+        sizeof(obs = environment()->present_objects(implode(tokens, " "))))
+        return obs;
+
+    return present_objects(implode(tokens, " "));
+}
+
+object *find_objects(string what, object env, int is_source)
+{
+  if (!stringp(what) || !sizeof(what))
+    return ({});
+  return __find_objects(explode(what, " "), env, is_source);
+}
+
+
+/* varargs int drop_objects(string str, mixed msg);
+ * varargs int put_objects(string str, int casus, string verb, mixed msg);
+ * varargs int pick_objects(string str, int flag, mixed msg);
+ * varargs int give_objects(string str, mixed msg);
+ * varargs int show_objects(string str, mixed msg);
+ *
+ * Ein Befehl wie "wirf waffen weg" resultiert in einem Aufruf von
+ * drop_objects("waffen"). Diese Funktionen sind hauptsaechlich fuer die
+ * Behandlung der jeweiligen Kommandos vorgesehen, koennen jedoch auch fuer
+ * eigene Befehle verwendet werden. put_objects() erwartet ausserdem den
+ * Kasus ("Du kannst nichts an DER Pinwand befestigen.") und das verwendete
+ * Verb in der Gundform ("befestigen"). Das Flag fuer pick_objects() gibt
+ * an, ob das Objekt auch einfach herumliegen darf ("nimm ...") oder nicht
+ * ("hole ..."). Gibt bei Erfolg 1, sonst 0 zurueck.
+ */
+
+varargs int drop_objects(string str, mixed msg)
+{
+    object *obs;
+
+    if (!sizeof(obs = find_objects(str, this_object(), 1)))
+        return 0;
+
+    foreach (object o: obs) {
+        if (objectp(o))
+            drop(o, msg);
+
+        if (get_eval_cost() < 100000) {
+            TME("Den Rest behaeltst Du erst mal.");
+            last_moved_objects = obs[..member(obs, o)];
+            last_moved_where = 0;
+            return 1;
+        }
+    }
+
+    last_moved_objects = obs;
+    last_moved_where = 0;
+    return 1;
+}
+
+varargs int put_objects(string str, int casus, string verb, mixed msg)
+{
+    object *obs, dest, *no_move;
+    
+    if (!stringp(str) || !sizeof(str)) return 0;
+
+    string *tokens = explode(str, " ");
+    int allow_room = 1;
+    int allow_me = 1;
+
+    if (sizeof(tokens) > 1 && tokens[<1] == "hier") {
+        tokens = tokens[..<2];
+        allow_me = 0;
+    } else if (sizeof(tokens) > 2 && tokens[<2] == "in")
+        if (tokens[<1] == "mir" ||
+            tokens[<1] == "dir") {
+            tokens = tokens[..<3];
+            allow_room = 0;
+        } else if (tokens[<1] == "raum") {
+            tokens = tokens[..<3];
+            allow_me = 0;
+        }
+
+    for (int i = sizeof(tokens)-1; i > 1; i--) {
+        if (!(dest = allow_room && present(implode(tokens[i..], " "),
+                                           environment())) &&
+            !(dest = allow_me && present(implode(tokens[i..], " "),
+                                         this_object())))
+            continue;
+
+        if (living(dest)) {
+            NF("Aber " + dest->name(WER, 1) + " lebt doch!");
+            continue;
+        }
+/*
+        if (verb == "legen" && !dest->QueryProp(P_TRAY)) {
+            NF("Du kannst nichts auf " + dest->name(WEN, 1) + " legen.");
+            continue;
+        }
+*/
+        if (verb == "stecken" && !dest->QueryProp(P_CONTAINER)) {
+            NF("Du kannst in " + dest->name(WEN, 1) + " nichts reinstecken.");
+            continue;
+        }
+
+        if (dest->QueryProp(P_CNT_STATUS)) {
+            NF("Aber " + dest->name(WER, 1) + " ist doch geschlossen!");
+            continue;
+        }
+
+        if (tokens[i-1] != dest->QueryProp(P_DEST_PREPOSITION)) {
+            NF("Du kannst nichts " + tokens[i-1] + " " +
+               dest->name(casus, 1) + " " + verb + ".");
+            continue;
+        }
+
+        if (!sizeof(obs = __find_objects(tokens[..i-2], 0, 1) - ({ dest }))) {
+            NF("WAS moechtest Du " + tokens[i-1] + " " +
+               dest->name(casus, 1) + " " + verb + "?");
+            return 0;
+        }
+
+        if (sizeof(no_move = obs & all_inventory(dest))) {
+            TME(capitalize(CountUp(map_objects(no_move, "name", WER, 1))) +
+                (sizeof(no_move) == 1 ? " ist" : " sind") +
+                " doch bereits in " + dest->name(WEM,1) + ".");
+            if (!sizeof(obs -= no_move))
+                return 0;
+        }
+
+        foreach (object o: obs) {
+            if (objectp(o))
+                put(o, dest, msg);
+
+            if (get_eval_cost() < 100000) {
+                TME("Den Rest laesst Du erst mal, wo er ist.");
+                last_moved_objects = obs[..member(obs, o)];
+                last_moved_where = dest;
+                return 1;
+            }
+        }
+
+        last_moved_objects = obs;
+        last_moved_where = dest;
+        return 1;
+    }
+    
+    return 0;
+}
+
+varargs int pick_objects(string str, int flag, mixed msg)
+{
+    object *obs;
+
+    if (((int)QueryProp(P_MAX_HANDS)) < 1){
+        NF("Ohne Haende kannst Du nichts nehmen.");
+        return 0;
+    }
+
+    if (!sizeof(obs = find_objects(str, 0, 1) - all_inventory()
+	  - (flag ? all_inventory(environment()) : ({}))))
+        return 0;
+
+    foreach (object o: obs) {
+        if (objectp(o))
+            pick(o, msg);
+
+        if (get_eval_cost() < 100000) {
+            TME("Den Rest laesst Du erst mal liegen.");
+            last_moved_objects = obs[..member(obs, o)];
+            last_moved_where = 0;
+            return 1;
+        }
+    }
+
+    last_moved_objects = obs;
+    last_moved_where = 0;
+    return 1;
+}
+
+varargs int give_objects(string str, mixed msg)
+{
+    object *obs, dest;
+    
+    if (!stringp(str) || !sizeof(str)) return 0;
+
+    string *tokens = explode(str, " ");
+
+    if (((int)QueryProp(P_MAX_HANDS)) < 1){
+        NF("Ohne Haende kannst Du nichts weggeben.");
+        return 0;
+    }
+
+    for (int i = 0; i < sizeof(tokens)-1; i++) {
+        if (!(dest = present(implode(tokens[..i], " "), environment())))
+            continue;
+
+        if (!living(dest)) {
+            NF("Aber " + dest->name(WER, 1) + " lebt doch gar nicht!");
+            dest = 0;
+            continue;
+        }
+
+        if (!sizeof(obs = __find_objects(tokens[i+1..], 0, 1))) {
+            NF("WAS moechtest Du " + dest->name(WEM, 1)+" geben?");
+            dest = 0;
+        } else
+            break;
+    }
+
+    if (!dest) {
+        int pos;
+
+        if ((pos = strrstr(str, " an ")) >= 0) {
+            dest = present(str[pos+4..], environment());
+            // zu gebende Objekte in Env + Living suchen
+            obs = find_objects(str[..pos-1], 0, 1);
+        }
+    }
+
+    if (!dest || !living(dest) || !sizeof(obs))
+        return 0;
+
+    foreach (object o: obs) {
+        if (objectp(o))
+            give(o, dest, msg);
+
+        if (get_eval_cost() < 100000) {
+            TME("Den Rest behaeltst Du erst mal.");
+            last_moved_objects = obs[..member(obs, o)];
+            last_moved_where = dest;
+            return 1;
+        }
+    }
+
+    last_moved_objects = obs;
+    last_moved_where = dest;
+    return 1;
+}
+
+varargs int show_objects(string str, mixed msg)
+{
+    object *obs, whom;
+    
+    if (!stringp(str) || !sizeof(str))
+      return 0;
+
+    string *tokens = explode(str, " ");
+
+    for (int i = 0; i < sizeof(tokens)-1; i++) {
+        if (whom = present(implode(tokens[..i], " "), environment())) {
+            if (!living(whom)) {
+                NF("Aber " + whom->name(WER, 1) + " lebt doch gar nicht!");
+                continue;
+            }
+        } else {
+            if (i != 0 || tokens[0] != "allen")
+                continue;
+
+            if (!sizeof(filter(all_inventory(environment()) -
+                    ({ this_object() }), #'living))) {
+                NF("Hier ist niemand, dem Du etwas zeigen koenntest!");
+                continue;
+            }
+        }
+
+        if (whom == this_object()) {
+            NF("Dazu solltest Du dann besser 'schau' benutzen!\n");
+            continue;
+        }
+
+        if (!sizeof(obs = __find_objects(tokens[i+1..], this_object(), 0)) &&
+            !sizeof(obs = __find_objects(tokens[i+1..], 0, 0)
+                            - ({ this_object(), whom }))) {
+            NF("WAS moechtest Du " + (whom ? whom->name(WEM, 1) : "allen") +
+               " zeigen?");
+            continue;
+        }
+
+        foreach (object o: obs) {
+            if (objectp(o))
+                show(o, whom, msg);
+
+            if (get_eval_cost() < 100000) {
+                TME("Das reicht erst mal.");
+                last_moved_objects = obs[..member(obs, o)];
+                last_moved_where = whom;
+                return 1;
+            }
+        }
+
+        last_moved_objects = obs;
+        last_moved_where = whom;
+        return 1;
+    }
+
+    return 0;
+}
+
+object *moved_objects(void)
+{
+    return last_moved_objects;
+}
+
+object moved_where(void)
+{
+    return last_moved_where;
+}
+
+
+/************************* Die einzelnen Kommandos **************************/
+
+/* static int fallenlassen(string str)
+ * static int werfen(string str)
+ * static int legen(string str)
+ * static int stecken(string str)
+ * static int holen(string str)
+ * static int nehmen(string str)
+ * static int geben(string str)
+ *   Minimale Wrapper fuer XXX_objects(), entfernen "fallen", "weg" bzw. "ab"
+ *   aus den Argumenten und setzen entsprechende Standard-Fehlermeldungen.
+ *   
+ * protected void add_put_and_get_commands()
+ *   Registriert obige Funktionen per add_action().
+ */
+
+static int fallenlassen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts fallenlassen.\n");
+        return 0;
+    }
+
+    if (!str || str[<7..] != " fallen") {
+        _notify_fail("Lass etwas FALLEN, oder was meinst Du?\n");
+        return 0;
+    }
+
+    _notify_fail("WAS moechtest Du fallenlassen?\n");
+    return drop_objects(str[0..<8]);
+}
+
+static int werfen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts wegwerfen.\n");
+        return 0;
+    }
+
+    if (!str || str[<4..] != " weg") {
+        _notify_fail("Wirf etwas WEG, oder was meinst Du?\n");
+        return 0;
+    }
+
+    _notify_fail("WAS moechtest Du loswerden?\n");
+    return drop_objects(str[0..<5]);
+}
+
+static int legen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts weglegen.\n");
+        return 0;
+    }
+
+    if (!str) {
+        _notify_fail("Lege etwas AB, oder was meinst Du?\n");
+        return 0;
+    }
+
+    if (str[<3..] == " ab") {
+        _notify_fail("WAS moechtest Du ablegen?\n");
+        return drop_objects(str[0..<4]);
+    }
+
+    if (str[<4..] == " weg") {
+        _notify_fail("WAS moechtest Du weglegen?\n");
+        return drop_objects(str[0..<5]);
+    }
+
+    _notify_fail("WAS moechtest Du WOHIN legen?\n");
+    return put_objects(str, WEN, "legen");
+}
+
+static int stecken(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Das kannst Du in Deinem immateriellen Zustand nicht.\n");
+        return 0;
+    }
+
+    _notify_fail("WAS moechtest Du WOHIN stecken?\n");
+    return put_objects(str, WEN, "stecken");
+}
+
+static int holen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts nehmen.\n");
+        return 0;
+    }
+
+    _notify_fail("WAS moechtest Du aus WAS holen?\n");
+    return pick_objects(str, 1);
+}
+
+static int nehmen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts nehmen.\n");
+        return 0;
+    }
+
+    _notify_fail("WAS moechtest Du nehmen?\n");
+    return pick_objects(str, 0);
+}
+
+static int geben(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du nichts weggeben.\n");
+        return 0;
+    }
+
+    _notify_fail("WEM moechtest Du WAS geben?\n");
+    return give_objects(str);
+}
+
+static int zeigen(string str)
+{
+    if (QueryProp(P_GHOST)) {
+        _notify_fail("Als Geist kannst Du niemandem etwas zeigen.\n");
+        return 0;
+    }
+
+    _notify_fail("WEM moechtest Du WAS zeigen?\n");
+    return show_objects(str);
+}
+
+protected void add_put_and_get_commands(void)
+{
+    add_action("fallenlassen", "lass");
+    add_action("fallenlassen", "lasse");
+    add_action("werfen",       "wirf");
+    add_action("werfen",       "werf");
+    add_action("werfen",       "werfe");
+    add_action("legen",        "leg");
+    add_action("legen",        "lege");
+    add_action("stecken",      "steck");
+    add_action("stecken",      "stecke");
+    add_action("holen",        "hol");
+    add_action("holen",        "hole");
+    add_action("nehmen",       "nimm");
+    add_action("nehmen",       "nehm");
+    add_action("nehmen",       "nehme");
+    add_action("geben",        "gebe");
+    add_action("geben",        "gib");
+    add_action("zeigen",       "zeig");
+    add_action("zeigen",       "zeige");
+}
+
+
+/********** Aus reinen Kompatibilitaetsgruenden weiterhin enthalten *********/
+
+object* find_obs(string str, int meth)
+// gibt ein array zurueck mit allen Objekten die mit str angesprochen werden
+{
+   object inv;
+   if (!str) return 0;
+   if (str[<7..]==" in mir") {
+     inv=ME;
+     str=str[0..<8];
+   }
+   else if (str[<8..]==" in raum") {
+     if (meth & PUT_GET_DROP) { // man kann nichts aus dem Raum wegwerfen
+       _notify_fail("Du kannst nichts wegwerfen, das Du gar nicht hast.\n");
+       return 0;
+     }
+     inv=environment();
+     str=str[0..<9];
+   }
+   else if (meth & PUT_GET_DROP) inv=ME; // Raum bei drop uninteressant
+   // else kein besonderes inv ausgewaehlt also inv=0
+   if (!sizeof(str))
+     return 0; // hier passt die bereits gesetzte _notify_fail
+   else {
+     object *obs;
+     string con;
+     if (sscanf(str, "%s aus %s", str, con)==2 ||
+         sscanf(str, "%s in %s", str, con)==2 ||
+         sscanf(str, "%s von %s", str, con)==2 ||
+         sscanf(str, "%s vom %s", str, con)==2) {
+       if (!inv) {
+         if (!environment() || !(inv=present(con, environment())))
+            inv=present(con, ME); // sowohl im env als auch im inv suchen
+       }
+       else inv=present(con, inv); // nur in ausgewaehltem inv suchen
+       if (inv==ME) inv=0;
+       if (!inv || !(inv->short())) {
+         _notify_fail(break_string("Du hast hier aber kein '"+capitalize(con)
+                                 +"'.",78));
+         return 0;
+       }
+       if (living(inv)) {
+         _notify_fail(break_string("Aber "+inv->name(WER,1)+" lebt doch!",78));
+         return 0;
+       }
+       // wieso man aus Objekten die von std/tray abgeleitet werden etwas
+       // nehmen koennen soll, versteh ich zwar nicht so ganz...
+       if (!(inv->QueryProp(P_CONTAINER)) && !(inv->QueryProp(P_TRAY))) {
+         _notify_fail(break_string("Du kannst nichts aus "+inv->name(WEM,1)
+                                 +" nehmen.",78));
+         return 0;
+       }
+       if (inv->QueryProp(P_CNT_STATUS)) { // Container ist geschlossen
+         _notify_fail(break_string("Aber "+inv->name(WER,1)
+                                 +" ist doch geschlossen.", 78));
+         return 0;
+       }
+     }
+     else if (inv==ME && (meth & PUT_GET_TAKE)) { // nichts aus sich nehmen
+       _notify_fail("Du kannst nichts nehmen, "
+                    "was Du schon bei Dir traegst.\n");
+       return 0;
+     }
+     if (!inv && (meth & PUT_GET_TAKE))
+       inv=environment(); // nichts nehmen was man schon hat
+
+     if (!inv) {
+       if (environment()) {
+         obs=(environment()->present_objects(str)||({}));
+         if (!sizeof(obs)) obs+=(ME->present_objects(str)||({}));
+       }
+       else obs=(ME->present_objects(str) || ({}));
+     }
+     else obs=(inv->present_objects(str) || ({}));
+     return obs-({ ME });
+   }
+   return(0);
+}
+
+int pick_obj(object ob)
+{
+  object env;
+
+  if (!ob || ob == this_object() || environment(ob) == this_object()) return 0;
+  if ((env=environment(ob)) != environment()) {
+    if (!env->QueryProp(P_CONTAINER) && !env->QueryProp(P_TRAY)) {
+      TME("Du kannst nichts aus " + env->name(WEM,1) + " nehmen.");
+      return 1;
+    }
+    else if (env->QueryProp(P_CNT_STATUS)) {  // Container ist geschlossen
+      TME("Aber " + env->name(WER, 1) + " ist doch geschlossen.");
+      return 1;
+    }
+  }
+  if (ob->IsUnit() && ob->QueryProp(P_AMOUNT)<0) {
+    TME("Du kannst nicht mehr nehmen als da ist.");
+    return 1;
+  }
+  pick(ob);
+  return 1;
+}
+
+int drop_obj(object ob)
+{
+  if (!ob || ob==this_object() || environment(ob)!=this_object()) return 0;
+  drop(ob);
+  return 1;
+}
+
+int put_obj(object ob, object where)
+{
+  object env;
+
+  if (ob == this_object() || ob == where || environment(ob) == where) return 0;
+  env=environment(ob);
+  if (!where->QueryProp(P_CONTAINER)) {
+    TME("Du kannst in " + where->name(WEN,1) + " nix reinstecken.");
+    return 1;
+  }
+  if (where->QueryProp(P_CNT_STATUS)) {  // Container ist geschlossen
+    TME("Aber " + where->name(WER, 1) + " ist doch geschlossen.");
+    return 1;
+  }
+  if (env!=environment(this_object()) && env!=this_object()) {
+    _notify_fail("Da kommst du so nicht ran.\n");
+    return 0;
+  }
+  put(ob, where);
+  return 1;
+}
+
+int give_obj(object ob, object where)
+{
+  object env;
+
+  if (environment(ob)!=this_object()) {
+    TME("Das solltest Du erstmal nehmen.");
+    return 1;
+  }
+  if (!ob || ob == this_object() || ob == where ||
+      environment(where)!=environment())
+    return 0;
+  if (environment(ob) == where) {
+    _notify_fail("Das Ziel ist in dem zu gebenden Object enthalten!\n");
+    return 0;
+  }
+  if (environment(ob)!=this_object()) {
+    TME("Das hast Du nicht.");
+    return 1;
+  }
+  give(ob, where);
+  return 1;
+}