Added public files

Roughly added all public files. Probably missed some, though.
diff --git a/std/.readme b/std/.readme
new file mode 100644
index 0000000..089c803
--- /dev/null
+++ b/std/.readme
@@ -0,0 +1,24 @@
+Hier findest du vor allem abstrakte Basisklassen fuer alle Objekte.
+
+armour[.c]      - die Basisklasse fuer Ruestungen
+clothing[.c]    - die Basisklasse fuer (Be)Kleidung
+container[.c]   - container-Basisobjekt
+corpse.c        - die Leiche von Spielern und NPCs
+gilde.c         - Basisklasse fuer die Filialen der ADV-Gilde
+inpc[.c]        - Basisklasse fuer intelligenten NPC [IN ENTWICKLUNG]
+laden.c         - Basisklasse fuer den Laden
+living          - Basisklassen fuer Lebewesen
+npc[.c]         - Basisklasse fuer Non-Player-Characters
+player          - Basis fuer Spielerobjekte, siehe shells
+pub.c           - Basisklasse fuer Kneipen
+room[.c]        - davon leiten sich alle Raeume ab
+shells          - da stehen die Player-Objekte drin
+store.c         - Basisklasse fuer Lagerraeume - passt zu Laden.c
+thing[.c]       - Basisklasse fuer ALLES Objekte
+unit.c          - Klasse fuer Mehrfach-Objekte wie das Geld
+weapon[.c]      - Basisklasse fuer Waffen
+
+Bitte Objekt hier nicht mit 'upd -l', 'xload' o.ae. laden! Zum "Neuladen" nur
+die Blueprint destructen (z.B. per upd), sie werden automatisch bei Bedarf
+neugeladen.
+
diff --git a/std/armour.c b/std/armour.c
new file mode 100644
index 0000000..e0f4b8f
--- /dev/null
+++ b/std/armour.c
@@ -0,0 +1,53 @@
+// MorgenGrauen MUDlib
+//
+// armour.c -- armour standard object
+//
+// $Id: armour.c 7804 2011-07-10 20:37:52Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma range_check
+#pragma pedantic
+
+inherit "/std/thing/properties";
+inherit "/std/thing/commands";
+inherit "/std/thing/restrictions";
+inherit "/std/thing/light";
+inherit "/std/armour/description";
+inherit "/std/clothing/moving";
+inherit "/std/armour/wear";
+inherit "/std/armour/combat";
+inherit "/std/thing/language";
+inherit "/std/thing/envchk";
+
+//#define NEED_PROTOTYPES
+
+#include <config.h>
+#include <properties.h>
+#include <language.h>
+#include <combat.h>
+#include <wizlevels.h>
+#include <defines.h>
+
+protected void create() {
+  properties::create();
+  commands::create();
+  light::create();
+  restrictions::create();
+  description::create();
+  wear::create();
+  combat::create();
+  envchk::create();
+  AddId("Ding");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// Zum Ueberschreiben, damit es nicht buggt, wenn die Leute in ihren Objekten (wie
+// gewuenscht) ::reset() rufen.
+void reset() {
+}
+
diff --git a/std/armour/combat.c b/std/armour/combat.c
new file mode 100644
index 0000000..e688131
--- /dev/null
+++ b/std/armour/combat.c
@@ -0,0 +1,228 @@
+// MorgenGrauen MUDlib
+//
+// armour/combat.c -- armour standard object
+//
+// $Id: combat.c 9092 2015-01-19 23:57:50Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <thing/commands.h>
+#include <thing/description.h>
+#include <config.h>
+#include <combat.h>
+#include <language.h>
+#include <defines.h>
+#include <new_skills.h>
+
+
+// Globale Variablen
+private nosave closure defend_cl;
+private nosave mapping resistance_strengths=([]);
+
+void create() {
+    // Einige Grundwerte setzen
+    Set(P_ARMOUR_TYPE, AT_ILLEGAL, F_VALUE);
+    Set(P_LAST_USE,time(),F_VALUE);
+}
+
+// Die Funktion, die den Schutzwert berechnet, den die Ruestung bietet
+int QueryDefend(string|string* dam_type, int|mapping spell, object enemy)
+{
+    int     prot;
+    mixed   def_func;
+    object  pl;
+    // Zum Cachen von spell[EINFO_DEFEND], haeufiges Lookup aus dem Mapping
+    // koennte unnoetig Zeit kosten.
+    mapping einfo;
+
+    // AT_MISC-Ruetungen schuetzen normalerweise gar nicht...
+    if (QueryProp(P_ARMOUR_TYPE)==AT_MISC) {
+        // es sei denn, sie haben eine spezielle DefendFunc
+        if (!closurep(defend_cl)) return(0);
+    }
+    else {
+        // ansonsten Ruestungsschutz ermitteln und in EINFO_DEFEND vermerken.
+        // (Beides fuer AT_MISC in jedem Fall unnoetig)
+
+        // Ruestungen schuetzen nur gegen physikalischen Schaden
+        if ((!spell || (mappingp(spell) && spell[SP_PHYSICAL_ATTACK]))
+            && sizeof(filter(dam_type,PHYSICAL_DAMAGE_TYPES)))
+        { 
+          // Schutz bestimmen, Minimum 1, aber nur wenn P_AC > 0
+          int pac = QueryProp(P_AC);
+          if (pac > 0)
+            prot = (pac/4 + random(pac*3/4 + 1)) || 1 ;
+          object stat = find_object("/d/erzmagier/zesstra/pacstat");
+          if (stat)
+            stat->acstat(QueryProp(P_ARMOUR_TYPE),prot,
+                         random(pac)+1);
+
+          // ruestungschutz an defendfunc weitermelden
+          if (mappingp(spell) &&
+              mappingp(einfo=spell[EINFO_DEFEND])) {
+              // Schutz d. akt. Ruestung vermerken.
+              einfo[DEFEND_CUR_ARMOUR_PROT]=prot;
+              // daten der Ruestung vermerken.
+              if (mappingp(einfo[DEFEND_ARMOURS])) {
+                einfo[DEFEND_ARMOURS][ME,DEF_ARMOUR_PROT]=prot;
+              }
+          } // ende vom if (mapping(spell) ...)
+        } // ende vom if (phys Schaden)
+
+    } // ende vom else (wenn kein AT_MISC) 
+
+    // Ist eine DefendFunc gesetzt, wird diese ausgewertet
+    if (closurep(defend_cl)) {
+        if (!objectp(get_type_info(defend_cl,2))) {
+            // Closure gesetzt, aber nicht gueltig, schauen, ob wir sie neu
+            // erstellen koennen.
+            if (objectp(def_func=QueryProp(P_DEFEND_FUNC))) {
+                defend_cl=symbol_function("DefendFunc",def_func);
+            }
+            // sonst loeschen, um spaeter diesen Zweig ganz zu sparen.
+            else defend_cl=0;
+        }
+        // BTW: Es ist ok, wenn defend_cl jetzt 0 ist, dann liefert funcall()
+        // auch 0.
+        // Bei Netztoten keine (zurueckschlagende) DefendFunc
+        if (objectp(pl=QueryProp(P_WORN)) && (!query_once_interactive(pl) ||
+                interactive(pl)) ) {
+          // Der Rueckgabewert der DefendFunc wird zum Schutz addiert
+          prot += funcall(defend_cl, dam_type, spell, enemy);
+          // leider kann die DefendFunc jetzt auch noch das Objekt zerstoert
+          // haben...
+          if (!objectp(this_object()))
+            return prot;
+        }
+    }
+
+    // Zeitpunkt der letzten Benutzung ausgeben
+    SetProp(P_LAST_USE,time());
+
+    // Berechneten Schutz zurueckgeben
+    return prot;
+}
+
+// Es duerfen nur "legale" Ruestungstypen gesetzt werden, ansonsten
+// wird AT_ILLEGAL gesetzt.
+static mixed _set_armour_type(mixed type ) {
+    if (!COMBAT_MASTER->valid_armour_type(type))
+    {
+        Set(P_ARMOUR_TYPE, (type=AT_ILLEGAL), F_VALUE);
+    }
+    else
+    {
+        Set(P_ARMOUR_TYPE, type);
+    }
+    AddId(type);
+
+    resistance_strengths=([]);
+    return type;
+}
+
+
+// Wird etwas an P_DEFEND_FUNC geaendert, muss die zugehoerige closure
+// neu erstellt werden.
+static object _set_defend_func(object arg) {
+  if (objectp(arg) &&
+      closurep(defend_cl=symbol_function("DefendFunc",arg))) {  
+    return Set(P_DEFEND_FUNC, arg, F_VALUE);
+  }
+  defend_cl=0;
+  return(Set(P_DEFEND_FUNC, 0, F_VALUE));
+}
+
+// Auch Ruestungen koennen einen Schadenstyp haben. Dieser kann als string
+// oder array angegeben werden, wird aber intern auf jeden Fall als
+// array gespeichert.
+static mixed _set_dam_type(mixed arg) {
+    if (pointerp(arg))
+    {
+        return Set(P_DAM_TYPE, arg, F_VALUE);
+    }
+    else if (stringp(arg))
+    {
+        return Set(P_DAM_TYPE, ({ arg }), F_VALUE);
+    }
+    return Query(P_DAM_TYPE, F_VALUE);
+}
+
+// Ruestungen koennen Resistenzen setzen. Diese werden jedoch nicht wie
+// "normale" Resistenzen gesetzt, sondern als Prozentwerte der fuer diesen
+// Typ maximal zulaesigen Resistenz. Die Aenderung der Resistenzen darf
+// nur durch die Ruestung selbst erfolgen.
+// Beispiel: ([DT_FIRE: 100, DT_WATER: -150])
+// max. Feuerresistenz, 1.5fache Anfaelligkeit
+static mixed _set_resistance_strengths(mixed resmap) {
+    float  max_res;
+    object worn_by;
+
+    // Es duerfen nur mappings angegeben werden
+    if (!mappingp(resmap))
+    {
+        return -1;
+    }
+
+    // die Maxwerte muessen jeweils durch -100 geteilt sein, da hinterher
+    // mit der Prozentzahl multipliziert wird und die angabe der Vorzeichen
+    // hier umgekehrt wie bei den "normalen" Resistenzen ist. Der
+    // Maximalwert ist vom Ruestungstyp abhaengig.
+    switch (QueryProp(P_ARMOUR_TYPE))
+    {
+        case AT_CLOAK  :
+        case AT_RING   :
+        case AT_AMULET : max_res=-0.0010;
+                         break;
+        case AT_SHIELD :
+        case AT_ARMOUR : max_res=-0.0015;
+                         break;
+        default        : max_res=-0.0005;
+    }
+
+    // Resistenz-Mapping aktualisieren
+    resistance_strengths=([]);
+    foreach(string damtype, int res: resmap)
+    {
+        if (!intp(res)) res=to_int(res);
+        // Mehr als 100 Prozent ist nicht erlaubt
+        if (res>100)
+        {
+            res=100;
+        }
+        else if (res<0)
+        {
+             res=(res/4)*5;
+             // Man kann auch nicht beliebig negativ werden
+             if (res<-1000)
+             {
+                 res=-1000;
+             }
+        }
+        // Der Resistenzwert berechnet sich aus dem Produkt des
+        // Maximalwertes und der Prozentzahl
+        resistance_strengths[damtype]=res*max_res;
+    }
+
+    // Werden die Resistenzen bei einer getragenen Ruestung geaendert,
+    // muss der Traeger davon natuerlich beeinflusst werden.
+    if (objectp(worn_by=QueryProp(P_WORN)))
+    {
+        worn_by->AddResistanceModifier(resistance_strengths,
+                                       QueryProp(P_ARMOUR_TYPE));
+    }
+   return resistance_strengths;
+}
+
+// Bei einem QueryProp(P_RESISTANCE_STRENGTHS) soll das aktuelle
+// Resistenzen-Mapping zurueckgegeben werden
+static mapping _query_resistance_strengths() {
+  return (resistance_strengths||([]));
+}
+
diff --git a/std/armour/container_description.c b/std/armour/container_description.c
new file mode 100644
index 0000000..b1e73ad
--- /dev/null
+++ b/std/armour/container_description.c
@@ -0,0 +1,67 @@
+// MorgenGrauen MUDlib
+//
+// armour/container_description.c -- armour_container description handling
+//
+// $Id: container_description.c 6306 2007-05-20 11:32:03Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/clothing/container_description";
+
+#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <thing/language.h>
+#include <container.h>
+#include <combat.h>
+#include <thing/material.h>
+#include <defines.h>
+#include <wizlevels.h>
+#include <player/base.h>
+
+string dam_descr() {   
+    string re;
+    mixed desc;
+    int maximum,dam,pos;
+
+    if (!QueryProp(P_NAME) || !QueryProp(P_DAMAGED) || !QueryProp(P_SHORT) ||
+        !(desc=QueryProp(P_DAM_DESC)) || (!stringp(desc) && !pointerp(desc)))
+        return "";
+    re = capitalize(name(WER,2))+" ";
+    maximum = QueryProp(P_AC)+(dam=QueryProp(P_DAMAGED));
+    if (stringp(desc))
+        return (dam>(maximum/2))?(re+desc+".\n"):"";
+    pos = (sizeof(desc)*dam/maximum);
+    if (stringp(desc[pos]))
+        return (re+desc[pos]+".\n");
+    return "";
+}
+
+mapping _query_material() {
+  mixed res,at;
+
+  if (mappingp(res=Query(P_MATERIAL)))
+    return res;
+  at=QueryProp(P_ARMOUR_TYPE);
+  switch(at) {
+    case AT_ARMOUR:
+    case AT_HELMET:
+    case AT_RING:
+    case AT_AMULET:
+    case AT_SHIELD:
+    return ([MAT_MISC_METAL:100]);
+    case AT_CLOAK:
+    case AT_TROUSERS:
+    return ([MAT_CLOTH:100]);
+    case AT_GLOVE:
+    case AT_BOOT:
+    return ([MAT_LEATHER:100]);
+  }
+  return ([MAT_LEATHER:100]);
+}
+
diff --git a/std/armour/description.c b/std/armour/description.c
new file mode 100644
index 0000000..2e26294
--- /dev/null
+++ b/std/armour/description.c
@@ -0,0 +1,62 @@
+// MorgenGrauen MUDlib
+//
+// armour/description.c -- armour description handling
+//
+// $Id: description.c 6306 2007-05-20 11:32:03Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/clothing/description";
+
+//#define NEED_PROTOTYPES
+
+#include <properties.h>
+
+string dam_descr()
+{   string re;
+    mixed desc;
+    int maximum,dam,pos;
+
+    if (!QueryProp(P_NAME) || !QueryProp(P_DAMAGED) || !QueryProp(P_SHORT) ||
+        !(desc=QueryProp(P_DAM_DESC)) || (!stringp(desc) && !pointerp(desc)))
+        return "";
+    re = capitalize(name(WER,2))+" ";
+    maximum = QueryProp(P_AC)+(dam=QueryProp(P_DAMAGED));
+    if (stringp(desc))
+        return (dam>(maximum/2))?(re+desc+".\n"):"";
+    if (maximum==dam)
+        pos=sizeof(desc)-1;
+    else
+        pos = (sizeof(desc)*dam/maximum);
+    if (stringp(desc[pos]))
+        return (re+desc[pos]+".\n");
+    return "";
+}
+
+
+mapping _query_material() {
+  mixed res,at;
+
+  if (mappingp(res=Query(P_MATERIAL)))
+    return res;
+  at=QueryProp(P_ARMOUR_TYPE);
+  switch(at) {
+    case AT_ARMOUR:
+    case AT_HELMET:
+    case AT_RING:
+    case AT_AMULET:
+    case AT_SHIELD:
+    return ([MAT_MISC_METAL:100]);
+    case AT_CLOAK:
+    case AT_TROUSERS:
+    return ([MAT_CLOTH:100]);
+    case AT_GLOVE:
+    case AT_BOOT:
+    return ([MAT_LEATHER:100]);
+  }
+  return ([MAT_LEATHER:100]);
+}
diff --git a/std/armour/moving.c b/std/armour/moving.c
new file mode 100644
index 0000000..de598a8
--- /dev/null
+++ b/std/armour/moving.c
@@ -0,0 +1,18 @@
+// MorgenGrauen MUDlib
+//
+// armour/moving.c -- armour moving object
+//
+// $Id: moving.c 6306 2007-05-20 11:32:03Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+// Dies muss eigentlich nix ueber clothing/moving hinausgehendes koennen.
+// Die Ruestungen erben auch alle direkt clothing/moving, dieses File
+// existiert eigentlich nur fuer den Fall, dass Leute ausserhalb von /std von
+// armour/moving erben.
+inherit "/std/clothing/moving";
+
diff --git a/std/armour/wear.c b/std/armour/wear.c
new file mode 100644
index 0000000..fef87c5
--- /dev/null
+++ b/std/armour/wear.c
@@ -0,0 +1,260 @@
+// MorgenGrauen MUDlib
+//
+// armour/wear.c -- armour standard object
+//
+// $Id: combat.c 6243 2007-03-15 21:10:21Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/clothing/wear";
+
+#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <living/combat.h>
+#include <living/attributes.h>
+#include <language.h>
+#include <defines.h>
+#include <armour.h>
+
+// Uebernimmt viele wesentliche Eigenschaften aus der Kleidung, daher werden
+// hier auch nur div. Funktionen ueberschrieben, die sich leicht
+// unterschiedlich zur Kleidung verhalten.
+
+// Globale Variablen
+private nosave int     logged;
+
+protected void create() {
+  ::create();
+  // P_DAMAGED laesst sich zwar (noch) von aussen setzen, aber bitte nur ueber
+  // die hier definierte Setmethode.
+  // TODO: Direktes Setzen von P_DAMAGED entfernen.
+  Set(P_DAMAGED, PROTECTED, F_MODE_AS);
+}
+
+/* Wenn eine Ruestung vom gleichen Typ im Array ist, gebe diese zurueck. */
+private object TestType(object *armours) {
+    mixed type;
+
+    // Den eigenen Typ feststellen
+    type = QueryProp(P_ARMOUR_TYPE);
+
+    // Zerstoerte Objekte aussortieren
+    armours-=({0});
+
+    foreach(object armour: armours)
+    {
+      if (type==(armour->QueryProp(P_ARMOUR_TYPE)))
+        {
+            // Ruestung vom gleichen Typ gefunden -> zurueckgeben
+            return armour;
+        }
+    }
+    // Keine Ruestung vom gleichen Typ gefunden, also 0 zurueckgeben
+    return 0;
+}
+
+// liefert 1 zurueck, wenn der Spieler die ruestung anziehen darf, 0 sonst.
+protected int _check_wear_restrictions(int silent, int all) {
+  mixed   type,res;
+  object  *armours;
+
+  type = QueryProp(P_ARMOUR_TYPE);
+
+  // Das DoWear() der Kleidung prueft auf genuegend freie Haende,
+  // prueft aber den Fall nicht ab, ob man hier ein Schild hat, was laut Prop
+  // weniger als eine Hand belegt. Daher hier nachholen und korrigieren. Und
+  // direkt mal loggen, damit das gefixt wird.
+  if ( (type==AT_SHIELD) && QueryProp(P_NR_HANDS)<1 ) {
+    SetProp(P_NR_HANDS,1);
+    log_file("INVALID_SHIELD",
+	sprintf("Fixme: AT_SHIELD item without valid P_NR_HANDS: %O\n",
+	  object_name()));
+  }
+
+  armours=(object*)PL->QueryProp(P_ARMOURS) - ({0});
+
+  // Von jedem Ruestungstyp ausser AT_MISC kann man immer nur ein
+  // Teil tragen
+  if ( (type!=AT_MISC) && (res=TestType(armours)) && objectp(res)) {
+    msg(break_string(sprintf(
+	  "Du traegst bereits %s als Schutz der Sorte %s.",
+	  res->name(WEN,1), type),78,
+	  (all?(Name(WER)+": "):0)), all);
+    return(-1);
+  }
+
+  // Ruestungen vom Typ AT_ILLEGAL oder solche mit einem fuer ihren
+  // Ruestungstyp zu hohen Schutzwert koennen nicht angezogen werden
+  if ( (type==AT_ILLEGAL) || (QueryProp(P_AC)>VALID_ARMOUR_CLASS[type])) {
+    write("Ungueltiger Ruestungstyp, bitte Erzmagier verstaendigen.\n");
+        "/p/daemon/ruestungen"->RegisterArmour();      
+    return(-2);
+  }
+
+  // Ruestungen, die ein oder mehrere Attribut veraendern und gegen
+  // das gesetzte Limit verstossen, haben keine Wirkung bezueglich der
+  // Attribute. Dies gibt aber nur ne Meldung aus, angezogen werden darf sie
+  // trotzdem.
+  if (mappingp(res=QueryProp(P_M_ATTR_MOD)) && PL->TestLimitViolation(res) ) {
+    write(break_string(sprintf(
+          "Irgendetwas an Deiner Ausruestung verhindert, dass Du Dich mit "
+          "%s so richtig wohl fuehlst.",name(WEM,1)),78,
+          (all?(Name(WER)+": "):0)));
+  }
+
+  // dann mal aus der Kleidung geerbte Pruefungen ausfuehren und Ergebnis
+  // liefern.
+  return ::_check_wear_restrictions(silent,all);
+}
+
+protected void _informwear(int silent, int all) {
+
+  // Ruestungen koennen Resistenzen beeinflussen
+  PL->AddResistanceModifier(QueryProp(P_RESISTANCE_STRENGTHS),
+                            QueryProp(P_ARMOUR_TYPE));
+
+  // Ruestungen koennen Attribute aendern/blockieren. Also muessen diese
+  // nach dem Anziehen aktualisiert werden
+  PL->register_modifier(ME);
+  PL->UpdateAttributes();
+
+  // P_TOTAL_AC im Traeger updaten (fuer Query()s)
+  PL->QueryProp(P_TOTAL_AC);
+
+  // Alle Ruestungen werden im awmaster registriert, sobald sie von
+  // einem Spieler gezueckt werden
+  if (!logged && query_once_interactive(PL)) {
+    call_other("/secure/awmaster","RegisterArmour",ME);
+    logged=1;
+  }
+  // noch das aus der Kleidung rufen, damit die Anziehmeldungen auch kommen.
+  // ausserdem laeuft das Anstosses von InformWear() von dort.
+  ::_informwear(silent,all);
+}
+
+
+protected int _check_unwear_restrictions(object worn_by, int silent, 
+                                            int all) {
+  // liefert >=0 zureck, wenn die Kleidung/Ruestung ausgezogen werden kann, 
+  // <0 sonst.
+
+  // Schilde belegen (mindestens) eine Hand. Hngl. Wenn man diesen bloeden
+  // Check nicht machen muesste, koennte man sich die ganze Funktion sparen.
+  // Hmpfgrmpfl. Achja, raise_error(), weil das eigentlich ja nicht vorkommen
+  // sollte und gefixt werden soll. Das geht naemlich nur, wenn jemand diese
+  // Prop geaendert, waehrend der Spieler das Schild getragen hatte.
+  if ( (QueryProp(P_ARMOUR_TYPE)==AT_SHIELD) && 
+      QueryProp(P_NR_HANDS)<1 ) {
+    raise_error(sprintf("Fixme: AT_SHIELD beim Ausziehen ohne P_NR_HANDS: %O",
+	  object_name()));
+  }
+  
+  // Ausziehcheck der Kleidung machen
+  return ::_check_unwear_restrictions(worn_by,silent,all);
+}
+
+protected void _informunwear(object worn_by, int silent, int all) {
+  mixed res;
+  // Gesetzte Resistenzen loeschen
+  worn_by->RemoveResistanceModifier(res=QueryProp(P_ARMOUR_TYPE));
+  
+  // Ruestungen koennen Attribute aendern/blockieren. Also muessen diese
+  // nach dem Ausziehen aktualisiert werden
+  worn_by->deregister_modifiers(ME);
+  worn_by->UpdateAttributes();
+
+  // P_TOTAL_AC im Traeger updaten
+  worn_by->QueryProp(P_TOTAL_AC);
+
+  // die geerbte Funktion aus der Kleindung gibt noch meldungen aus und ruft
+  // Informunwear().
+  ::_informunwear(worn_by,silent,all);
+}
+
+// Funktion, die das "trage"/"ziehe * an"-Kommando auswertet
+varargs int do_wear(string str, int silent) {  
+  int all;  
+
+  // Hat der Spieler "trage alles" eingegeben?
+  all=(str=="alles" || str=="alle ruestungen");
+  
+  return(_do_wear(str,silent,all));
+}
+
+// Die Funktion, die das "ziehe * aus"-Kommando auswertet
+varargs int do_unwear(string str, int silent) {
+  int all;
+
+  all=(str=="alles" || str=="alle ruestungen");
+  return(_do_unwear(str,silent,all));
+}
+
+
+// Objekte, die die Beschaedigung einer Ruestung durch direktes Setzen von
+// P_DAMAGED durchfuehren, werden im awmaster geloggt
+static mixed _set_item_damaged(mixed arg) {
+    if (arg && !intp(arg))
+    {
+        return Query(P_DAMAGED, F_VALUE);
+    }
+
+    if (previous_object(1))
+      call_other("/secure/awmaster","RegisterDamager",
+            previous_object(1),QueryProp(P_DAMAGED),arg); 
+
+    return Set(P_DAMAGED, arg, F_VALUE);
+}
+
+// Will man eine Ruestung beschaedigen oder reparieren, so macht man das
+// am besten ueber die Funktion Damage(argument). Positive Argumente
+// bedeuten eine Beschaedigung, negative eine Reparatur. Der Rueckgabewert
+// ist die wirklich durchgefuehrte Aenderung des Beschaedigungswertes
+int Damage(int new_dam) {   
+  int    ac,maximum,old_dam;
+  object w;
+
+  if (!new_dam || !intp(new_dam)) {      
+    return 0;
+  }
+
+  if ( (ac=QueryProp(P_AC))<=0 && (new_dam>0) ) {
+    // Sonst wuerde Beschaedigung zur Reparatur fuehren
+    return 0;
+  }
+
+  // Min-AC und MAX_AC beachten
+  if ((ac-new_dam) < MIN_ARMOUR_CLASS) {      
+    new_dam = ac-MIN_ARMOUR_CLASS;
+  }
+  else if ((ac-new_dam) >
+    (maximum=VALID_ARMOUR_CLASS[QueryProp(P_ARMOUR_TYPE)])) {      
+    new_dam = ac-maximum;
+  }
+
+  // Nie mehr als 100% reparieren
+  if ( ((old_dam=QueryProp(P_DAMAGED))<-new_dam) && (new_dam<0) ) {
+    new_dam=-old_dam;
+  }
+
+  // Aenderungen an der Ruestungsklasse und dem Beschaedigungswert
+  // durchfuehren
+  SetProp(P_AC,(ac-new_dam));
+  // eigene Set-Methode umgehen
+  Set(P_DAMAGED,(old_dam+new_dam),F_VALUE);
+
+  // P_TOTAL_AC im Traeger updaten, wenn vorhanden
+  if (objectp(w=QueryProp(P_WORN)))
+    w->QueryProp(P_TOTAL_AC);
+
+  // Rueckgabewert: Durchgefuehrte Aenderung an P_DAMAGE
+  return new_dam;
+}
+
+public status IsArmour() {return 1;}
+public status IsClothing() {return 0;}
+
diff --git a/std/armourHG.c b/std/armourHG.c
new file mode 100644
index 0000000..352e75c
--- /dev/null
+++ b/std/armourHG.c
@@ -0,0 +1,59 @@
+// MorgenGrauen MUDlib
+//
+// armourHG.c -- armour standard object for hats and glasses
+//
+// $Id: armourHG.c 7804 2011-07-10 20:37:52Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/armour";
+
+#define NEED_PROTOTYPES
+#include <thing/commands.h>
+#undef NEED_PROTOTYPES
+
+#include <language.h>
+#include <defines.h>
+
+int setzen(string s);
+
+void create() {
+    ::create();
+    AddCmd(({"setz","setze"}),"setzen");
+}
+
+varargs void doWearMessage( int all ) {
+  if( query_once_interactive(PL) ) {
+    write( "Du setzt " + name(WEN,1) + " auf.\n" );
+  }
+  if (objectp(environment()) && objectp(environment(environment())))
+    tell_room(environment(environment()),
+	capitalize((string)PL->name(WER)) + " setzt " + name(WEN,0) + 
+	" auf.\n");
+}
+
+varargs void doUnwearMessage( object worn_by, int all ) {
+  if( query_once_interactive(worn_by) ) {
+    tell_object(worn_by,  "Du setzt " + name(WEN,1) + " ab.\n" );
+  }
+  tell_room(environment(worn_by), 
+	    (capitalize((string)worn_by->name(WER))) + " setzt " +
+	    name(WEN,0) + " ab.\n", ({worn_by}));
+}
+
+int setzen(string str) {
+  string ob;
+
+  if(!str)
+    return 0;
+  if(sscanf(str, "%s auf", ob)==1 )
+    return _do_wear(ob, 0, 0);
+  if(sscanf(str, "%s ab", ob)==1 )
+    return _do_unwear(ob, 0, 0);
+  return 0;
+}
+
diff --git a/std/armour_container.c b/std/armour_container.c
new file mode 100644
index 0000000..7ea22d2
--- /dev/null
+++ b/std/armour_container.c
@@ -0,0 +1,54 @@
+// MorgenGrauen MUDlib
+//,
+// armour_container.c -- Ruestung, in die man was reinstecken kann
+//
+// $Id: armour_container.c 7804 2011-07-10 20:37:52Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/properties";
+inherit "/std/thing/commands";
+inherit "/std/thing/language";
+inherit "/std/thing/envchk";
+inherit "/std/container/light";
+inherit "/std/container/restrictions";
+inherit "/std/container/inventory";
+inherit "/std/container/items";
+inherit "/std/clothing/moving";
+inherit "/std/armour/wear";
+inherit "/std/armour/combat";
+inherit "/std/armour/container_description";
+
+//#define NEED_PROTOTYPES
+
+#include <container.h>
+
+protected void create() {
+  properties::create();
+  commands::create();
+  light::create();
+  container_description::create();
+  restrictions::create();
+  items::create();
+  wear::create();
+  combat::create();
+  envchk::create();
+  SetProp(P_CONTAINER,1);
+  SetProp(P_PREPOSITION, "in");
+  SetProp(P_SOURCE_PREPOSITION, "aus");
+  SetProp(P_DEST_PREPOSITION, "in");
+  AddId("Ding");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void reset()  {
+  items::reset();
+}
+
diff --git a/std/clothing.c b/std/clothing.c
new file mode 100644
index 0000000..8d25984
--- /dev/null
+++ b/std/clothing.c
@@ -0,0 +1,54 @@
+// MorgenGrauen MUDlib
+//
+// armour.c -- armour standard object
+//
+// $Id: armour.c,v 3.8 2003/08/25 09:36:04 Rikus Exp $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma range_check
+#pragma pedantic
+
+inherit "/std/thing/properties";
+inherit "/std/thing/commands";
+inherit "/std/thing/restrictions";
+inherit "/std/thing/light";
+inherit "/std/clothing/description";
+inherit "/std/clothing/moving";
+inherit "/std/clothing/wear";
+inherit "/std/thing/language";
+inherit "/std/thing/envchk";
+
+//#define NEED_PROTOTYPES
+
+#include <config.h>
+#include <properties.h>
+#include <language.h>
+#include <combat.h>
+#include <wizlevels.h>
+#include <defines.h>
+
+protected void create()
+{
+  properties::create();
+  commands::create();
+  light::create();
+  restrictions::create();
+  description::create();
+  wear::create();
+  envchk::create();
+  AddId("Ding");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// zum Ueberschreiben, damit es nicht buggt, wenn Leute (wie gewuenscht) in 
+// ihren Objekten ::reset() aufrufen.
+void reset() {
+}
+
+int IsClothing() {return 1;}
+
diff --git a/std/clothing/container_description.c b/std/clothing/container_description.c
new file mode 100644
index 0000000..f7942bc
--- /dev/null
+++ b/std/clothing/container_description.c
@@ -0,0 +1,137 @@
+// MorgenGrauen MUDlib
+//
+// clothing/container_description.c -- clothing_container description handling
+//
+// $Id: container_description.c 6198 2007-02-13 23:39:43Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/description";
+
+#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <thing/language.h>
+#include <container.h>
+#include <combat.h>
+#include <thing/material.h>
+#include <defines.h>
+#include <wizlevels.h>
+#include <player/base.h>
+
+void create()
+{
+  ::create();
+  SetProp(P_TRANSPARENT, 1);
+  AddId("Container");
+}
+
+string dam_descr()
+{   string re;
+    mixed desc;
+    int maximum,dam,pos;
+
+    if (!QueryProp(P_NAME) || !QueryProp(P_DAMAGED) || !QueryProp(P_SHORT) ||
+        !(desc=QueryProp(P_DAM_DESC)) || (!stringp(desc) && !pointerp(desc)))
+        return "";
+    re = capitalize(name(WER,2))+" ";
+    maximum = QueryProp(P_AC)+(dam=QueryProp(P_DAMAGED));
+    if (stringp(desc))
+        return (dam>(maximum/2))?(re+desc+".\n"):"";
+    pos = (sizeof(desc)*dam/maximum);
+    if (stringp(desc[pos]))
+        return (re+desc[pos]+".\n");
+    return "";
+}
+
+string short()
+{   string s;
+    if(!(s=process_string(QueryProp(P_SHORT))))
+        return 0;
+    return s+(QueryProp(P_WORN)?" (angezogen).\n":".\n");
+}
+
+varargs string long(int mode)
+{   string descr, inv_descr;
+
+    descr=(process_string(QueryProp(P_LONG)||"") + (dam_descr()||""));
+    if (!QueryProp(P_TRANSPARENT))
+      return descr;
+
+    inv_descr = make_invlist(PL, all_inventory(ME), mode );
+    if ( inv_descr != "" )
+        descr += capitalize(QueryPronoun(WER)) + " enthaelt:\n" + inv_descr;
+    return descr;
+}
+
+mapping _query_material() {
+  mixed res,at;
+
+  if (mappingp(res=Query(P_MATERIAL)))
+    return res;
+  at=QueryProp(P_ARMOUR_TYPE);
+  switch(at) {
+    case AT_ARMOUR:
+    case AT_HELMET:
+    case AT_RING:
+    case AT_AMULET:
+    case AT_SHIELD:
+    return ([MAT_MISC_METAL:100]);
+    case AT_CLOAK:
+    case AT_TROUSERS:
+    return ([MAT_CLOTH:100]);
+    case AT_GLOVE:
+    case AT_BOOT:
+    return ([MAT_CLOTH:100]);
+  }
+  return ([MAT_CLOTH:100]);
+}
+
+// flags: 1 - wizard, 2 - don't collect equal objects '
+// flags: 4 - don't append infos for wizards
+private void stringenize(mixed obj, int flags, mixed objs, mixed info)
+{
+  string id, tmp;
+  int idx;
+  tmp = capitalize(obj->short()||"")[0..<2]
+      + (!(flags & 4) && (flags & 1) ? " ["+object_name(obj)+"]" : "");
+  if(flags & 3 || living(obj)) id = object_name(obj);
+  else
+    id = explode(object_name(obj), "#")[0] + tmp;
+  if((idx = member(objs, id)) == -1)
+  {
+    objs += ({ id });
+    info += ({ ({ tmp, 1, obj}) });
+  }
+  else
+    info[idx][1]++;
+}
+
+private string collect(mixed obj)
+{
+  if(!sizeof(obj[0])) return 0;
+  return obj[0] + (obj[1] > 1 ? " ("+obj[1]+")" : "");
+}
+
+// flags: 1 - return array, 2 - don't collect equal objects '
+// flags: 4 - don't append infos for wizards
+varargs mixed make_invlist(object viewer, mixed inv, int flags)
+{
+  int iswiz;
+  mixed objs, info;
+  string descr;
+
+  iswiz = IS_LEARNER( viewer ) && viewer->QueryProp(P_WANTS_TO_LEARN);
+  descr = ""; objs = ({}); info = ({});
+  map(inv, #'stringenize/*'*/, iswiz | (flags & 2) | (flags & 4), &objs, &info);
+  if(flags & 1) return info;
+  inv = map(info, #'collect/*'*/) - ({ 0 });
+  if(!sizeof(inv)) return "";
+  return sprintf("%"+(sizeof(inv) > 6 ? "#" : "=")+"-78s",
+                 implode(inv, "\n")) + "\n";
+}
diff --git a/std/clothing/description.c b/std/clothing/description.c
new file mode 100644
index 0000000..fa226fd
--- /dev/null
+++ b/std/clothing/description.c
@@ -0,0 +1,58 @@
+// MorgenGrauen MUDlib
+//
+// armour/description.c -- armour description handling
+//
+// $Id: description.c,v 1.7 2002/08/19 14:21:04 Rikus Exp $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+#pragma warn_empty_casts
+
+inherit "/std/thing/description";
+
+//#define NEED_PROTOTYPES
+
+#include <properties.h>
+
+string dam_descr() {
+  return "";
+}
+
+string short()
+{   string s;
+    if(!(s=process_string(QueryProp(P_SHORT))))
+        return 0;
+    return s+(QueryProp(P_WORN)?" (angezogen).\n":".\n");
+}
+
+varargs string long()
+{   
+    return (process_string(QueryProp(P_LONG)||"") + (dam_descr()||""));
+}
+
+mapping _query_material() {
+  mixed res,at;
+
+  if (mappingp(res=Query(P_MATERIAL)))
+   return res;
+  at=QueryProp(P_ARMOUR_TYPE);
+  switch(at) {
+    case AT_ARMOUR:
+    case AT_HELMET:
+    case AT_RING:
+    case AT_AMULET:
+    case AT_SHIELD:
+    return ([MAT_MISC_METAL:100]);
+    case AT_CLOAK:
+    case AT_TROUSERS:
+    return ([MAT_CLOTH:100]);
+    case AT_GLOVE:
+    case AT_BOOT:
+    return ([MAT_LEATHER:100]);
+  }
+  return ([MAT_CLOTH:100]);
+}
+
diff --git a/std/clothing/moving.c b/std/clothing/moving.c
new file mode 100644
index 0000000..ba2e05f
--- /dev/null
+++ b/std/clothing/moving.c
@@ -0,0 +1,46 @@
+// MorgenGrauen MUDlib
+//
+// armour/moving.c -- armour moving object
+//
+// $Id: moving.c,v 3.3 1998/03/02 08:34:57 Paracelsus Exp $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/moving";
+
+#define NEED_PROTOTYPES 1
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <moving.h>
+#include <defines.h>
+#include <clothing.h>
+
+/* Bekleidung muss vor Bewegung und Zerstoerung ausgezogen werden */
+
+varargs int move(mixed dest, int method ) {
+  // ggf. Ausziehen
+  if (objectp(QueryProp(P_WORN)))
+    DoUnwear(method & (M_SILENT|M_NOCHECK));
+
+  if ((method&M_NOCHECK) || (!(object)QueryProp(P_WORN)))
+    return ::move(dest, method);
+
+  return ME_CANT_BE_DROPPED;
+}
+
+varargs int remove(int silent) {
+  // ggf. Ausziehen
+  if (objectp(QueryProp(P_WORN)))
+    DoUnwear(M_SILENT|M_NOCHECK);
+
+  if (!(object)QueryProp(P_WORN))
+    return ::remove(silent);
+  // Ausziehen hat irgendwie nicht geklappt. :-(
+  return 0;
+}
+
diff --git a/std/clothing/wear.c b/std/clothing/wear.c
new file mode 100644
index 0000000..dcde4bd
--- /dev/null
+++ b/std/clothing/wear.c
@@ -0,0 +1,594 @@
+// MorgenGrauen MUDlib
+//
+// clothing/wear.c -- Funktionen rund ums Anziehen/Tragen von Kleidung.
+//
+// $Id: combat.c 6243 2007-03-15 21:10:21Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <thing/commands.h>
+#include <thing/description.h>
+#include <living/clothing.h>
+#include <clothing.h>
+#include <living/combat.h>
+#include <language.h>
+#include <defines.h>
+#include <new_skills.h>
+#include <moving.h>
+
+// Globale Variablen
+nosave int  flaw, ftime;
+
+void create()
+{
+  // Einige Properties sollten nicht von aussen gesetzt werden koennen
+  Set(P_WORN, PROTECTED, F_MODE);
+  Set(P_LAST_USE, PROTECTED, F_MODE_AS);
+  Set(P_RESTRICTIONS,([]),F_VALUE);
+
+  // Bekleidung benoetigt Kommandos, mit denen man sie an- und
+  // ausziehen kann
+  AddCmd( ({"zieh","ziehe"}),"ziehe" );
+  AddCmd( ({"trag","trage"}),"do_wear" );
+}
+
+// aktuelles Lebewesen, was diese Kleidung (oder auch Ruestung) zur Zeit
+// traegt.
+public object QueryUser()
+{
+  return QueryProp(P_WORN);
+}
+
+
+// Ausgabe von Meldungen ueber write() oder _notify_fail(), je nachdem, ob der
+// Spieler alles anzieht oder was bestimmtes.
+protected void msg(string str, mixed fl) {
+  if (!stringp(str)) {
+    return;
+  }
+  if (fl) {
+    write(str);
+  }
+  else {
+    _notify_fail(str);
+  }
+}
+
+/*
+ * Ausgabe einer Meldung beim Anziehen geht nur an Spieler, nicht an NPC.
+ * Die Umgebung bekommt immer eine Meldung.
+ */
+varargs void doWearMessage(int all) {
+  string *str,s1;
+  mixed wearmsg;
+
+  if(wearmsg=QueryProp(P_WEAR_MSG)) { // Ist eine WearMsg gesetzt?
+    if(closurep(wearmsg)) { // Evtl. gar als extra Fkt.?
+
+      str = funcall(wearmsg, PL);
+      if(interactive(PL)) {
+         // Im Falle, dass all gesetzt ist, wird als Indent der Name des
+         // angezogenen Objektes gesetzt. (trag alles)
+         write(break_string(str[0],78,
+                (all?(Name(WER)+": "):0), BS_LEAVE_MY_LFS));
+      }
+      //(Zesstra): say() ist hier bloed, weil es an das Env vom this_player()
+      //ausgibt, sofern der existiert. So koennen Spieler die meldung kriegen,
+      //obwohl die laengst woanders sind (z.B. Sequenzen)
+      //Daher nun Ausgabe an das Env vom Env (wenn das kein Raum sein sollte,
+      //was durchaus sein koennte, macht tell_room() nix). 
+      if ( objectp(environment()) && objectp(environment(environment())) )
+          tell_room(environment(environment()),
+            break_string(str[1], 78, 0, BS_LEAVE_MY_LFS),({PL}) );
+
+      return;
+    }
+    else if(interactive(PL)) {
+      s1 = replace_personal(sprintf(wearmsg[0],"@WEN2"), ({PL,ME}), 1);
+
+      write(break_string(s1,78,(all?(Name(WER)+": "):0), BS_LEAVE_MY_LFS));
+    }
+
+    s1 = replace_personal(sprintf(wearmsg[1],"@WER1","@WENU2"),
+                            ({PL, ME}), 1);
+ 
+    if ( objectp(environment()) && objectp(environment(environment())) )
+        tell_room(environment(environment()),
+            break_string(s1, 78, 0, BS_LEAVE_MY_LFS),({ PL }) );
+
+    return;
+  }
+  /*
+   * Keine WearMsg gesetzt. Ausgabe der Default-Meldungen.
+   */
+  else if(interactive(PL)) {
+    write(break_string("Du ziehst " + name(WEN,1) + " an.",78,
+     (all?(Name(WER)+": "):0)));
+  }
+  if ( objectp(environment()) && objectp(environment(environment())) )
+      tell_room(environment(environment()),break_string(PL->Name(WER)
+            + " zieht " + name(WEN,0) +" an.",78), ({PL}));
+}
+
+/*
+ * Ausgabe einer Meldung beim Ausziehen geht nur an Spieler, nicht an NPC.
+ * Die Umgebung bekommt natuerlich immer eine Meldung.
+ */
+void doUnwearMessage(object worn_by, int all)
+{
+  string *str,s1;
+  mixed msg;
+
+  if(!objectp(worn_by)) { // Na huch, gar nicht angezogen? Abbruch.
+    return;
+  }
+
+  if(msg=QueryProp(P_UNWEAR_MSG)) { // Ist eine UnwearMsg gesetzt?
+    if(closurep(msg)) { // Oho! Gar gleich als Fkt.?
+
+      str = funcall(msg, worn_by);
+      if(interactive(worn_by)) {
+         tell_object(worn_by,break_string(str[0], 78,
+                                 (all?(Name(WER)+": "):0),BS_LEAVE_MY_LFS));
+      }
+
+      //(Zesstra): say() ist hier bloed, weil es an das Env vom this_player()
+      //ausgibt, sofern der existiert. So koennen Spieler die meldung kriegen,
+      //obwohl die laengst woanders sind (z.B. Sequenzen)
+      //Daher nun Ausgabe an das Env vom worn_by (wenn das kein Raum sein sollte,
+      //macht tell_room() nix). 
+      if ( objectp(environment(worn_by)) )
+          tell_room(environment(worn_by),
+              break_string(str[1],78, 0, BS_LEAVE_MY_LFS),({ worn_by }) );
+      
+      return;
+    }
+    else if(interactive(worn_by)) {
+      s1 = replace_personal(sprintf(msg[0],"@WEN2"),
+                      ({worn_by,ME}), 1);
+
+      tell_object(worn_by,break_string(s1,78,
+                              (all?(Name(WER)+": "):0), BS_LEAVE_MY_LFS));
+    }
+
+    s1 = replace_personal(sprintf(msg[1],"@WER1","@WENU2"),
+                    ({worn_by, ME }), 1);
+
+    if ( objectp(environment(worn_by)) )
+        tell_room(environment(environment()),
+            break_string(s1,78, 0, BS_LEAVE_MY_LFS),({ worn_by }) );
+    return;
+  }
+  /*
+   * Keine UnwearMsg gesetzt. Ausgabe der Default-Meldungen.
+   */
+  else if(interactive(worn_by)) {
+    tell_object(worn_by,break_string("Du ziehst " + name(WEN,1) + " aus.",78,
+     (all?(Name(WER)+": "):0)));
+  }
+  if ( objectp(environment(worn_by)) )
+      tell_room(environment(worn_by), break_string(worn_by->Name(WER)
+            + " zieht " + name(WEN,0) + " aus.",78), ({ worn_by }) );
+}
+
+// Diese Funktion wird aufgerufen, wenn die Ruestung wirklich angezogen
+// wird
+protected void InformWear(object pl, int silent, int all) {
+    return;
+}
+
+// Diese Funktion wird aufgerufen, wenn die Ruestung wirklich ausgezogen
+// wird
+protected void InformUnwear(object pl, int silent, int all) {
+    return;
+}
+
+// liefert Werte <0 zurueck, wenn der Spieler die Kleidung _nicht_ 
+// anziehen darf.
+// Hierbei steht -1 dafuer, dass der Aufrufer return 0 machen sollte,
+// <= -2 sollte zur einem return !all fuehren.
+protected int _check_wear_restrictions(int silent, int all) {
+  mixed   type,res;
+  object  *armours;
+
+  // Man kann nur Kram anziehen, die man bei sich traegt
+  if (environment()!=PL) {            
+    msg(break_string("Du musst "+name(WEN,1)+" erst nehmen!",78,
+            (all?(Name(WER)+": "):0)), all);
+    return(-1);
+  }
+
+  // Eine getragene Ruestung kann man nicht nochmal anziehen
+  if (QueryProp(P_WORN)) {
+    msg(break_string("Du traegst "+name(WEN,1)+" bereits.",78,
+          (all?(Name(WER)+": "):0)), all);
+    return(-1);
+  }
+
+  // Diese Funktion versucht immer, TP anzuziehen (*args*). Es gibt aber viele
+  // Magier, die ohne TP oder mit dem falschen TP anziehen wollen. Daher mal
+  // pruefen und ggf. Fehler ausloesen.
+  if (!this_player())
+      raise_error("Kein this_player() existent beim Anziehen!\n");
+  else if (this_player() != environment())
+      raise_error("Meine Umgebung beim Anziehen ist nicht this_player()!\n");
+
+  // Ueber P_RESTRICTIONS kann man einige Restriktionen definieren, ohne
+  // gleich auf eine WearFunc zurueckgreifen zu muessen.
+  // Die Auswertung erfolgt ueber den RestrictionChecker
+  if ((res=QueryProp(P_RESTRICTIONS)) && mappingp(res) &&
+      (res=(string)"/std/restriction_checker"->check_restrictions(PL,res))
+      && stringp(res)) {  
+    msg(break_string(res,78,(all?(Name(WER)+": "):0)),all);
+    return(-1);
+  }
+
+  // Ist eine WearFunc gesetzt, wird diese aufgerufen.
+  if (objectp(res=QueryProp(P_WEAR_FUNC)) && 
+      !(res->WearFunc(ME, silent, environment()))) {
+    // Eine Meldung muss von der WearFunc ausgegeben werden
+    return(-2);
+  }
+
+  // scheinbar darf man das hier anziehen. ;-)
+  return 0;
+}
+
+protected void _informwear(int silent, int all) {
+
+  // Eine Meldung ausgeben, wenn das silent-Flag nicht gesetzt ist
+  if (!silent) {
+    doWearMessage(all);
+  }
+
+  // Inform-Funktion aufrufen
+  InformWear(PL, silent, all);
+}
+
+// Die Funktion, die das eigentliche Anziehen durchfuehrt
+varargs int DoWear(int silent, int all) {
+  int nh;
+
+  // Bedingungen pruefen, _check_restrictions() gibt die notwendigen Meldungen
+  // an den Spieler aus.
+  int res = _check_wear_restrictions(silent, all);
+  if (res == -1)
+    return(0);
+  else if (res <= -2)
+    return(!all);
+
+  // Der Check auf freie Haende muss nach allen anderen Checks aus Kleidung
+  // und Ruestung erfolgen und ist im Prinzip identisch fuer beide. Daher wird
+  // der hier in dieser Funktion gemacht.
+  // Soll das Objekt Haende "benutzen"? Steht da auch wirklich ein
+  // Integer-Wert drin? ich mach da jetzt ein raise_error(), das soll
+  // schliesslich gefixt werden. Ausserdem spart es nen Workaround beim
+  // Ausziehen.
+  if (!intp(nh=QueryProp(P_NR_HANDS))) {
+    raise_error(sprintf("Invalid P_NR_HANDS in %O",object_name()));
+  }
+  // Wenn Haende benutzt werden sollen, muss natuerlich auch getestet
+  // werden, ob das ueberhaupt geht
+  if (nh>0) {
+    if (!(PL->UseHands(ME, nh))) {
+      // Schade, nicht genug Haende frei -> Meldung ausgeben
+      write(break_string("Du hast keine Hand mehr frei.",78,
+            (all?(Name(WER)+": "):0)));
+      return(!all);
+    }
+  }
+
+  // OK, die Ruestung kann angezogen werden.
+  // Behinderung beim Wechsel nur fuer Spieler
+  if (query_once_interactive(PL))
+  // Wenn das Ganze ,,wirklich'' eine Kleidung/Ruestung ist und kein SMS
+  // oder aehnliches...
+  if (!QueryProp(P_WEAPON_TYPE)) {
+    // Aktion noch setzen, Spieler hat ja was angezogen
+    PL->SetProp(P_LAST_WEAR_ACTION,({WA_WEAR,time()}));
+    // Im Kampf verliert der Spieler durch Kleidungswechsel eine Runde.
+    if (PL->InFight()) {
+      PL->SetProp(P_ATTACK_BUSY,1);
+    }
+  }
+  // Eintragen in P_CLOTHING/P_ARMOURS
+  PL->Wear(this_object());
+
+  PL->SetProp(P_EQUIP_TIME,time());
+  SetProp(P_WORN, PL);
+  SetProp(P_EQUIP_TIME,time());
+
+  // ggf. andere Objekte informieren etc.
+  _informwear(silent, all);
+
+  // Fertig mit dem Anziehen. Vorgang beenden bzw. mit anderen
+  // Ruestungen fortfahren
+  return !all;
+}
+
+
+// liefert 0 zureck, wenn die Kleidung/Ruestung ausgezogen werden kann
+// bei M_NOCHECK ist das Ausziehen immer erlaubt, allerdings wird
+// P_REMOVE_FUNC auch dann gerufen (wenn auch ignoriert).
+// <0 verbietet das Ausziehen
+protected int _check_unwear_restrictions(object worn_by, int silent, 
+                                            int all)
+{
+  // Nicht getragene Ruestungen kann man auch nicht ausziehen
+  if (!objectp(worn_by)) {
+      return(-2);
+  }
+
+  // Ist eine RemoveFunc gesetzt, wird diese aufgerufen
+  // Im Falle von M_NOCHECK wird das Ergebnis allerdings ignoriert.
+  mixed res=QueryProp(P_REMOVE_FUNC);
+  if (objectp(res)
+      && !res->RemoveFunc(ME,silent,worn_by)
+      && !(silent & M_NOCHECK)
+      )
+  {
+      // Eine Meldung muss von der RemoveFunc ausgegeben werden
+      return(-2);
+  }
+
+  // generell hebt M_NOCHECK die Restriktionen auf - sonst kommt es zu
+  // massiven Inkonsistenzen beim Bewegen mit M_NOCHECK.
+  if (silent & M_NOCHECK)
+    return 1;
+
+  // Eine verfluchte Ruestung kann man natuerlich nicht ausziehen
+  res=QueryProp(P_CURSED);
+  if (res ) {
+    if (stringp(res)) {
+      // Stand in P_CURSED ein String? Dann diesen ausgeben
+      tell_object(worn_by,      
+          (res[<1]=='\n' ? res : break_string(res,78,
+            (all?(Name(WER)+": "):0))));
+    }
+    else {
+      // Sonst eine Standard-Meldung ausgeben
+      tell_object(worn_by,break_string(
+          "Du kannst " + name(WEN) + " nicht ausziehen, " + QueryPronoun(WER)
+          + " ist verflucht worden.\n",78,(all?(Name(WER)+": "):0)));
+    }
+    return(-2);
+  }
+
+  // Ausziehen moeglich
+  return(1);
+}
+
+protected void _informunwear(object worn_by, int silent, int all) {
+
+  // Inform-Funktion aufrufen
+  InformUnwear(worn_by, silent, all);
+
+  // Meldung ausgeben, wenn das silent-Flag nicht gesetzt ist
+  if (!(silent&M_SILENT)) {
+    doUnwearMessage( worn_by, all );
+  }
+}
+
+// Die Funktion, die das eigentliche Ausziehen durchfuehrt
+// hier steht nur drin, was auf jeden Fall fuer Kleidungen und Ruestungen
+// gleich ist, damit man bei Ruestungen diese Funktion nicht ueberschreiben
+// muss.
+varargs int DoUnwear(int silent, int all) {
+  object worn_by;
+  int nh;
+
+  // Das Flag "silent" wird in der RemoveFunc() etwas anders behandelt
+  // als ueberall anders. Deshalb wird M_SILENT gesetzt, sofern "silent"
+  // _irgendeinen_ Wert ausser M_NOCHECK hatte.
+  if ( silent & ~M_NOCHECK )
+      silent |= M_SILENT;
+
+  // Standard-Notfiyfail setzen.
+  if (all)
+    notify_fail("Alles ausgezogen, was ging.\n");
+
+  // Hat das Objekt Haende "benutzt"? Steht da auch wirklich ein
+  // Integer-Wert drin? Wenn nicht, mach ich nen raise_error(), das duerfte
+  // eigentlich gar nicht passieren. Pruefung mal am Anfang machen, bevor
+  // irgendwas anderes (RemoveFunc()) passiert ist.
+  if (!intp(nh=QueryProp(P_NR_HANDS))) {
+    raise_error(sprintf("Invalid P_NR_HANDS in %O",object_name()));
+  }
+
+  worn_by=QueryProp(P_WORN);
+  // darf ausgezogen werden? Wenn nicht, Ende.
+  int res = _check_unwear_restrictions(worn_by,silent,all);
+  if (res < 0)
+    return(!all);
+
+  // OK, alles klar, die Ruestung wird ausgezogen
+  worn_by->Unwear(ME);
+
+  // Benutzte Haende wieder freigeben
+  if (nh>0) {
+    worn_by->FreeHands(ME);
+  }
+
+  worn_by->SetProp(P_EQUIP_TIME, time());
+  SetProp(P_WORN, 0);
+
+  // Flag noch setzen, Spieler hat ja was ausgezogen
+  // Aber nur wenns auch der Spieler selbst ist.
+  // und wenn das wirklich eine Ruestung und kein SMS o.ae. ist.
+  if (PL && PL==worn_by && !QueryProp(P_WEAPON_TYPE)) {
+    //Behinderung beim Wechsel nur fuer Spieler
+    if (query_once_interactive(PL)) {
+      PL->SetProp(P_LAST_WEAR_ACTION,({WA_UNWEAR,time()}));
+      if (PL->InFight()) { 
+        PL->SetProp(P_ATTACK_BUSY,1);
+      }
+    }
+  }
+
+  // ok, nun noch andere Objekte informieren.
+  _informunwear(worn_by,silent,all);
+
+  // Fertig mit dem Anziehen. Vorgang beenden bzw. mit anderen
+  // Ruestungen fortfahren
+  return !all;
+}
+
+protected int _do_wear(string str, int silent, int all) {
+  int *last;
+
+  // Standard-Notfiy-Fail setzen.
+  if (all)
+    notify_fail("Alles angezogen, was ging.\n");
+
+  // Ist diese Ruestung ueberhaupt gemeint? Bei "trage alles" ist dies
+  // natuerlich immer der Fall
+  if (!str || (!all && !id(str))) {
+      return 0;
+  }
+
+  // Vielleicht darf der Spieler ja gar nix mehr anziehen.
+  if ((object)PL->InFight()) {        
+    last=(int*)PL->QueryProp(P_LAST_WEAR_ACTION);
+      if (pointerp(last) && (last[0]==WA_UNWEAR) && ((time()-last[1])<2)) {
+        notify_fail("Du hast doch gerade erst etwas ausgezogen!\n"
+            "So schnell bist Du nicht!\n");        
+        return 0;        
+      }
+  }
+
+  // Auf zum eigentlichen Anziehen
+  return DoWear(silent, all);
+
+}
+
+// Funktion, die das "trage"/"ziehe * an"-Kommando auswertet
+varargs int do_wear(string str, int silent) {  
+  int all;  
+
+  // Hat der Spieler "trage alles" eingegeben?
+  all=(str=="alles" || str=="alle kleidung" || str=="alle bekleidung");
+  
+  return(_do_wear(str,silent,all));
+}
+
+protected int _do_unwear(string str, int silent, int all) {
+  int * last;
+
+  // Ist diese Ruestung ueberhaupt gemeint? Und hat dieser Spieler sie
+  // auch wirklich an?
+  if (!stringp(str) || (!all && !id(str))) {
+    return 0;
+  }
+
+  if (!QueryProp(P_WORN)) { 
+    if (all) {  
+      notify_fail("Alles ausgezogen, was ging.\n");
+      return 0;
+    }
+    if (!Query(P_ARTICLE) || QueryProp(P_PLURAL)) {
+      notify_fail( break_string(
+            "Du traegst k"+name(WEN,0)+".",78) );
+    }
+    else {    
+      notify_fail( break_string(
+           "Du traegst "+name(WEN,1)+" nicht.",78) );  
+    }
+    return 0;
+  }
+
+  // Vielleicht darf der Spieler ja gar nichts mehr ausziehen.
+  if ((object)PL->InFight()) {
+    last=(int*)PL->QueryProp(P_LAST_WEAR_ACTION);
+    if (pointerp(last) && (last[0]==WA_WEAR) && ((time()-last[1])<2)) {
+      notify_fail("Du hast doch gerade erst etwas angezogen!\n"              
+          "So schnell bist Du nicht!\n");
+      return 0;
+    }
+  }
+  // Auf zum eigentlichen Ausziehen
+  return DoUnwear(silent, all);
+}
+
+// Die Funktion, die das "ziehe * aus"-Kommando auswertet
+varargs int do_unwear(string str, int silent) {
+  int all;
+
+  all=(str=="alles" || str=="alle kleidung" || str=="alle bekleidung");
+
+  return(_do_unwear(str,silent,all));
+}
+
+// Funktion, die das "ziehe"-Kommando auswertet
+int ziehe(string str) {   
+  string ob;
+
+  // Uebergebenes Argument pruefen
+  if (!stringp(str)) {
+    return 0;  
+  }
+
+  // Ist ANziehen gemeint?
+  if (sscanf(str, "%s an", ob)==1) {
+      return do_wear(ob );
+  }
+
+  // Oder ist AUSziehen gemeint?
+  if (sscanf(str, "%s aus", ob)==1 ) { 
+      return do_unwear(ob);
+  }
+
+  // Ok, es geht wohl weder ums an- noch ums ausziehen
+  return 0;
+}
+
+
+// Beschaedigen des Kleidungsstuecks
+
+// Direktes Beschaedigen der Kleidung durch Setzen der Prop gibts nicht. ;-)
+// Das geht aus Kompatibilitaetgruenden bei Ruestungen, aber nicht mehr bei
+// Kleidung. Punkt.
+static mixed _set_item_damaged(mixed arg) {
+  return(QueryProp(P_DAMAGED));
+}
+
+// Will man eine Kleidung beschaedigen oder reparieren, so macht man das
+// am besten ueber die Funktion Damage(argument). Positive Argumente
+// bedeuten eine Beschaedigung, negative eine Reparatur. Der Rueckgabewert
+// ist die wirklich durchgefuehrte Aenderung des Beschaedigungswertes
+int Damage(int new_dam) {
+  return 0;
+}
+
+// Wird die Kleidung einer Belastung ausgesetzt (bei Ruestungen z.B. bei einem
+// Angriff eines Gegners), dann wird TakeFlaw() aufgerufen. Bei Kleidungen
+// koennte man ja per P_SENSITIVE arbeiten oder ein Magier ruft bei Aktionen
+// TakeFlaw() auf.
+varargs void TakeFlaw(mixed dam_types,mapping einfos) {   
+  int quality;
+
+  // Ist der Ruestung eine Qualitaet gesetzt worden, so kann es zu einer
+  // allmaehlichen Beschaedigung der Ruestung kommen. Im if() flaw gleich
+  // hochzaehlen.
+  if ((quality=QueryProp(P_QUALITY)) && !((++flaw) % quality)) {      
+    Damage(1);
+  }
+
+  // Zeitpunkt des ersten Aufrufes festhalten
+  if (!ftime)   
+    ftime=time();
+}
+
+// Die Flaw-Daten koennen natuerlich auch abgerufen werden
+mixed *QueryFlaw() {
+  return ({flaw,ftime,dtime(ftime)});
+}
+
+public status IsClothing() {return 1;}
+
diff --git a/std/clothingHG.c b/std/clothingHG.c
new file mode 100644
index 0000000..fbd265a
--- /dev/null
+++ b/std/clothingHG.c
@@ -0,0 +1,59 @@
+// MorgenGrauen MUDlib
+//
+// armourHG.c -- armour standard object for hats and glasses
+//
+// $Id: armourHG.c,v 1.4 2002/08/19 14:21:31 Rikus Exp $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/clothing";
+
+#define NEED_PROTOTYPES
+#include <thing/commands.h>
+#undef NEED_PROTOTYPES
+
+#include <language.h>
+#include <defines.h>
+
+int setzen(string s);
+
+void create() {
+    ::create();
+    AddCmd(({"setz","setze"}),"setzen");
+}
+
+varargs void doWearMessage( int all ) {
+  if( query_once_interactive(PL) ) {
+    write( "Du setzt " + name(WEN,1) + " auf.\n" );
+  }
+  if (objectp(environment()) && objectp(environment(environment())))
+    tell_room(environment(environment()),
+	capitalize((string)PL->name(WER)) + " setzt " + name(WEN,0) + 
+	" auf.\n");
+}
+
+varargs void doUnwearMessage( object worn_by, int all ) {
+  if( query_once_interactive(worn_by) ) {
+    tell_object(worn_by,  "Du setzt " + name(WEN,1) + " ab.\n" );
+  }
+  tell_room(environment(worn_by), 
+	    (capitalize((string)worn_by->name(WER))) + " setzt " +
+	    name(WEN,0) + " ab.\n", ({worn_by}));
+}
+
+int setzen(string str) {
+  string ob;
+
+  if(!str)
+    return 0;
+  if(sscanf(str, "%s auf", ob)==1 )
+    return _do_wear(ob, 0, 0);
+  if(sscanf(str, "%s ab", ob)==1 )
+    return _do_unwear(ob, 0, 0);
+  return 0;
+}
+
diff --git a/std/clothing_container.c b/std/clothing_container.c
new file mode 100644
index 0000000..ec93b92
--- /dev/null
+++ b/std/clothing_container.c
@@ -0,0 +1,54 @@
+// MorgenGrauen MUDlib
+//,
+// clothing_container.c -- Kleidung, in die man was reinstecken kann
+//
+// $Id: clothing_container.c,v 1.3 2003/08/07 07:25:43 Rikus Exp $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/properties";
+inherit "/std/thing/commands";
+inherit "/std/thing/language";
+inherit "/std/thing/envchk";
+inherit "/std/container/light";
+inherit "/std/container/restrictions";
+inherit "/std/container/inventory";
+inherit "/std/container/items";
+inherit "/std/clothing/moving";
+inherit "/std/clothing/wear";
+inherit "/std/clothing/container_description";
+
+//#define NEED_PROTOTYPES
+
+#include <container.h>
+
+protected void create() {
+  properties::create();
+  commands::create();
+  light::create();
+  container_description::create();
+  restrictions::create();
+  items::create();
+  wear::create();
+  envchk::create();
+  SetProp(P_CONTAINER,1);
+  SetProp(P_PREPOSITION, "in");
+  SetProp(P_SOURCE_PREPOSITION, "aus");
+  SetProp(P_DEST_PREPOSITION, "in");
+  AddId("Ding");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void reset()  {
+  items::reset();
+}
+
+int IsClothing() {return 1;}
+
diff --git a/std/container.c b/std/container.c
new file mode 100644
index 0000000..a77c7fa
--- /dev/null
+++ b/std/container.c
@@ -0,0 +1,67 @@
+// MorgenGrauen MUDlib
+//
+// container.c -- container standard object
+//
+// $Id: container.c 7804 2011-07-10 20:37:52Z Zesstra $
+
+// The most general object class. It defines the really basic functions.
+//
+// This object should understand the properties short, long, info, read_msg,
+// value, weight.
+//
+// weight is given in grams, 1 kilogram (kg) = 1000 grams (g) = 1 old weight
+// unit
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/properties";
+inherit "/std/thing/moving";
+inherit "/std/thing/commands";
+inherit "/std/thing/language";
+inherit "/std/container/description";
+inherit "/std/container/light";
+inherit "/std/container/restrictions";
+inherit "/std/container/inventory";
+inherit "/std/container/items";
+inherit "/std/thing/envchk";
+
+#include <properties.h>
+#include <wizlevels.h>
+#include <defines.h>
+
+protected void create()
+{
+  properties::create();
+  commands::create();
+  light::create();
+  description::create();
+  restrictions::create();
+  items::create();
+  envchk::create();
+  SetProp(P_CONTAINER,1);
+  SetProp(P_PREPOSITION, "in");
+  SetProp(P_SOURCE_PREPOSITION, "aus");
+  SetProp(P_DEST_PREPOSITION, "in");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+/*
+void init() 
+{
+  commands::init();
+  description::init();
+}
+*/
+
+void reset()
+{
+  items::reset();
+}
+
diff --git a/std/container/description.c b/std/container/description.c
new file mode 100644
index 0000000..c4f7b5c
--- /dev/null
+++ b/std/container/description.c
@@ -0,0 +1,103 @@
+// MorgenGrauen MUDlib
+//
+// container/description.c -- standard description for containers
+//
+// $Id: description.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+inherit "/std/thing/description";
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <container.h>
+#include <properties.h>
+#include <defines.h>
+#include <wizlevels.h>
+
+
+void create()
+{
+  ::create();
+  SetProp(P_TRANSPARENT, 1);
+  AddId("Container");
+}
+
+varargs string long(int mode) {
+  string descr, inv_descr;
+
+  descr = process_string(QueryProp(P_LONG));
+  if(!QueryProp(P_TRANSPARENT)) return descr;
+
+  inv_descr = make_invlist(PL, all_inventory(ME), mode );
+  if ( inv_descr != "" )
+    descr += capitalize(QueryPronoun(WER)) + " enthaelt:\n" + inv_descr;
+  return descr;
+}
+
+// flags: 1 - wizard, 2 - don't collect equal objects '
+// flags: 4 - don't append infos for wizards
+private void stringenize(mixed obj, int flags, mixed objs, mixed info)
+{
+  string id, tmp;
+  int idx;
+  
+  if ( (!(flags & 4) && (flags & 1))) {
+    //wenn Magier und Magierinfos angehaengt werden sollen:
+    tmp = capitalize(obj->short()||"")[0..<2]
+        + " ["+object_name(obj)+"]";
+  }
+  else if (obj->QueryProp(P_INVIS)) {
+    //keine Ausgabe bzw. leerer String, wird dann von collect rausgeschmissen
+    tmp="";
+  }
+  else {
+    //Spieler, Magier ohne P_WANTS_TO_LEARN etc.
+    tmp = capitalize(obj->short()||"")[0..<2];
+  }
+  //wenn wizard und 'dont collect equal objects': id ist object_name()
+  if(flags & 3 || living(obj)) 
+    id = object_name(obj);
+  //sonst nehmen wir den Namen der Blueprint ->zusammenfassen der Objekte
+  else
+    id = BLUE_NAME(obj) + tmp;
+  // Anzahl des jeweiligen Objekts (anhand von id) zaehlen.
+  if((idx = member(objs, id)) == -1)
+  {
+    objs += ({ id });
+    info += ({ ({ tmp, 1, obj}) });
+  }
+  else
+    info[idx][1]++;
+}
+
+private string collect(mixed obj)
+{
+  //bekommt ein Array ({name,anzahl,objekt})
+  //Objekte ohne Namen (P_SHORT==0 oder P_INVIS) wegwerfen
+  if(!sizeof(obj[0])) return 0;
+  // Objektname + (Anzahl) zurueckgeben.
+  return obj[0] + (obj[1] > 1 ? " ("+obj[1]+")" : "");
+}
+
+// flags: 1 - return array, 2 - don't collect equal objects '
+// flags: 4 - don't append infos for wizards
+varargs mixed make_invlist(object viewer, mixed inv, int flags)
+{
+  int iswiz;
+  mixed objs, info;
+
+  iswiz = IS_LEARNER( viewer ) && viewer->QueryProp(P_WANTS_TO_LEARN);
+  objs = ({}); info = ({});
+  map(inv, #'stringenize/*'*/, iswiz | (flags & 2) | (flags & 4), &objs, &info);
+  if(flags & 1) return info;
+  inv = map(info, #'collect/*'*/) - ({ 0 });
+  if(!sizeof(inv)) return "";
+  return sprintf("%"+(sizeof(inv) > 6 ? "#" : "=")+"-78s",
+                 implode(inv, "\n")) + "\n";
+}
+
diff --git a/std/container/inventory.c b/std/container/inventory.c
new file mode 100644
index 0000000..0a057b7
--- /dev/null
+++ b/std/container/inventory.c
@@ -0,0 +1,128 @@
+// MorgenGrauen MUDlib
+//
+// container/inventory.c -- Umgang mit besonderen Objekten im Inventory
+//
+// $Id: inventory.c 6379 2007-07-20 22:32:02Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <thing.h>
+#include <properties.h>
+#include <sensitive.h>
+
+#define ME this_object()
+
+void RemoveSensitiveObjectFromList(object ob, string list) {
+  mixed a,b,c;
+  int i,f;
+
+  if (!pointerp(a=QueryProp(SENS_PROP_PREFIX+list)))
+    return;
+  f=1;
+  for (i=sizeof(a)-1;i>=0;i--)
+    if (!pointerp(b=a[i]) ||
+	!sizeof(b) ||
+	!objectp(c=b[0]) ||
+	environment(c)!=ME ||
+	c==ob)
+      a[i]=f=0;
+  if (f)
+    return;
+  a-=({0});
+  if (!sizeof(a))
+    a=0;
+  SetProp(SENS_PROP_PREFIX+list,a);
+}
+
+void RemoveSensitiveObject(object ob) {
+  RemoveSensitiveObjectFromList(ob,SENSITIVE_INVENTORY);
+  RemoveSensitiveObjectFromList(ob,SENSITIVE_INVENTORY_TRIGGER);
+}
+
+varargs void InsertSensitiveObjectToList(object ob, string list, string key,
+					 int threshold, mixed *opt) {
+  mixed a;
+  int i;
+  
+  if (!pointerp(a=QueryProp(SENS_PROP_PREFIX+list)))
+    a=({});
+  for (i=sizeof(a)-1;i>=0;i--)
+    if (a[i][SENS_OBJECT]==ob && a[i][SENS_KEY]==key)
+      return;
+  a+=({({ob,symbol_function(SENS_TRIGGER_PREFIX+list,ob),
+         key,threshold,opt})});
+  SetProp(SENS_PROP_PREFIX+list,a);
+}
+
+void InsertSensitiveObject(object ob, mixed arg) {
+  int i;
+  mixed x;
+  mapping map_ldfied;
+  
+  if (!pointerp(arg))
+    return;
+  for (i=sizeof(arg)-1;i>=0;i--)
+    if (pointerp(x=arg[i]) &&
+	sizeof(x)>=3 &&
+	stringp(x[0]) &&
+	stringp(x[1]) &&
+	intp(x[2])) {
+      InsertSensitiveObjectToList(ob,x[0],x[1],x[2],x[3..]);
+      call_other(ME,SENS_INSERT_PREFIX+x[0],ob,x[1],x[2],x[3..]);
+    }
+}
+
+varargs void insert_sensitive_inv(object ob, string key,
+				 int threshold, mixed *opt) {
+  // Diese Funktion sucht, ob beim Hinzufuegen eines sensitiven Objekts
+  // schon ein Objekt da ist, dass dieses ausloest.
+  // z.B. (dynamit, feuer, 100, opt_dynamit) sorgt fuer
+  // dynamit->trigger_sensitive_inv(fackel,feuer,120,opt_fackel,opt_dynamit)
+  // wenn eine Fackel vorher mit Wert 120 eingetragen war.
+  mixed a,x;
+  int i;
+  
+  if (!pointerp(a=QueryProp(P_SENSITIVE_INVENTORY_TRIGGER)) ||
+      !objectp(ob))
+    return;
+  for (i=sizeof(a)-1;i>=0;i--)
+    if (pointerp(x=a[i]) &&
+	x[SENS_KEY]==key &&
+	x[SENS_THRESHOLD]>threshold && // Ist der Ausloeser gross genug?
+	objectp(x[SENS_OBJECT]) &&
+	environment(x[SENS_OBJECT])==environment(ob))
+      ob->trigger_sensitive_inv(x[SENS_OBJECT],x[SENS_KEY],x[SENS_THRESHOLD],
+				x[SENS_OPT],opt);
+  // Zuerst Trigger-Optionen, dann Optionen des sensitiven Objekts
+}
+
+varargs void insert_sensitive_inv_trigger(object ob, string key,
+					 int threshold, mixed *opt) {
+  // Diese Funktion sucht, ob ein sensitives Objekt im Inventory ist,
+  // das durch dieses Objekt ausgeloest wird.
+  // z.B. (fackel, feuer, 120, opt_fackel) sorgt fuer
+  // dynamit->trigger_sensitive_inv(fackel,feuer,120,opt_fackel,opt_dynamit)
+  // wenn Dynamit mit Wert<120 eingetragen war
+  mixed a,x;
+  int i;
+  
+  if (!pointerp(a=QueryProp(P_SENSITIVE_INVENTORY)) ||
+      !objectp(ob))
+    return;
+  for (i=sizeof(a)-1;i>=0;i--)
+    if (pointerp(x=a[i]) &&
+	x[SENS_KEY]==key &&
+	x[SENS_THRESHOLD]<threshold && // Ausloeser gross genug?
+	objectp(x[SENS_OBJECT]) &&
+	environment(x[SENS_OBJECT])==environment(ob) &&
+	closurep(x[SENS_CLOSURE]))
+      funcall(x[SENS_CLOSURE],
+	      ob,key,threshold,opt,x[SENS_OPT]);
+  // Zuerst Trigger-Optionen, dann Optionen des sensitiven Objekts
+}
diff --git a/std/container/items.c b/std/container/items.c
new file mode 100644
index 0000000..9961bfe
--- /dev/null
+++ b/std/container/items.c
@@ -0,0 +1,256 @@
+// MorgenGrauen MUDlib
+//
+// container/items.c -- creating extra items in room
+//
+// $Id: items.c 8811 2014-05-09 17:30:37Z Zesstra $
+
+// extra items handling
+//
+//	AddItem(string filename, int refresh)
+//	  Clones an item and puts it into the room. <refresh> may be
+//	  on of the following:
+//	  REFRESH_NONE: No refresh done until reboot.
+//	  REFRESH_DESTRUCT: Refresh on reset if item was destructed.
+//	  REFRESH_REMOVE: Refresh on reset if item was removed from room.
+//	  REFRESH_ALWAYS: Create a new clone on every reset.
+//
+// The commands are implemented as properties P_ITEMS as mapping. They are 
+// stored locally (_set_xx) as mapping to speed up the routines 
+// in this module.
+// 
+// Format of key and data in the P_ITEMS mapping:
+//
+// ([ key1 : refresh1; obp1; arr1, ..., keyn : refreshn; obpn; arrn ])
+
+#include <sys_debug.h>
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <rooms.h>
+#include <container.h>
+#undef NEED_PROTOTYPES
+#include <defines.h>
+#include <config.h>
+#include <properties.h>
+#include <moving.h>
+#include <daemon.h>
+
+protected void create()
+{ 
+    Set( P_ITEMS, ({}) );
+    Set( P_ITEMS, SECURED, F_MODE_AS );
+    
+    OBJECTD->QueryObject();  // querying general objects
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+/* Kram zum Aufraeumen von multiplen gleichen Items im Container. */
+private object removeable_ob( object ob )
+{
+    if ( !query_once_interactive(ob) && !living(ob) )
+        return ob;
+
+    return 0;
+}
+
+protected varargs void remove_multiple(int limit, mixed fun)
+{
+    object *inh = all_inventory(this_object()) - ({0});
+
+    inh = filter( inh, #'removeable_ob );
+    foreach(mixed item : QueryProp(P_ITEMS))
+      inh -= ({ item[0] });
+ 
+    if (!stringp(fun) && !closurep(fun))
+      fun = "description_id";
+    inh = unique_array(inh, fun, 0);
+    foreach(mixed arr: inh)
+    {
+      if (sizeof(arr) <= limit)
+        continue;
+      catch(call_other(arr[limit ..], "remove"); publish);
+    }
+}
+
+
+/* Item handling */
+public varargs object AddItem( mixed filename, int refresh, mixed props ) 
+{
+    string file;
+    object ob;
+    int i;
+
+    if( pointerp(filename) ) {
+        for( i = sizeof(filename); i--; )
+            filename[i] = (string)master()->_get_path( filename[i], "?" );
+        
+        file = filename[random( sizeof(filename) )];
+    }
+    else 
+        file = filename = (string)master()->_get_path( filename, "?" );
+    
+    if ( props == 1 )
+        catch(ob = load_object( file); publish);
+    else
+	catch(ob = clone_object(file); publish);
+    
+    if (objectp(ob)) {
+      ob->move( this_object(), M_NOCHECK|M_NO_ATTACK );
+      // mit Absicht keine Pruefung aufs Move, wenns nicht geht, solls 2s
+      // spaeter auf der Ebene buggen, weil praktisch niemand im create() das
+      // Ergebnis vom AddItem() prueft.
+    }
+
+    // In P_ITEMS vermerken, es sei denn, REFRESH_NONE ist gegeben, in dem
+    // Fall ist die Speicherung voellig unnoetig.
+    // TODO: Pruefen, ob das wirklich problemlos geht. Bis dahin werden auch
+    // TODO::REFRESH_NONE-Items vermerkt. (s. clean_up() in /std/room.c)
+    //if (!(refresh & REFRESH_NONE)) {
+      SetProp( P_ITEMS, QueryProp(P_ITEMS) +
+             ({ ({ ob,        // RITEM_OBJECT
+                   filename,  // RITEM_FILE
+                   refresh    // RITEM_REFRESH
+                       }) +
+                    ((mappingp(props) || props == 1) ? ({ props }) : ({})) }) );
+    //}
+
+    if ( ob && mappingp(props) ) 
+        walk_mapping( props, symbol_function( "SetProp", ob ) );
+    
+    return ob;
+}
+
+
+private void ri_rem_ob( object ob )
+{
+    object *inv;
+    int i;
+  
+    if ( objectp(ob) && present(ob, this_object()) ) {
+        inv = deep_inventory(ob);
+        
+        for ( i = sizeof(inv); i--; )
+            if ( inv[i] ) {
+                inv[i]->remove(1);
+                
+                if ( inv[i] )
+                    destruct(inv[i]);
+            }
+        
+        ob->remove(1);
+        
+        if ( ob )
+            destruct(ob);
+    }
+}
+
+
+private int ri_filter( mixed *ritem, mixed file )
+{
+    object ob, *inv;
+    int i;
+  
+    ob = ritem[RITEM_OBJECT];
+  
+    if ( stringp(file) && ritem[RITEM_FILE] == file )
+        return ri_rem_ob(ob), 0;
+    else if ( pointerp(ritem[RITEM_FILE]) && pointerp(file) &&
+              sizeof(file & ritem[RITEM_FILE]) == sizeof(ritem[RITEM_FILE]) )
+        return ri_rem_ob(ob), 0;
+
+    return 1;
+}
+
+
+public void RemoveItem( mixed filename )
+{
+    mixed *items;
+    int i;
+
+    if ( !pointerp(items = QueryProp(P_ITEMS)) || !sizeof(items) )
+        return;
+
+    if ( pointerp(filename) )
+        for ( i = sizeof(filename); i--; )
+            filename[i] = (string)master()->_get_path( filename[i], "?" );
+    else
+        filename = (string)master()->_get_path( filename, "?" );
+  
+    SetProp( P_ITEMS, filter( items, #'ri_filter/*'*/, filename ) );
+}
+
+
+private mixed _do_refresh( mixed item )
+{
+    string file;
+    object ob;
+
+    if ( !pointerp(item) || item[RITEM_REFRESH] == REFRESH_NONE )
+        return item;
+
+    if ( pointerp(item[RITEM_FILE]) ) 
+        file = item[RITEM_FILE][random( sizeof(item[RITEM_FILE]) )];
+    else
+        file = item[RITEM_FILE];
+
+    switch( item[RITEM_REFRESH] ){
+    case REFRESH_MOVE_HOME:
+        if ( objectp(item[RITEM_OBJECT]) &&
+             environment(item[RITEM_OBJECT]) != ME ) {
+            item[RITEM_OBJECT]->move( ME, M_GO|M_NO_ATTACK );
+            break;
+        }
+        
+    // fall through
+    case REFRESH_DESTRUCT:
+        if ( objectp(item[RITEM_OBJECT]) ) 
+            break; // else FALL THROUGH
+        
+    case REFRESH_REMOVE:
+        if ( objectp(item[RITEM_OBJECT]) &&
+             environment(item[RITEM_OBJECT]) == ME )
+            break; // else FALL THROUGH
+        
+    default:
+        if ( sizeof(item) > RITEM_PROPS && item[RITEM_PROPS] == 1 ) {
+            ob = load_object(file);
+        }
+        else
+            ob = clone_object(file);
+
+        ob->move( ME, M_NOCHECK|M_NO_ATTACK );
+        break;
+    }
+    
+    if ( ob ){
+        item[RITEM_OBJECT] = ob;
+        
+        if ( sizeof(item) > RITEM_PROPS && mappingp(item[RITEM_PROPS]) )
+            walk_mapping( item[RITEM_PROPS], symbol_function( "SetProp", ob ) );
+    }
+    
+    return item;
+}
+
+
+// reset handling: check how the items should be refreshed.
+void reset() 
+{
+    mixed *items;
+
+    if ( !pointerp(items = QueryProp(P_ITEMS)) ){
+        SetProp( P_ITEMS, ({}) );
+        return;
+    }
+    
+    SetProp( P_ITEMS, map( items, #'_do_refresh/*'*/ ) - ({0}) );
+}
+
diff --git a/std/container/light.c b/std/container/light.c
new file mode 100644
index 0000000..435400f
--- /dev/null
+++ b/std/container/light.c
@@ -0,0 +1,149 @@
+// MorgenGrauen MUDlib
+//
+// container/light.c -- Lichtsystem fuer Container
+//
+// $Id: description.c 6986 2008-08-22 21:32:15Z Zesstra $
+
+inherit "/std/thing/light";
+
+#pragma strict_types
+#pragma save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <container.h>
+#include <properties.h>
+
+// BTW: _query_last_content_change() sollte bloss nie auch -1 fuer
+// uninitialisiert zurueckliefern...
+private nosave int last_light_calc = -1;
+
+protected void create()
+{
+  ::create();
+  // wieviel Licht schluckt der Container _muss_ > 0 sein!
+  SetProp(P_LIGHT_TRANSPARENCY, 2);
+}
+
+protected void create_super() {set_next_reset(-1);}
+
+
+static mixed _query_light_transparency()
+{
+   if (QueryProp(P_TRANSPARENT))
+     return Query(P_LIGHT_TRANSPARENCY,F_VALUE);
+   return 999;
+}
+
+public int add_light_sources(int * sources) {
+  float light = 0.0;
+  //printf("als(%O): %O\n",this_object(),sources);
+  // Alle Lichtlevel werden als Exponent von e aufgefasst und die Summe dieser
+  // Potenzen gebildet.
+  foreach(int l : sources) {
+     if (l > 0)
+       light += exp(l);
+     else if (l < 0)
+       light -= exp(abs(l));
+     // l==0 muss ignoriert werden
+  }
+  // anschliessend wird aus dieser Summe der natuerliche Logarithmus
+  // berechnet.
+  // auf diese Weise haben hoehere Lichtlevel ueberproportional viel Einfluss
+  // auf die Helligkeit im Raum, kleine Lichtlevel fallen aber nicht komplett
+  // raus.
+  if (light > 0) {
+      light = log(light);
+  }
+  else if (light < 0) {
+      light = -log(abs(light));
+  }
+
+  //printf("als(): %O\n",light);
+  // runden und als int zurueckgeben.
+  if (light >= 0)
+    return to_int(light+0.5);
+  //else
+  return to_int(light-0.5);
+}
+
+static int _query_total_light()
+{
+  int totallight;
+
+  if ( _query_last_content_change() == last_light_calc )
+    return Query(P_TOTAL_LIGHT);
+
+  // eigenes P_LIGHT und P_TOTAL_LIGHT der enthaltenen Objekte verrechnen
+  int intlight = add_light_sources(
+      all_inventory()->QueryProp(P_TOTAL_LIGHT) + ({QueryProp(P_LIGHT)}));
+  // P_INT_LIGHT (gerundet) abspeichern
+  Set(P_INT_LIGHT, intlight, F_VALUE);
+
+  // Licht nach aussen muss Containertransparenz beruecksichtigen.
+  if (intlight > 0) {
+    totallight = intlight - QueryProp(P_LIGHT_TRANSPARENCY);
+    if (totallight < 0)
+      totallight = 0;
+  }
+  else {
+    totallight = intlight + QueryProp(P_LIGHT_TRANSPARENCY);
+    if (totallight > 0)
+      totallight = 0;
+  }
+  //printf("_query_total_light(%O): %O\n",this_object(),totallight);
+
+  last_light_calc = _query_last_content_change();
+
+  // Runden und Konversion nach int nicht vergessen...
+  if (totallight > 0)
+    return SetProp(P_TOTAL_LIGHT, to_int(totallight + 0.5));
+  //else
+  return SetProp(P_TOTAL_LIGHT, to_int(totallight - 0.5));
+}
+
+static int _query_int_light()
+{
+   int intlight, envlight;
+
+   if ( _query_last_content_change() != last_light_calc )
+       _query_total_light();
+
+   // P_INT_LIGHT des environments kann sich natuerlich aendern _ohne_ das
+   // etwas an einem container geaendert wird. Daher Auswertung jedes mal
+   // neu aktualisieren.
+   if (!environment()
+       || !(envlight=(int)environment()->QueryProp(P_INT_LIGHT)))
+      return Query(P_INT_LIGHT, F_VALUE);
+   else {
+      intlight = Query(P_INT_LIGHT, F_VALUE);
+      int transparency = QueryProp(P_LIGHT_TRANSPARENCY);
+
+      // bei einem transparentem Container kann natuerlich das Licht des
+      // environments den Container auch ausleuchten....
+      // (Anmerkung: in dem Licht von draussen ist unser Lichtanteil
+      // natuerlich auch drin, gedaempft um die Transparenz (raus und rein).
+      // Ich vernachlaessige das.)
+      if (abs(envlight) > transparency) {
+        if (envlight > 0)
+            envlight -= transparency;
+        else
+            envlight += transparency;
+        // jetzt Licht von aussen und innen verrechnen
+        intlight = add_light_sources(({intlight, envlight}));
+      }
+      // else: nur P_INT_LIGHT, Licht des Environments keine Bedeutung
+   }
+   return intlight;
+}
+
+static int _set_light(int light)
+{
+  last_light_calc=-1;
+
+  return ::_set_light(light);
+}
+
diff --git a/std/container/moneyhandler.c b/std/container/moneyhandler.c
new file mode 100644
index 0000000..ae25453
--- /dev/null
+++ b/std/container/moneyhandler.c
@@ -0,0 +1,53 @@
+// MorgenGrauen MUDlib
+//
+// container/moneyhandler.c -- money handler for container
+//
+// $Id: moneyhandler.c 6738 2008-02-19 18:46:14Z Humni $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <moving.h>
+#include <money.h>
+
+public int AddMoney( int amount )
+{
+  object ob;
+  int ret;
+
+  if ( !amount )
+    return 1;
+    
+  ob = clone_object( GELD );
+  ob->SetProp( P_AMOUNT, amount );
+
+  ret=ob->move( this_object(), M_PUT|M_MOVE_ALL );
+  // Bei fehlerhaftem move ggf. wieder zerstoeren.
+  if ((ret != MOVE_OK)
+      && ob)
+  {
+    ob->remove(1);
+  }
+  return ret;
+}
+
+public int QueryMoney()
+{
+  object money;
+  int geld;
+
+  if ( money = present_clone(GELD, this_object()) )
+    geld = money->QueryProp(P_AMOUNT);
+
+  if ( money = present(GELDBOERSE_MIT_GELD, this_object()) )
+    geld += money->QueryProp(P_AMOUNT);
+
+  return geld;
+}
diff --git a/std/container/restrictions.c b/std/container/restrictions.c
new file mode 100644
index 0000000..3da5c9d
--- /dev/null
+++ b/std/container/restrictions.c
@@ -0,0 +1,480 @@
+// MorgenGrauen MUDlib
+//
+// container/restrictions.c -- container restrictions
+//
+// $Id: restrictions.c 9020 2015-01-10 21:49:41Z Zesstra $
+
+// This is a simple container to put objects in. It defines all functions
+// which are necessary to describe an object which can be filled with
+// other things.
+//
+// It will support restrictions for volume, weight etc.
+//
+// The following properties are defined:
+// P_MAX_WEIGHT - maximum weight which container can carry
+// P_TOTAL_WEIGHT - total weight, with contents.
+// P_WEIGHT - the weight of the container without contents
+//
+// Functions for manipulation of weight
+// MayAddWeight(weight) - Can <weight> be inserted?
+// AddWeight(weight) - Add an amount of <weight>
+//
+// IMPORTANT: unit equals 1 gram
+
+#pragma strict_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include "/sys/thing/properties.h"
+#include <properties.h>
+#include <defines.h>
+#include <thing/description.h>
+
+// local properties prototypes
+static int _query_total_weight();
+
+
+private nosave int LastWeightCalc, contents, LastObjectCount, objcount;
+private nosave int last_content_change;
+
+
+void create()
+{
+    Set( P_WEIGHT_PERCENT, 50 );
+    Set( P_TOTAL_WEIGHT, NOSETMETHOD, F_SET_METHOD );
+    Set( P_TOTAL_WEIGHT, PROTECTED, F_MODE );
+    Set( P_MAX_OBJECTS, 100 );
+    Set( P_TOTAL_OBJECTS, NOSETMETHOD, F_SET_METHOD );
+    Set( P_TOTAL_OBJECTS, PROTECTED, F_MODE );
+    LastWeightCalc = -1;
+    LastObjectCount = -1;
+}
+
+
+static int _query_last_content_change()
+{
+    return last_content_change;
+}
+
+
+// absichtlich public, da es von der simul_efun.c *direkt* aufgerufen
+// wird aus Performancegruenden!
+public varargs int _set_last_content_change( mixed wert )
+{
+    // Der uebergebene Wert ist unerheblich. Die Property
+    // P_LAST_CONTENT_CHANGE wird so oder so bei jedem SetProp()-
+    // Aufruf um eins erhoeht, um der 2s-"Unschaerfe" von time()
+    // aus dem Weg zu gehen.
+    return ++last_content_change;
+}
+
+
+int query_weight_contents()
+{
+    object *objs;
+    int w, w2, i;
+
+    if ( last_content_change == LastWeightCalc )
+        return contents;
+    
+    w = 0;
+    objs = all_inventory(this_object());
+    i = sizeof(objs);
+    
+    while ( i-- ){
+        if ( !(w2 = (int)objs[i]->QueryProp(P_TOTAL_WEIGHT)) )
+            w2 = (int)objs[i]->QueryProp(P_WEIGHT);
+        w += w2;
+    }
+    
+    LastWeightCalc = last_content_change;
+    return contents = w;
+}
+
+
+static int _query_total_objects()
+{
+  if ( last_content_change == LastObjectCount )
+      return objcount;
+
+  objcount = sizeof( filter_objects( all_inventory(), "short" ) );
+  LastObjectCount = last_content_change;
+  return objcount;
+}
+
+
+// diese Funktion sollte von Raeumen natuerlich ueberschrieben werden...
+int MayAddObject( object ob )
+{
+    if (ob) {
+        if ( !ob->short() )
+            return 1; // invis-Objekte duerfen immer
+        
+        if ( ob == ME || environment(ob) == ME)
+            return 1; // objekt ist schon drin
+    }
+    
+    return (QueryProp(P_TOTAL_OBJECTS) < QueryProp(P_MAX_OBJECTS));
+}
+
+
+#define ENV environment
+#define PO previous_object()
+
+int MayAddWeight( int w )
+{
+    int nw, aw;
+
+    // was nix wiegt, passt
+    if ( w <= 0 )
+        return 0;
+
+    nw = w; // Gewicht im neuen Container
+    aw = 0; // Gewicht fuer das env()
+
+    // Von Raum in Behaelter im Spieler: Container sieht volles Gewicht (nw=w),
+    // Check im env() (Spieler) mit reduziertem Gewicht.
+    if ( ENV() && PO && ENV(PO) != ENV() )
+        aw = QueryProp(P_WEIGHT_PERCENT) * w / 100 || 1;
+
+    if ( PO && ENV(PO) && ENV(ENV(PO)) ){
+        // Wenn das Objekt ein env() hochbewegt wird, wird das Gewicht im alten
+        // Container abgezogen. Weiterer Check im env() ist unnoetig (aw=0).
+        if ( ENV(ENV(PO)) == ME )
+            nw = w - (int)ENV(PO)->QueryProp(P_WEIGHT_PERCENT) * w / 100;
+        // Eine Ebene tiefer bewegen: Test im neuen Container mit unveraendertem
+        // Gewicht; Check im env() mit um Gewichtsreduktion verringertem Gewicht
+        else if ( present( ME, ENV(PO) ) )
+            aw = QueryProp(P_WEIGHT_PERCENT) * w / 100 - w;
+        // Auf derselben Ebene verschieben (von Paket in Beutel):
+        // Neuer Container sieht volles Gewicht (nw=w), Check im env() mit
+        // Differenz aus Gewicht in Container1 und in Container2.
+        else if ( ENV(ENV(ENV(PO))) && ENV() == ENV(ENV(PO)) )
+            aw = QueryProp(P_WEIGHT_PERCENT) * w / 100
+                - ((int)ENV(PO)->QueryProp(P_WEIGHT_PERCENT) * w / 100 || 1);
+    }
+
+    if ( query_weight_contents() + nw > QueryProp(P_MAX_WEIGHT) )
+        // Container kann Gewicht nicht mehr aufnehmen
+        return -1;
+    
+    if ( aw && ENV()->MayAddWeight(aw) < 0 )
+        // Umgebung des Containers kann Gewicht nicht mehr aufnehmen
+        return -2;
+    
+    return 0;
+}
+
+
+/* Redefine PreventInsert() to prevent inserting of special objects. If
+ * a value greater 0 is returned the object ob can't be inserted in the
+ * container.
+ */
+public int PreventInsert( object ob ) { return 0; }
+public int PreventLeave( object ob, mixed dest ) { return 0; }
+public int PreventInsertLiving( object ob ) { return 0; }
+public int PreventLeaveLiving( object ob, mixed dest ) { return 0; }
+public void NotifyInsert(object ob, object oldenv) { }
+
+// **** local property methods
+static int _query_total_weight() 
+{ 
+    return QueryProp(P_WEIGHT) +
+        (QueryProp(P_WEIGHT_PERCENT) * query_weight_contents() / 100); 
+}
+
+
+// Hilfsfunktion
+static int _behalten( object ob, string uid )
+{
+    return (string)ob->QueryProp(P_KEEP_ON_SELL) == uid;
+}
+
+
+/*
+ *
+ * get a list of all contained objects matching a complex description
+ *
+ */
+
+#define POS_INVERS   0x01  /* nur zur funktionsinternen Verwendung */
+#define POS_LETZTES  0x02  /* nur zur funktionsinternen Verwendung */
+
+object *present_objects( string complex_desc )
+{
+    int i;          // Zaehlervariable
+    int meth;       // 0x01 = invers?,  0x02 = letztes?
+    object ob;      // einzelnes temporaeres Objekt
+    object *obs;    // liste der ausgewaehlten Objekte
+    object *erg;    // zum sammeln bis es nachher zurueckgegeben wird...
+    string *strlst; // liste aller Gruppen die mit AND verknuepft sind
+    object haufen;
+
+    strlst = allocate(2);
+    
+    if ( sscanf( complex_desc, "%s ausser %s", strlst[0], strlst[1]) == 2 ){
+        erg = present_objects( strlst[0] );
+        obs = present_objects( strlst[1] );
+        return erg-obs;
+    }
+    
+   strlst = explode( complex_desc, " und " );
+   erg = ({});
+   
+   for ( i = sizeof(strlst); i--; )
+   {
+       complex_desc = strlst[i];
+       // auf letzte/letztes/letzten pruefen...
+       if ( complex_desc[0..5] == "letzte" ){
+           switch( complex_desc[6..6] ){
+           case " ":
+               meth |= POS_LETZTES;
+               complex_desc = complex_desc[7..];
+               break;
+           case "s":
+           case "n":
+           case "m":
+           case "r":
+               if ( complex_desc[7..7] != " " )
+                   break;
+               meth |= POS_LETZTES;
+               complex_desc = complex_desc[8..];
+               break;
+           default:
+           }
+       }
+
+       // auf verneinung pruefen
+       if ( complex_desc[0..5] == "nicht " ){
+           meth |= POS_INVERS;
+           complex_desc = complex_desc[6..];
+       }
+
+       obs=({});
+       // nun nach Main-Ids (Gruppen) suchen...
+       if ( meth & POS_LETZTES )
+       { // geht es nur um den letzten Gegenstand?
+           switch( complex_desc ){
+           case "waffe":
+               obs = filter_objects( all_inventory(), "QueryProp",
+                                     P_WEAPON_TYPE );
+               break;
+               
+           case "ruestung":
+               obs = filter_objects( all_inventory(), "QueryProp",
+                                     P_ARMOUR_TYPE );
+               break;
+          
+           case "kleidung":
+               obs = filter_objects( all_inventory(), "IsClothing");
+               break;
+               
+           case "verschiedenem":
+           case "verschiedenes":
+               obs = all_inventory();
+               obs -= filter_objects( obs, "QueryProp", P_WEAPON_TYPE );
+               obs -= filter_objects( obs, "QueryProp", P_ARMOUR_TYPE );
+               obs -= filter_objects( obs, "IsClothing");
+               obs -= filter( obs, #'living/*'*/ );
+               break;
+        
+           case "eigenes":
+           case "meins":
+               if (objectp(haufen=present("\nhaufen "+
+                     this_player()->name(WEM)))) {
+                  obs = all_inventory(haufen);
+               }
+               // kein break;, Fall-through!
+           case "behaltenem":
+           case "behaltenes":
+               obs += filter( all_inventory(), "_behalten", ME,
+                                   getuid(this_player() || previous_object()) );
+
+               obs += (QueryProp(P_ARMOURS) || ({}))
+                   + ({ QueryProp(P_WEAPON) }) - ({ 0 });
+                   break;
+                   
+           case "gegenstand":
+               obs = all_inventory() -
+                   filter( all_inventory(), #'living/*'*/ );
+               break;
+               
+           default:
+               obs = filter_objects( all_inventory(), "id", complex_desc );
+           }
+
+           // unsichtbare objekte entfernen
+           obs = filter_objects( obs, "short" );
+
+           // letzten Gegenstand raussuchen
+           obs = obs[0..0]; 
+       } // if (letzter Gegenstand)
+       else
+       { // ganze Gruppen und nicht nur das letzte Objekt
+           switch ( complex_desc )
+           {
+           case "allem":
+           case "alles":
+           case "jeden gegenstand":
+           case "jedem gegenstand":
+           case "gegenstaende":
+           case "alle gegenstaende":
+           case "allen gegenstaenden":
+               if ( meth & POS_INVERS )
+                   continue; // alles nicht = nichts :)
+               
+               obs = all_inventory() -
+                   filter( all_inventory(), #'living/*'*/ );
+               break;
+               
+           case "waffen":
+           case "jede waffe":
+           case "jeder waffe":
+           case "alle waffen":
+           case "allen waffen":
+               obs = filter_objects( all_inventory(), "QueryProp",
+                                     P_WEAPON_TYPE );
+               break;
+               
+           case "ruestungen":
+           case "jede ruestung":
+           case "jeder ruestung":
+           case "alle ruestungen":
+           case "allen ruestungen":
+               obs = filter_objects( all_inventory(), "QueryProp",
+                                     P_ARMOUR_TYPE );
+               break;
+ 
+           case "kleidung":
+           case "jede kleidung":
+           case "jeder kleidung":
+           case "alle kleidung":
+           case "allen kleidung":
+               obs = filter_objects( all_inventory(), "IsClothing");
+               break;
+              
+           case "gegenstand":
+               obs = filter_objects( all_inventory() -
+                                     filter( all_inventory(),
+                                                   #'living/*'*/ ),
+                                     "short" )[0..0];
+               break;
+               
+           case "verschiedenem":
+           case "verschiedenes":
+               obs = all_inventory();
+               obs -= filter_objects( obs, "QueryProp", P_WEAPON_TYPE );
+               obs -= filter_objects( obs, "QueryProp", P_ARMOUR_TYPE );
+               obs -= filter_objects( obs, "IsClothing");
+               obs -= filter( obs, #'living/*'*/ );
+               break;
+       
+           case "eigenes":
+           case "eigenem":
+           case "eigenen":
+           case "meins":
+           case "alles eigene":
+               if (objectp(haufen=present("\nhaufen "+
+                       this_player()->name(WEM)))) {
+                  obs = all_inventory(haufen);
+               }
+               // kein break;, Fall-through!
+           case "behaltenem":
+           case "behaltenen":
+           case "behaltenes":
+           case "alles behaltene":
+               obs += filter( all_inventory(), "_behalten", ME,
+                                   getuid(this_player() || previous_object()) );
+
+               obs += (QueryProp(P_ARMOURS) || ({}))
+                   + ({ QueryProp(P_WEAPON) }) - ({ 0 });
+                   break;
+                   
+           default:
+               if ( complex_desc[0..3] == "jede" ||
+                    complex_desc[0..4] == "alle ")
+               {
+                   if ( complex_desc[4..4] == " " )
+                   {
+                       obs = filter_objects( all_inventory(), "id",
+                                             complex_desc[5..] );
+                       break;
+                   }
+                   else
+                   {
+                       switch( complex_desc[4..5] )
+                       {
+                       case "m ":
+                       case "r ":
+                       case "n ":
+                       case "s ":
+                           obs = filter_objects( all_inventory(), "id",
+                                                 complex_desc[6..] );
+                           break;
+                           
+                       default:
+                           obs = 0;
+                       }
+                       if (obs)
+                           break;
+                   }
+               }
+
+               // Der Normalfall: einzelne ID...
+               ob = present( complex_desc, ME );
+               // Achtung: dieser Teil setzt das for() fort (continue) und
+               // umgeht dabei die Pruefung auf Sichtbarkeit nach dem Ende vom
+               // switch(). Aus diesem Grunde muss hier selber geprueft
+               // werden.
+               if ( meth & POS_INVERS )
+               {
+                   if ( ob && ob != ME )
+                       erg += (filter_objects( all_inventory(), "short" )
+                               - ({ ob }) );
+                   else
+                       erg += filter_objects( all_inventory(), "short" );
+               }
+               else if ( ob && ob != ME && !ob->QueryProp(P_INVIS) )
+                   erg += ({ ob });   //Normalfall: einzelne ID
+
+               continue;
+
+           }  // switch
+           // unsichtbare objekte entfernen
+           obs = filter_objects( obs, "short" ); 
+       } // else
+
+       if ( meth & POS_INVERS )
+           erg += ( filter_objects( all_inventory(), "short" ) - obs );
+       else
+           erg += obs;
+   } // for
+   return erg;
+}
+
+/*
+ * returns a list of all found objects inside itself
+ * may call same function in objects inside
+ *
+ * Funktion wird nicht mehr von put_and_get aufgerufen, stattdessen wird
+ * direkt present_objects benutzt!
+ */ 
+object *locate_objects( string complex_desc, int info ) {
+    string was, wo;
+
+    if ( sscanf( complex_desc, "%s in %s", was, wo ) == 2 ){
+      object *found_obs = ({});
+      foreach(object invob: present_objects(wo)) {
+        // || ({}) weil invob ein Objekt ohne locate_objects() sein koennte.
+        found_obs += (object *)invob->locate_objects( was, info) || ({});
+      }
+      return found_obs;
+    }
+    // kein "in" gefunden
+    return present_objects( complex_desc );
+}
+
diff --git a/std/corpse.c b/std/corpse.c
new file mode 100644
index 0000000..8e4d8ae
--- /dev/null
+++ b/std/corpse.c
@@ -0,0 +1,727 @@
+// MorgenGrauen MUDlib               
+//
+// corpse.c -- corpse standard object
+//
+// $Id: corpse.c 9391 2015-12-07 22:28:53Z Arathorn $
+
+// A corpse.
+// 
+// Ekeltexte by Boing
+//
+// This is a decaying corpse. It is created automatically
+// when a player or monster dies.
+//
+// Erweiterung Raschaua:
+// - Kopieren von P_CLASS
+// - Teilweises aufessen
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma pedantic
+
+//#define NEED_PROTOTYPES
+
+inherit "std/container";
+
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <living/life.h>
+#include "/secure/scoremaster.h"
+
+#define CORPSE_OBJ "/std/corpse.c"
+#define PILE_OBJ "/std/pile.c"
+
+nosave int _decay = 4;
+nosave string _name = "Unbekannt";
+nosave object moerder;
+private nosave int gespottet = 0;
+nosave int nahrung_gesamt = 1;
+nosave int nahrung_aktuell = 1;
+// Spielerleiche?
+private nosave status spielerleiche;
+
+void Identify( object ob );
+void do_decay();
+int QueryDecay();
+static int mampf( string str );
+static int spott(string str);
+void ChannelMessageJeer( mixed sender, string text, int flag );
+object _channel( object ob );
+void transform_into_pile();
+
+void create()
+{
+  if (object_name(ME) == __FILE__[0..<3]) {
+    set_next_reset(-1);
+  }
+  ::create();
+  if( clonep(this_object()) )
+  {
+    AddId(({ "leiche","\nleiche","rest","ueberrest"}));
+    SetProp( P_GENDER, FEMALE );
+    SetProp( P_NAME, "Leiche" );
+    SetProp( P_MATERIAL, ([ MAT_MISC_DEAD: 100 ]) );
+    SetProp( P_CORPSE_DECAY_TIME, 30 );
+    SetProp(P_NO_SCORE,1);
+    AddCmd(({"iss","verspeise"}),"mampf");
+    AddCmd("spotte","spott");
+    SetProp(P_ARTICLE, 1);
+  }
+  else
+    SetProp( P_ARTICLE, 0 );
+}
+
+/* Damit die Leiche nicht voll wird... */
+int MayAddWeight( int weight ) { return 0; }
+int MayAddObject( object ob ) { return 1; }
+
+
+/* Humni, 2004-06-16: Entstatict */
+string _query_leichen_name()
+{   return _name;   }
+
+/* Uebernehme den Namen von ob */
+void Identify( object ob )
+{
+    closure cl;
+    int i;
+    string info;
+
+    spielerleiche = query_once_interactive(ob);
+    cl=symbol_function("QueryProp", ob);
+    _name = (string) ob->name(WESSEN,0);
+    SetProp( P_SHORT, "Die Leiche "+ _name );
+    SetProp( P_LONG, "Du siehst die sterblichen Ueberreste "+ _name + ".\n" );
+    _decay = 4;
+    SetProp( P_ORIG_NAME, to_string(ob->name(RAW))      );
+    SetProp( P_PILE_NAME, to_string(ob->name(WEM))      );
+    AddId( "\nleiche "+ QueryProp(P_PILE_NAME)          );
+    SetProp( P_KILLER,        (object)   funcall(cl, P_KILLER)        );
+    SetProp( P_ATTRIBUTES,    (mapping)  funcall(cl, P_ATTRIBUTES)    );
+    SetProp( P_LAST_DAMTYPES, (string *) funcall(cl, P_LAST_DAMTYPES) );
+    SetProp( P_XP,     to_int(funcall(cl, P_XP))        );
+    SetProp( P_SIZE,   to_int(funcall(cl, P_SIZE))      );
+    SetProp( P_LEVEL,  to_int(funcall(cl, P_LEVEL))     );
+    SetProp( P_MAX_HP, to_int(funcall(cl, P_MAX_HP))    );
+    SetProp( P_RACE,   to_string(funcall(cl, P_RACE))   );
+    SetProp( P_CLASS,  (string *)(funcall(cl, P_CLASS)) );
+    SetProp( P_HANDS,  (mixed *) funcall(cl, P_HANDS)   );
+    SetProp( P_WEIGHT, (mixed *) funcall(cl, P_WEIGHT)  );
+    SetProp( P_ALIGN,  to_int(funcall(cl, P_ALIGN))     );
+    SetProp( P_ORIG_FILE_NAME, object_name(ob)          );
+    SetProp( P_HEAL,   to_int(funcall(cl, P_HEAL))      );
+    
+    // Saettigung der Leiche berechnen
+    // P_WEIGHT und P_SIZE werden verwendet, Mittel bilden
+    // Ein Saettigungspunkt fuer (konservative Rechnung):
+    //           5 kg
+    //          12 cm
+    // Beispiele:
+    //          Mensch, 1.80 m gross, 75 kg schwer:
+    //          180/12 + 75/5 = 15 + 15 = 30
+    //          Kuh, 1.50 m gross, 500 kg schwer:
+    //          150/12 + 500/5 = 12 + 100 = 112
+    //          Drache, 5 m gross, 1500 kg schwer:
+    //          500/12 + 1500/5 = 41 + 300 = 341
+    nahrung_gesamt = QueryProp(P_WEIGHT)/5000 + QueryProp(P_SIZE)/12;
+    if (nahrung_gesamt <= 0)
+      nahrung_gesamt = 1;
+    nahrung_aktuell = nahrung_gesamt;
+
+    if ( intp(i = funcall(cl, P_CORPSE_DECAY_TIME)) && i > 2 )
+        SetProp( P_CORPSE_DECAY_TIME, i );
+
+    call_out( "do_decay", QueryProp(P_CORPSE_DECAY_TIME) );
+
+    if ( !query_once_interactive(ob) )
+        moerder = (object) "/std/corpse"->_channel(ob);
+}
+
+public int IsPlayerCorpse() { return spielerleiche; }
+
+public string QueryHealInfo()
+{
+  string info = "Solltest Du auf die aberwitzige Idee verfallen, diese "
+    "Leiche zu essen, ";
+  switch( QueryProp(P_HEAL) )
+  {
+    case -__INT_MAX__..-100:
+      info += "wird Dir das erheblichen Schaden zufuegen.";
+      break;
+    case -99..-11:
+      info += "ist das ausgesprochen ungesund fuer Dich.";
+      break;
+    case -10..-1:
+      info += "wird Dir sicher ein wenig uebel werden.";
+      break;
+    case 0..5:
+      info += "wird Dir nichts schlimmes passieren.";
+      break;
+    case 6..__INT_MAX__:
+      info += "koenntest Du sogar ein wenig geheilt werden.";
+      break;
+  }
+  return info;
+}
+
+private string knabber_text()
+{
+  string txt;
+  switch (nahrung_aktuell * 4 / nahrung_gesamt) {
+  case 4:
+    // Leiche noch komplett
+    txt = "";
+    break;
+  case 3:
+    // noch maximal 99%
+    txt = " mit Knabberspuren uebersaete";
+    break;
+  case 2:
+    // noch maximal 74%
+    txt = " angefressene";
+    break;
+  case 1:
+    // noch maximal 49%
+    txt = " schon halb aufgefutterte";
+    break;
+  default:
+    // noch maximal 24%
+    txt = " total abgenagte";
+    break;
+  }
+  return txt;
+}
+
+static string _query_short()
+{
+  string txt;
+  switch(_decay) {
+  case 4:
+    txt = "Die"+knabber_text()+" Leiche ";
+    break;
+  case 3:
+    txt = "Die bereits ziemlich stinkende"+knabber_text()+" Leiche ";
+    break;
+  case 2:
+    txt = "Die schimmelnde und halbverweste"+knabber_text()+" Leiche ";
+    break;
+  case 1:
+    txt = "Die verfaulten"+(sizeof(knabber_text()) ?
+                            "n":"")+" Einzelteile ";
+    break;
+  }
+  txt += _name;
+  return txt;
+}
+
+void do_decay()
+{
+    _decay -= 1;
+    
+    if ( _decay > 0 ) 
+        {
+          if ( (_decay == 2) && (QueryProp( P_HEAL ) >= -4) )
+            SetProp( P_HEAL, -4 );
+            call_out( "do_decay", QueryProp(P_CORPSE_DECAY_TIME) );
+            return;
+        }
+   
+    transform_into_pile();
+    remove();
+}
+
+/* Haeh? Das move ins env macht der Master im prepare_destruct() doch eh? */
+/*
+varargs int remove()
+{
+    // units bekommen eine Chance zur Vereinigung...
+    filter_objects( all_inventory(ME), "move", environment(ME), M_SILENT );
+    return ::remove();
+}
+*/
+
+int QueryDecay() { return _decay; }
+
+static int * _query_nahrung()
+{   return ({nahrung_aktuell, nahrung_gesamt});   }
+
+private string mampf_heilung(int wieviel)
+{
+  int heal;
+  string msg;
+  heal = QueryProp(P_HEAL);
+  // Anteil an Gesamtheilung ausrechnen
+  heal = heal * wieviel / nahrung_gesamt;
+  if ( ( heal ) < 0 ) {
+    // 
+    this_player()->do_damage( random(-heal), this_object() );
+    msg = "Buah, diese Leiche war zweifellos nicht besonders gesund.\n";
+  } else {
+    this_player()->heal_self( random(heal) );
+    msg = "Hmmm, lecker!\n";
+  }
+  return msg;
+}
+
+static int mampf( string str )
+{
+    int gegessen;
+
+    _notify_fail("Was moechtest Du essen?\n");
+    
+    if ( !str || !id(str) )
+        return 0;
+
+    if (this_player()->QueryProp(P_GHOST))
+    {
+	_notify_fail("Das wuerde durch Dich hindurch fallen.\n");
+	return 0;
+    }
+
+    // Das folgende ist nicht ganz sauber, da die Staerke bei eat_food nicht
+    // unbedingt dem Saettigungswert entsprechen muss (wegen FOOD_VALUE).
+    gegessen = (int) this_player()->QueryProp(P_MAX_FOOD) -
+      (int) this_player()->QueryProp(P_FOOD);
+    if (gegessen <= 0) {
+      // Spieler ist proppenvoll, Meldung ausgeben
+      gegessen = 0;
+      write("Du bekommst von der Leiche nicht einen Bissen mehr runter.\n");
+    } else if (gegessen >= nahrung_aktuell) {
+      // spieler kann die gesamte Leiche essen, also entfernen.
+      this_player()->eat_food(nahrung_aktuell);
+      // Verdammt. eat_food() kann TP umgebracht haben und im Falle eines NPC
+      // ist der dann weg.
+      if (objectp(this_player())) {
+	write(mampf_heilung(nahrung_aktuell));
+	say( sprintf("%s wird von %s voll Hingebung verspeist.\n",
+                   capitalize(name(WER)),
+                   (string) this_player()->name(WEM)) );
+      }
+      transform_into_pile();
+      remove();
+    } else {
+      // Auch teilweise Verspeisung ist moeglich, nahrung_aktuell anpassen
+      this_player()->eat_food(gegessen);
+      if (objectp(this_player())) {
+	write(mampf_heilung(gegessen)+"Leider bist Du nicht in der Lage,"
+               " alles aufzuessen.\n");
+      say( sprintf("%s knabbert an %s herum.\n",
+                   (string) this_player()->name(),
+                   capitalize(name(WEM)) ) );
+      }
+      nahrung_aktuell -= gegessen;
+    }
+    return 1;
+}
+
+
+static int spott( string str )
+{
+    if ( !str )
+        return _notify_fail( "Syntax: spotte <text>\n" ), 0;
+    
+    if ( !objectp(moerder) )
+        return _notify_fail( "Dieses Opfer ist den Spott nicht wert.\n" ), 0;
+    
+    if ( PL != moerder )
+        return _notify_fail( "Du kannst nur Deine eigenen Opfer "
+                             +"verspotten!\n" ), 0;
+
+    if ( gespottet )
+        return _notify_fail( "Du kannst Dein Opfer nur einmal verspotten!\n" )
+            , 0;
+
+    if ( _decay < 4 )
+        return _notify_fail( "Da solltest Du schon etwas spontaner sein.\n" )
+            , 0;
+
+    str = (string) PL->_unparsed_args();
+    
+    switch ( str[0] )
+        {
+        case ':':
+            "/std/corpse"->ChannelMessageJeer( PL, str[1..], MSG_EMOTE );
+            break;
+          
+        case ';':
+            "/std/corpse"->ChannelMessageJeer( PL, str[1..], MSG_GEMOTE );
+            break;
+          
+        default:
+            "/std/corpse"->ChannelMessageJeer( PL, str, MSG_SAY );
+            break;
+        }
+  
+  gespottet = 1;
+  write( "Du verspottest Dein totes Opfer.\n" );
+  
+  return 1;
+}
+
+
+void ChannelMessageJeer( mixed sender, string text, int flag )
+{
+  if (member(inherit_list(previous_object()),CORPSE_OBJ)>-1)
+      CHMASTER->send( "Moerder", sender, text, flag );
+}
+
+
+// _channel() --
+#define KILL_MESSAGES \
+"Jetzt hat mich doch glatt %s erwischt :(", \
+"Wie soll das noch enden, %s?", \
+"Unglaublich, wie hat %s das geschafft?", \
+"RACHE! Bringt %s um!", \
+"%s ist staerker als ich dachte.", \
+"Wenn ich Dich erwische, %s!", \
+"%s hat mich ermordet!", \
+"Welche Schmach! %s hat mich besiegt.", \
+"Huetet Euch vor %s!", \
+"Warum hat mir niemand gegen %s geholfen?", \
+"%s, Du Monsterschaender!", \
+"Monster aller Regionen, vereinigt Euch gegen %s!", \
+"Danke, %s, Du hast mich von dieser schrecklichen, " \
+"sterblichen Huelle befreit.", \
+"%s? Ich bin dann jetzt weg.", \
+"Macht Euch keine Muehe, %s hat mich mal kurz zu " \
+"Gevatter Tod geschickt.", \
+"Oh nein, %s hat mich schon wieder kaltgemacht!", \
+"Sheriff! Verhafte %s!", \
+"Hilfe! Diebe! Moerder! Halsabschneider! %s!", \
+"Wieder mal an %s gestorben, find ich albern!", \
+"%s, probier das bei Anne!", \
+"Und wieder ein Drittel Erfahrung weg wegen %s...", \
+"Oh waer ich doch ein Spieler, dann duerfte mich "\
+"%s nicht mehr toeten!", \
+"Wenn Du das noch oefter machst, %s, dann spiele "\
+"ich hier nicht mehr!", \
+"%s, wieso redet der Kerl hier in Grossbuchstaben "\
+"zu mir? CAPS-LOCK kaputt?", \
+"%s ist schuld!",\
+"So schaff ich's wohl nie, Seher zu werden, %s :(",\
+"Musste das WIRKLICH sein, %s?",\
+"Wuenscht mir ne kurze Todessequenz, ich hab sie "\
+"%s zu verdanken...",\
+"Naechstes Mal bring mir lieber Post statt mich "\
+"zu ermorden, %s!",\
+"%s, wieso sagt der Typ: KOMM MIT MIR, STERBLICHER?",\
+"Ich glaub, jetzt kenne ich ALLE Todessequenzen, %s!",\
+"Was, %s hat mir WIE VIELE Lebenspunkte abgezogen???",\
+"Verdammt, %s wollte nicht sterben...",\
+"Tod den Monsterschaendern! Straft %s!",\
+"Naechstes Mal werde ICH siegen, %s!",\
+"Findest Du das fair, %s?",\
+"Das war ein gemeiner Tiefschlag, %s!",\
+"Wenn ich mal gross bin, zahl ich Dir das alles "\
+"heim, %s!",\
+"ALAAAAAARM! %s IST AUSGEBROCHEN!",\
+"Ein Koenigreich fuer den, der mir den Kopf von "\
+"%s bringt!",\
+"Suche Killer, zahle gut, Zielperson %s.",\
+"Das sag ich alles Boing, %s!",\
+"Das sag ich alles Rumata, %s!",\
+"Das sag ich alles Jof, %s!",\
+"Das sag ich alles dem Sheriff, %s!",\
+"Komm Du mir nur wieder, %s!",\
+"Ich fordere Revanche, %s!",\
+"Sheriff! Unternimm was gegen %s!",\
+"%s, wer hat DICH denn eingeladen?",\
+"NAECHSTES MAL NEHM ICH DICH AUCH MIT, %s!",\
+"Haetten wir das nicht ausdiskutieren koennen, %s?",\
+"Dich merk ich mir, %s!",\
+"Na prima, schon wieder dieser dunkle Typ! Und "\
+"alles nur wegen %s.",\
+"Hallo? Hat jemand meinen Koerper gesehen? Muesste "\
+"irgendwo bei %s liegen!",\
+"%s, waer jetzt nicht eigentlich Vorlesung?",\
+"Lern lieber, statt unschuldige Monster zu er"\
+"morden, %s!",\
+"Ohne dieses Lag haette mich %s nicht umgebracht!",\
+"In spaetestens einer Stunde komme ich wieder, %s!",\
+"Na warte %s, beim naechsten Mal trete ich Dir "\
+"kraeftig in den Gedan-Bereich!",\
+"SCHIEBUNG, der letzte Schlag von %s war nicht "\
+"regelgerecht!",\
+"Lieber eine H2O-Vergiftung, als von %s ermordet "\
+"zu werden!",\
+"Die Ehre werd ich %s nicht antun, und hier "\
+"herumposaunen, wer mich umgenietet hat!",\
+"Is mir doch egal %s, ich glaube an "\
+"die Wiedergeburt!",\
+"Aber vorsicht, %s isch kuuul maen!",\
+"Ok, %s, Du wirst von meiner Erwarteliste gestrichen!",\
+"Das naechste Mal sieht man unsere beiden Namen auf [Tod:], %s!",\
+"Magier zu mir, hier ist ein Bug: %s ist immer noch nicht tot!",\
+"Morgen frueh um halb sechs in der Arena, %s! Ich hab die Wahl der Waffen.",\
+"%s ist schlimmer als das Mensaessen. Absolut toedlich.",\
+"Mist, meine Vorsicht hat versagt! Und das bei %s!",\
+"%s, die Hydra ist mir lieber...",\
+"So, meine Ignoriereliste ist schon wieder um einen Namen laenger, %s."
+
+#define KILL_MESSAGES2 \
+"Feierabend! Danke, %s!",\
+"Toll! GENAU das hab ich jetzt gebraucht! Super, %s! :(",\
+"Schon wieder ein Tag versaut wegen %s!",\
+"Ihr sagt, ICH sei ein Monster? Ihr kennt %s nicht!!!",\
+"Sperrt %s mal in's Polargebiet! Gaebe ein gutes Untier ab...",\
+"Freiheit fuer Nicht-Spieler! Vernichtet %s!",\
+"Merlin, mach %s zum Magier!",\
+"%s ist NICHT zu meiner Beerdigung eingeladen!",\
+"Ich hab keine Angst vor Dir, %s!",\
+"%s ist doof und stinkt!",\
+"Moegest Du im Lag verrotten, %s!",\
+"%s wird gleich das Spiel verlassen, um meiner Rache zu entgehen!",\
+"Der Todestag von %s wird fuer mich ein Feiertag sein!",\
+"%s ist ein Metzelskript, ich hab's genau gesehen!", \
+"Waer ich Tanjian, haettest Du das nicht geschafft, %s!", \
+"Das naechste Mal komme ich als Kaempfer wieder, %s!", \
+"Ich lass' mich aufwerten und werde Zauberer, %s!", \
+"Gildenbalance! %s ist viel zu stark!", \
+"Jetzt ist mir das Kleingeld zum Tanken ausgegangen, %s...", \
+"%s hat ein Zap-Tool!", \
+"%s, Du <zensiert>!", \
+"%s, das ist doch Hullepanz. (c) Catweazle", \
+"Bald bin ich SuperSeher, %s!", \
+"Mit Dir 'piel ich nich' mehr, %s, Du hast in mein Foermssen depinkelt!", \
+"Verdammt, %s muss mir den Feenring geklaut haben...", \
+"Nie ist ein Kleriker in der Naehe, wenn man ihn braucht, %s...", \
+"Menno, %s, wo war hier noch mal die Heilstelle?", \
+"Hoffentlich werde ich naechstes Mal als Kuh wiedergeboren, %s...", \
+"Wenn Du sonst noch Probleme hast, %s, geh' zu Marvin und red' " \
+"mit ihm darueber.", \
+"Du, %s, Gewalt ist keine Loesung, lass uns darueber diskutieren.", \
+"Das naechste Mal zeig' ich Dir meine Mateteesammlung, %s.", \
+"%s eunt domum!", \
+"%s, ist es so kalt hier oder bin ich das?", \
+"Ich war schon wieder zur falschen Zeit am falschen Ort, %s...", \
+"Danke, %s. Im Namen des Mondes werde ich Dich bestrafen!", \
+"Das war mal wieder ein Schuss in den Ofen, %s...", \
+"Kannst Du das mit Deinem Gewissen vereinbaren, %s?", \
+"Und Kandri sprach: Mein ist die Rache, %s!", \
+"%s, Du haettest meine Leiche nicht auch noch schaenden muessen...", \
+"%s, ich kann so nicht arbeiten!", \
+"%s, ich geb' Dir mal 2 Muenzen, dann kannst Du Deine Sorgen " \
+"jemand anderem erzaehlen...", \
+"Manchmal verspeist Du den Baer, %s, manchmal verspeist " \
+"der Baer Dich...", \
+"Das naechste Mal betrittst _Du_ die Welt des Schmerzes, %s!", \
+"Wenn ich wiederkomme, %s, werde ich erst Dich toeten, dann " \
+"Deinen Partner, dann Deine Brueder und Schwestern und jeden " \
+"der Dich kennt!", \
+"Oh nein, %s! Schon wieder diese ewig lange Todessequenz mit der " \
+"Drachenschule!", \
+"%s, wo bekomme ich jetzt einen neuen Koerper her?", \
+"Lass Dir meine Leiche schmecken, %s.", \
+"Haeh?", \
+"Ja, aber wieso das denn? Och menno, %s!", \
+"So ein Mist, %s, ich hatte 'nen Disconnect...", \
+"*winkewinke*, %s!", \
+"Heute ist nicht alle Tage, ich komm' wieder, keine Frage.", \
+"Lass das, %s!", \
+"Niemand! Niemand nennt mich eine feige Sau, %s!", \
+"Da hab' ich jetzt wohl verloren, %s...", \
+"%s, ich kann nicht metzeln, ich kann nur sterben...", \
+"Hattest Du ueberhaupt einen Termin, %s?", \
+"Du warst gar nicht an der Reihe, %s!", \
+"Aetsch, %s, der Kill zaehlt nicht!", \
+"Jetzt noch mal mit Gefuehl, %s.", \
+"Tschuess erstmal, %s.", \
+"Ich weiss nicht ob Du es gemerkt hast, %s, aber " \
+"ich bin schon tot...", \
+"%s, ich haette nicht gedacht, dass ich noch mal sterbe " \
+"bevor Taramis angeschlossen wird...", \
+"Ich wollte sowieso grade gehen, %s.", \
+"Shit happens, %s.", \
+"Dazu faellt mir jetzt nix mehr ein, %s."
+
+#define KILL_MESSAGES3 \
+"Was heisst denn \"Dein Teddy knuddelt Dich ein letztes Mal\", %s?", \
+"Ich bin beeindruckt, %s.", \
+"Ok, Dein Gesicht merke ich mir, %s!", \
+"Kaum da, haut mich %s schon wieder tot... *grummel*", \
+"Ich werde Dich verfolgen, %s! Ich werde in Deine Traeume eindringen " \
+"und Dich in den Wahnsinn treiben! Du wirst diesen Tag noch verfluchen!", \
+"Ich geh ja schon, %s.", \
+"Auf den ersten Blick sieht %s gar nicht so gefaehrlich aus.", \
+"Von %s besiegt - wie peinlich.", \
+"Mist. Einmal nicht aufgepasst, schon wird man von %s ermordet.", \
+"Das war ein Glueckstreffer, %s!", \
+"Hah, das ist doch nur eine Fleischwunde! Gibst Du etwa schon auf, %s?", \
+"%s macht gerade eine destruktive Phase durch.", \
+"Zum Glueck ist das ja nur ein Spiel, %s. Sonst waere ich jetzt wirklich " \
+"boese.", \
+"Begrabe mich wenigstens anstaendig, %s.", \
+"Hm, die Flatrate brauch' ich dann wohl doch nicht, %s.", \
+"Na warte, %s! Zur Strafe werde ich gleich das Mud crashen!", \
+"Ich sollte nicht mehr soviel mudden. Dann muesste ich mich auch nicht mehr " \
+"ueber %s aergern.", \
+"Auf am Boden Liegende einzuschlagen! Schaem Dich, %s!", \
+"Einigen wir uns auf unentschieden, %s?", \
+"Ich sagte doch \"Ich ergebe mich!\", %s.", \
+"Aeh, %s, koennte das unter uns bleiben? Ich habe schliesslich einen " \
+"Ruf zu verlieren...", \
+"Du bist vielleicht staerker, %s, aber dafuer bin ich schoener.", \
+"Die Rechnung fuer die Behandlung meiner Minderwertigkeitskomplexe geht " \
+"an %s.", \
+"Ab und zu muss man %s ja gewinnen lassen, sonst gibt es wieder Traenen.", \
+"Bis eben war mir %s noch sympathisch.", \
+"Geht's Dir jetzt besser, %s?", \
+"Macht kaputt, was mich kaputt macht! Toetet %s!", \
+"Beim naechsten Mal mach' ich es Dir nicht mehr so einfach, %s!", \
+"Weisst Du, %s, ich habe nur verloren, weil Du... aeh, weil ich... " \
+"aeh... weil Du gewonnen hast.", \
+"Jo, immer feste druff, %s. Ist ja nur ein dummer NPC. *grummel*", \
+"Ich muss sagen, %s kann feste schlagen.", \
+"Also %s! Kommst hier rein, haust mich um und tust dann so, als sei nichts " \
+"gewesen...", \
+"In welcher Gilde konnte man nochmal \"befriede\" lernen, %s?", \
+"Tja, %s, aus diesem Kampf bist Du wohl nur als Vorletzter hervorgegangen" \
+"...", \
+"Vorsicht, %s greift immer von hinten an!", \
+"Was meint %s mit \"Sparringspartner\"?", \
+"Ich habe mir bei Dir nur deshalb keine Muehe gegeben, %s, weil Du eh nicht " \
+"stupst.", \
+"Huch, ich bin ja schon wieder auf -Moerder!", \
+"Ich spuck' Dir in's Auge und blende Dich, %s!", \
+"Huch, wo seid ihr alle hin?", \
+"Wer war das?", \
+"Moegest Du in interessanten Zeiten leben, %s.", \
+"Ich sehe was, was Du nicht siehst, %s, und es ist... schwarz.", \
+"Hey Maedels! Ratet, wen ich gerade getroffen habe!", \
+"%s?", \
+"Noch einmal sowas, %s, und ich lasse Deine Gilde abwerten. Ich kann das!", \
+"Was bedeutet 'Jemand beschwoert Dein Bild herauf.'? Warst Du das, %s?"
+
+private string *moerder_msgs = ({KILL_MESSAGES, KILL_MESSAGES2, KILL_MESSAGES3,
+"Sterben ist schoen, Sterben ist toll - Ach waer ich wie ein Kaempfer voll!",
+"Lauschige Nacht - ich geh mir die Sterne anschauen. Bis spaeter!",
+"Echt %s, das war 0815, keine Kreativitaet beim Toeten!",
+"%s, Du kannst ja gar ni... Verdammt!",
+"Kaempfer, Zauberer, Tanjian, alles Luschen! Ich werde Abenteurer, da ist "
+    "wenigstens Thrill!", 
+"Toeten, Toeten, Toeten. Ist das alles, was Du kannst, %s?",
+"Ach geh doch Bluemchen pfluecken %s!", 
+"Mein Tod war KEIN FP %s... HA!",
+"Lars gibt mir nen Whisky, wenn ich komme. Und Dir?",
+"Geh lernen, Depp!",
+"Auf der naechsten Mud-Party haut Dich mein Magier zurueck!",
+"Lass gut sein, tat eh nicht weh!", 
+"Ich lass Dich von Zook abhaengen, %s!",
+"Koennen wir naechstes Mal nicht lieber einen trinken gehen, %s?",
+"Verdammt, meine neue Gilde ist ja noch gar nicht angeschlossen!",
+"Yohoho und ne Buddel voll Rum!",
+"Beim Kartenspiel haettest Du keine Chance gehabt %s!",
+"%s, Du hast echt keine Ahnung von stilvollem Metzeln...",
+"Was muss ich noch tun fuer Deinen Aufstieg, %s? *seufz*",
+"EK-Poser!",
+"Leistung ist was anderes, %s!",
+"Lass mich leeeeeeb... Arrrrggggh!",
+"Hast Du sonst nix in der Drachenschule gelernt, %s?",
+"Und wer kuemmert sich jetzt um meine Haustiere, %s?",
+"Ich musste sterben, Du willst mich nur beerben. Ich gehe "
+    "mit Stil, Du findest nicht viel.",
+"Ich habe nun mehr Tode als Du, %s. Topp mich, wenn Du kannst!",
+"Ok, dann geh ich halt WOW spielen.",
+"Toll, freut sich der Naechste. Wieder ne Todesfolge, %s.",
+"Du gucktest so traurig, %s, ich machte Dir meinen Tod zum Geschenk. "
+    "Sollst ja auch mal 'n Erfolgserlebnis haben!",
+"Nun reiche ich den letzten Becher, trinke den Wein, erkenne Dein Sein.",
+"%s hat nicht einmal getroffen. Ich habe mich wortwoertlich totgelacht.",
+"Lass mich nachdenken, %s. Ich starb fuer nen EK?",
+"Ich kann nicht glauben, dass das gerade passiert ist!",
+"Du Suender. Du sollst _nicht_ toeten!",
+"%s, mein Testament steckt im rechten Schuh!",
+"Sterben und sterben lassen, mein Freund. Wir sehen uns wieder!",
+"Und ich sag noch, %s... Das ist KEINE Schwertscheide... Aber Du musst "
+  "ja alles selbst ausprobieren.",
+"Soll das jetzt alles gewesen sein, %s?",
+"In meinem Code ist ein Fehler und gleich gibt's 'nen Crash.",
+"Ach, auch egal, %s. Ist eh gleich Schichtwechsel.",
+"Jofverdammt... warst Du gut, %s!",
+"Duelle von Killern enden meist fuer einen toedlich und das wirst "
+  "Du s... Argh!",
+"Bist Du sicher, dass Du den EK bekommen hast, %s?",
+"Danke %s! Weisst Du, ich hatte mit Zook gewettet, wer mich "
+  "toetet. Und ich habe eine Wiedergeburt gewonnen!",
+"Nun bin ich sicher vor Dir, %s!",
+"Ich mache jetzt Urlaub und habe Zook gebeten, meinen Reset "
+  "um einen Monat zu verschieben. Gluecklich, %s?",
+});
+
+
+int _query_kma() { return sizeof(moerder_msgs); }
+
+
+object _channel( object ob )
+{
+  int m_FMM, m_HP, m_WC, m_AC, s_HP, s_WC, s_AC;
+  string msg;
+  object rueck;
+
+  if (member(inherit_list(previous_object()),CORPSE_OBJ)>-1)
+      {
+          string killer;
+          int i, x, y, z, nr;
+          closure m_q, s_q;
+    
+          while( previous_object(i) &&
+                 !query_once_interactive(previous_object(i)) )
+              i++;
+          
+          if( !previous_object(i) || IS_LEARNER(previous_object(i)) )
+              return rueck;
+
+          killer = (string) previous_object(i)->name();
+          
+          if ( lower_case(killer) != getuid(previous_object(i)) )
+              killer = capitalize(getuid(previous_object(i)));
+          
+          m_q = symbol_function( "QueryProp", ob ); // Monster
+          s_q = symbol_function( "QueryProp", previous_object(i) ); // Spieler
+          
+          if ( (m_FMM = (int) funcall( m_q, P_FORCE_MURDER_MSG )) >= 0 )
+              if ( (object_name(ob) == "/obj/shut") ||
+                   (m_FMM > 0) ||
+                   (nr = (random(100) >= 99) ? 1 : 0 ) || 
+                   (nr = ((x = (m_HP = (int) funcall( m_q, P_MAX_HP )) * 
+                           ((m_WC = (int) funcall( m_q, P_TOTAL_WC )) +
+                            (m_AC = (int) funcall( m_q, P_TOTAL_AC ))))
+                          > 200000) ? 2 : 0) ||
+                   (nr = (((y = m_HP * (m_WC + m_AC)) >
+                           (z = 5 * (s_HP = (int) funcall( s_q, P_MAX_HP )) *
+                            ((s_WC = (int) funcall( s_q, P_TOTAL_WC )) +
+                             (s_AC = (int) funcall( s_q, P_TOTAL_AC )))))
+                          ? 3 : 0)))
+                  {
+                      SetProp( P_NAME, "Geist "+(string) ob->name(WESSEN, 0) );
+
+                      if( !(msg = (string) ob->QueryProp(P_MURDER_MSG)) )
+                          msg = moerder_msgs[random(sizeof(moerder_msgs))]; 
+
+		      if ( stringp(msg) )
+			  msg = sprintf( msg, killer || "Moerder" );
+
+                      CHMASTER->send( "Moerder", this_object(), funcall(msg) );
+
+                      rueck = previous_object(i);
+                  }
+          
+          log_file( "moerder.log",
+                    sprintf( "MON(%O) COND(%d) NPC(%d), DIFF(%d,%d)\n",
+                             ob, nr, x, y, z) );
+      }
+
+  return rueck;
+}
+
+void transform_into_pile() {
+	if( environment()->QueryProp(P_PREVENT_PILE) ) return;
+	object* inv = all_inventory();
+	if( sizeof(inv)<2 ) return;
+	object p = clone_object(PILE_OBJ);
+	filter_objects( inv, "move", p, M_SILENT | M_NOCHECK );
+	p->move( environment(), M_SILENT | M_NOCHECK );
+}
diff --git a/std/def_workroom.c b/std/def_workroom.c
new file mode 100644
index 0000000..12034fa
--- /dev/null
+++ b/std/def_workroom.c
@@ -0,0 +1,46 @@
+// MorgenGrauen MUDlib
+//
+// def_workroom.c -- standard workroom for new wizards
+//
+// $Id: def_workroom.c 7423 2010-02-07 22:56:38Z Zesstra $
+
+#pragma strong_types,save_types,rtt_checks
+#pragma pedantic, range_check
+
+inherit "std/room";
+
+#include <properties.h>
+
+void create()
+{
+  string WIZNAME;
+
+  ::create();
+  WIZNAME = capitalize(getuid(this_object()));
+
+  SetProp( P_LIGHT, 1 );
+  SetProp( P_INT_SHORT, "Der Arbeitsraum von "+WIZNAME );
+  SetProp( P_INT_LONG,
+   "Dieses ist der Arbeitsraum von "+WIZNAME+". Es liegen vollgeschriebene\n"
+  +"Zettel auf dem Boden, die Waende sind mit Kreidezeichen zugekritzelt.\n"
+  +"Man merkt, dass dieser Raum dafuer da ist, dass "+WIZNAME+" fuer sich\n"
+  +"ungestoert arbeiten kann. Ein magisches Portal fuehrt in die Welt\n"
+  +"der Sterblichen.\n"
+	  );
+  SetProp(P_INDOORS,1);
+  AddDetail( ({ "portal", "portale" }), 
+	    "Das Portal fuehrt in die Abenteurer-gilde.\n" );
+  AddDetail( ({ "raum", "arbeitsraum" }),
+	     "Du befindest Dich mittendrin.\n" );
+  AddDetail( "zettel",
+	     "Die Zettel sind mit unleserlichen Zeichen vollgeschrieben.\n" );
+  AddDetail( ({ "boden", "fussboden" }),
+	     "Der Fussboden ist bedeckt mit vollgeschriebenen Zetteln.\n" );
+  AddDetail( ({ "wand", "waende" }),
+	     "An den Waenden befinden sich Kreidezeichen.\n" );
+  AddDetail( ({ "kreide", "kreidezeichen", "zeichen" }),
+	    "Die Zeichen ergeben nur fuer " + WIZNAME + " einen Sinn.\n" );
+  AddDetail( "portal",
+	     "Das Portal fuehrt in die Welt der Sterblichen.\n" );
+  AddExit("gilde","in die Gilde#/gilden/abenteurer");
+}
diff --git a/std/food.c b/std/food.c
new file mode 100644
index 0000000..7b75921
--- /dev/null
+++ b/std/food.c
@@ -0,0 +1,732 @@
+// MorgenGrauen MUDlib
+//
+// /std/food.c  - Standardobjekt fuer Lebensmittel
+//
+// $Id: food.c 8248 2012-11-23 21:28:04Z Zesstra $
+#pragma strict_types,save_types,rtt_checks
+#pragma no_clone
+#pragma pedantic,range_check
+
+inherit "/std/thing";
+
+#include <food.h>
+#include <living/life.h>
+#include <break_string.h>
+
+// Standard-IDs und -Adjektive muessen gesichert werden
+#define  ID_BACKUP       "std_food_id_backup"
+#define  ADJ_BACKUP      "std_food_adj_backup"
+
+// Zeitpunkt, an dem die Speise verdorben ist
+private nosave int time_gone_bad;
+
+// Zeitpunkt, an dem die Speise verderben darf
+// wird immer erst gesetzt, wenn ein Spieler hiermit in Beruehrung kommt
+private nosave int time_togo_bad;
+
+// Zeitpunkt, an dem die Speise zerstoert werden darf
+// wird immer erst gesetzt, wenn ein Spieler hiermit in Beruehrung kommt
+private nosave int time_to_remove;
+
+// Zeitpunkt, ab dem die Speise mit einem Spieler in Beruehrung kam
+// bei ewig haltbaren Speisen ist sie immer 0;
+private nosave int start_life = 0;
+
+// Dauer des ersten Resets
+private nosave int first_reset_length = 0;
+
+// Wertet Properties aus (Ersetzungen werden durchgefuehrt)
+protected mixed eval_property(string val,object consumer) {
+  mixed what = QueryProp(val);
+  if (stringp(what)) {
+    what=capitalize(replace_personal(what,({this_object(),consumer}),1));
+  }
+  return break_string(what,78,"",BS_LEAVE_MY_LFS);
+}
+
+// wird nur verwendet, wenn der gesetzte Reset ein Timerende darstellt,
+// um zu garantieren, dass der nicht abgestellt wird.
+// Zugegebenenermassen keine schoener Hack :-/
+private void my_set_next_reset(int zeit) {
+  set_next_reset(zeit);
+  // Damit der Reset auch wirklich ausgefuehrt wird ...
+  call_out(#'id,1);
+}
+
+// Ausgabe von Meldungen an Besitzer oder Raum
+protected void message(string prop) {
+  object env = environment(this_object());
+  if (objectp(env)) {
+    if (interactive(env)) {
+      tell_object(env,
+        break_string(eval_property(prop,env),78,"",BS_LEAVE_MY_LFS));
+    }
+    else if(!living(env)) {
+      tell_room(env,
+        break_string(eval_property(prop,0),78,"",BS_LEAVE_MY_LFS));
+    }
+  }
+}
+
+// Flag, ob die Speise verdorben ist
+public int is_bad() {
+  return time_gone_bad > 0;
+}
+
+// Anzahl der Sekunden, die das Lebensmittel bereits hinter sich hat,
+// seit es mit einem Spieler in Beruehrung kam
+public int get_current_lifetime() {
+  if (!start_life) return 0;
+  return time() - start_life;
+}
+
+// Berechnet den Wert der Speise korrekt
+int _query_value() {
+  int result = 0;
+  if (!is_bad()) {
+    result += to_int(QueryProp(P_PORTIONS)*Query(P_VALUE,F_VALUE));
+  }
+  if (mappingp(QueryProp(P_EMPTY_PROPS))) {
+    result += QueryProp(P_EMPTY_PROPS)[P_VALUE];
+  }
+  return result;
+}
+
+// Berechnet den Wert der Speise korrekt
+int _query_weight() {
+  int result = to_int(QueryProp(P_PORTIONS)*Query(P_WEIGHT,F_VALUE));
+  if (mappingp(QueryProp(P_EMPTY_PROPS))) {
+    result += QueryProp(P_EMPTY_PROPS)[P_WEIGHT];
+  }
+  return result;
+}
+
+// Dauer des ersten Resets wird wieder hinzugerechnet
+// damit wird bei Nichtsetzen der Prop der Standard geliefert
+int _query_std_food_lifetime() {
+  return Query(P_LIFETIME,F_VALUE)+(first_reset_length);
+}
+
+// Setter fuer die Lifetime, damit die Resettime immer mitkorrigiert wird
+// und Dauer des ersten Resets abgezogen werden, da die als Standard immer
+// dazukommen
+protected void set_std_food_lifetime(int val) {
+  if (val > 0) {
+    if (!time_togo_bad && !is_bad()) {
+      Set(P_LIFETIME,val-(first_reset_length),F_VALUE);
+      // Anzahl der Resets richtet sich nach dem Mittelwert
+      Set(P_RESET_LIFETIME,to_int((val-(first_reset_length))
+                           /(__RESET_TIME__*0.75))+1,F_VALUE);
+      set_next_reset(val);
+    }
+  }
+  else {
+    write("Da ist was schief gelaufen! Sprich mal mit einem Magier.\n");
+    raise_error("setting P_LIFETIME to invalid int value: "+
+      to_string(val)+"\n");
+  }
+}
+
+// Anzahl der Resets als Lebenszeit
+void _set_std_food_lifetime_reset(int value) {
+  if (value < 1) value = 1;
+
+  Set(P_RESET_LIFETIME,value,F_VALUE);
+  // per Zufall die Laenge der einzelnen Resets berechnen
+  int length = first_reset_length;
+  foreach(int i: value) {
+    length += to_int((__RESET_TIME__ + random(__RESET_TIME__)) / 2);
+  }
+  set_std_food_lifetime(length);
+}
+
+// Flag, ob Speise "essbar" ist
+public int is_eatable() {
+  return QueryProp(P_FOOD) > 0;
+}
+
+// Flag, ob Speise "trinkbar" ist
+public int is_drinkable() {
+  return QueryProp(P_FOOD) == 0 && QueryProp(P_DRINK) > 0;
+}
+
+// Flag, ob noch Portionen der Speise vorhanden sind
+public int is_not_empty() {
+  return QueryProp(P_PORTIONS) > 0;
+}
+
+// Macht eine Speise leer bzw. zerstoert sie.
+// Muss beim Ueberschreiben immer als letztes aufgerufen werden.
+// Diese Methode wird aufgerufen, wenn ein Behaelter leergefuttert wird
+// oder der Inhalt zerstoert werden soll
+// Rueckgabewert: Ergebnis von remove() bzw. 0, wenn Objekt nicht
+//                zerstoert werden muss
+// Beim Aufruf bitte IMMER das Ergebnis pruefen, damit keine Aufrufe an
+// geloeschten Objekten passieren! if (make_empty()) return ...;
+public int make_empty() {
+  mixed props = QueryProp(P_EMPTY_PROPS);
+  if (mappingp(props)) {
+    foreach(string key:m_indices(props)) {
+      if (key == P_IDS) {
+        RemoveId(QueryProp(P_IDS));
+        AddId(QueryProp(ID_BACKUP));
+        AddId(props[key]);
+      }
+      else if (key == P_ADJECTIVES) {
+        RemoveId(QueryProp(P_ADJECTIVES));
+        AddAdjective(QueryProp(ADJ_BACKUP));
+        AddAdjective(props[key]);
+      }
+      else {
+        SetProp(key,props[key]);
+      }
+    }
+
+    // Sicherheitshalber :-)
+    SetProp(P_PORTIONS,0);
+    return 0;
+  }
+  else {
+    return remove(1);
+  }
+}
+
+// wird aufgerufen, wenn die Speise schlecht war und der Removetimer
+// abgelaufen ist
+// Zerstoert die Speise oder den Inhalt eines Behaelters
+// Rueckgabewert: Ergebnis von remove() bzw. 0, wenn Objekt nicht
+//                zerstoert werden muss
+public int make_destroy() {
+  message(P_REMOVE_MSG);
+  set_next_reset(-1);
+  return make_empty(); // Leermachen oder zerstoeren
+}
+
+// Laesst die Speise schlecht werden (mit Meldungen)
+// muss beim Ueberschreiben immer mit aufgerufen werden
+// Rueckgabewert: Ergebnis von remove() bzw. 0, wenn Objekt nicht
+//                zerstoert werden muss
+// Beim Aufruf bitte IMMER das Ergebnis pruefen, damit keine Aufrufe an
+// geloeschten Objekten passieren! if (make_bad()) return ...;
+public int make_bad() {
+  time_gone_bad = time();
+  if (is_not_empty()) {
+    message(P_BAD_MSG);
+  }
+  if (QueryProp(P_DESTROY_BAD) == DESTROY_BAD && !QueryProp(P_EMPTY_PROPS)) {
+    return remove(1);
+  }
+  return 0;
+}
+
+// Startet den Timer zum Schlechtwerden der Speise
+public void start_lifetime() {
+  // Test, ob Lebensmittel schlecht werden darf
+  if (!QueryProp(P_NO_BAD) && !time_togo_bad) {
+    start_life = time();
+    int zeit = QueryProp(P_LIFETIME);
+     // Zeitpunkt des Schlechtwerdens
+    time_togo_bad = start_life + zeit;
+    // Zeitpunkt des Zerstoerens
+    time_to_remove = time_togo_bad + QueryProp(P_DESTROY_BAD);
+    my_set_next_reset(zeit);
+  }
+}
+
+// Reset wird in 2 Situationen aufgerufen:
+// 1. Timer zum schlecht werden ist abgelaufen, das Futter muss also
+//    schlecht werden und evtl. der Timer zum Zerstoeren gestartet werden
+// 2. Timer zum Zerstoeren ist abgelaufen, also wird Futter zerstoert.
+// Wird der Reset aus irgendeinem Grund zu falschen Zeiten aufgerufen,
+// wird der naechste Reset korrekt initialisiert.
+void reset() {
+  // wenn kein env, Abbruch und reset abschalten.
+  if (!environment()) {
+      set_next_reset(-1);
+      return;
+  }
+
+  // Test, ob Lebensmittel schlecht werden darf
+  if (!QueryProp(P_NO_BAD)) {
+    if (!is_bad()) {
+      // Test, ob Timer laeuft
+      if (time_togo_bad) {
+        // Pruefen, ob der Reset so in etwa zur richtigen Zeit aufgerufen wurde
+        // 10% Abweichung sind erlaubt
+        int diff = time() - time_togo_bad;
+        int std = to_int(QueryProp(P_LIFETIME)/10);
+        if (abs(diff) < std || diff > 0) {
+          if (make_bad()) return;
+          // Reset zum Zerstoeren vorbereiten
+          if (QueryProp(P_DESTROY_BAD) > 0) {
+            my_set_next_reset(time_to_remove - time());
+          }
+          else {
+            set_next_reset(-1);
+          }
+        }
+        else {
+          // Reset nochmal zum Ablauf der Frist
+          my_set_next_reset(abs(diff));
+        }
+      }
+    }
+    else if (time_to_remove && QueryProp(P_DESTROY_BAD) > 0) {
+      // wir sind im Reset nach dem Schlechtwerden
+      int diff = time() - time_to_remove;
+      int std = to_int((QueryProp(P_LIFETIME)+QueryProp(P_DESTROY_BAD))/10);
+      // Pruefen, ob der Reset so in etwa zur richtigen Zeit aufgerufen wurde
+      // 10% Abweichung sind erlaubt
+      if (abs(diff) < std || diff > 0) {
+        if (make_destroy()) return; // Leermachen oder zerstoeren
+      }
+      else {
+        // Reset nochmal zum Ablauf der Frist
+        my_set_next_reset(abs(diff));
+      }
+    }
+  } // if (P_NO_BAD)
+
+  ::reset();
+}
+
+private int safety_check() {
+  // Damit ein Spieler auch gaaaanz sicher nicht Speisen ausserhalb der 
+  // P_LIFETIME nutzen kann ...
+  if (time_togo_bad && !is_bad() && time() > time_togo_bad) {
+    if (make_bad()) return 0;
+    // Reset zum Zerstoeren vorbereiten
+    if (QueryProp(P_DESTROY_BAD) > 0) {
+      my_set_next_reset(time_to_remove - time());
+    }
+    else {
+      set_next_reset(-1);
+    }
+    return 0;
+  }
+  else if (time_to_remove && time() > time_to_remove
+      && QueryProp(P_DESTROY_BAD) > 0) {
+    make_destroy(); // Leermachen oder zerstoeren
+    return 0;
+  }
+  return 1;
+}
+
+// Pruefung zum Starten ausgelagert, da das im init und in
+// NotifyMove gebraucht wird
+private void check_start_timer(object oldenv) {
+  // Test, ob Lebensmittel schlecht werden kann
+  if (!QueryProp(P_NO_BAD) && !time_togo_bad) {
+    // Wenn wir irgendwie in einem Spieler stecken oder ein Spieler in der
+    // Umgebung ist oder das Futter mehrfach bewegt wurde
+    if (sizeof(filter(all_environment(),#'query_once_interactive))
+        || sizeof(filter(all_inventory(environment()),#'query_once_interactive))
+        || objectp(oldenv)) {
+      start_lifetime();
+    }
+  }
+  else {
+    // Damit ein Spieler auch gaaaanz sicher nicht Speisen ausserhalb
+    // der P_LIFETIME nutzen kann ...
+    safety_check();
+  }
+}
+
+// hier wird geprueft, ob das Futter dem Spieler zugaenglich ist.
+void init() {
+  ::init();
+  check_start_timer(environment());
+}
+
+// hier wird geprueft, ob das Futter dem Spieler zugaenglich ist.
+// das ist sicherer als nur im init()
+protected void NotifyMove(object dest, object oldenv, int method) {
+  ::NotifyMove(dest, oldenv, method);
+  check_start_timer(oldenv);
+}
+
+void create() {
+  // Sicherheitshalber,
+  // falls jemand die Blueprint mit Props initialisieren will
+  ::create();
+  Set(P_LIFETIME, #'set_std_food_lifetime, F_SET_METHOD);
+  Set(P_LIFETIME, PROTECTED, F_MODE_AS);
+
+  time_togo_bad = 0;
+  time_to_remove = 0;
+  time_gone_bad = 0;
+
+  // Standardlaufzeit 1 Reset
+  first_reset_length = to_int((__RESET_TIME__ + random(__RESET_TIME__)) / 2);
+  // Reset wird erstmal abgeschaltet, das wird im NotifyMove geprueft
+  set_next_reset(-1);
+
+  SetProp(P_SHORT,"Ein Lebensmittel");
+  SetProp(P_LONG,"Das ist ein Lebensmittel.\n");
+  SetProp(P_NAME,"Lebensmittel");
+  SetProp(P_MATERIAL, MAT_MISC_FOOD);
+  SetProp(P_GENDER,NEUTER);
+  SetProp(P_WEIGHT,50);
+  SetProp(P_VALUE,10);
+  SetProp(P_PORTIONS,1); // einmal Abbeissen
+  SetProp(P_DESTROY_BAD,DESTROY_BAD); // Zerstoeren beim Schlechtwerden
+  SetProp(P_DISTRIBUTION,HD_STANDARD); // Heilung per Standardverteilung (5/hb)
+
+  SetProp(P_CONSUME_MSG,"@WER2 konsumiert @WEN1.");
+  SetProp(P_EATER_MSG,"Du konsumierst @WEN1.");
+  SetProp(P_EMPTY_MSG,"@WER1 ist bereits leer.");
+  SetProp(P_NOFOOD_MSG,"@WEN1 kann man nicht essen!");
+  SetProp(P_NODRINK_MSG,"@WEN1 kann man nicht trinken!");
+  SetProp(P_BAD_MSG,"@WER1 verdirbt.");
+  SetProp(P_FOOD_FULL_MSG,"Du bist zu satt, das schaffst Du nicht mehr.");
+  SetProp(P_DRINK_FULL_MSG,"So viel kannst Du im Moment nicht trinken.");
+  SetProp(P_ALC_FULL_MSG,"Soviel Alkohol vertraegst Du nicht mehr.");
+  SetProp(P_ENV_MSG,"Vielleicht solltest Du @WEN1 vorher nehmen.");
+  SetProp(P_REMOVE_MSG,"@WER1 zerfaellt zu Staub.");
+
+  // Sichern der Standards
+  SetProp(ID_BACKUP,QueryProp(P_IDS));
+  SetProp(ADJ_BACKUP,QueryProp(P_ADJECTIVES));
+
+  AddId(({"lebensmittel","nahrung","\nfood"}));
+
+  AddCmd(({"iss","esse"}),"cmd_eat");
+  AddCmd(({"trink","trinke"}),"cmd_drink");
+}
+
+// Aendert Werte der Speise, wenn sie vergammelt ist und konsumiert wird
+protected void consume_bad(mapping entry_info) {
+  entry_info[P_HP] = 0;
+  entry_info[P_SP] = 0;
+  entry_info[H_EFFECTS] = ([P_POISON:1]);
+}
+
+// Stellt Mapping zum Konsumieren zusammen und konsumiert.
+// kann mit testonly=1 aufgerufen werden, um zu pruefen, ob konsumieren klappt
+// Gibt das Ergebnis von living->consume() zurueck:
+// >0 -> erfolgreich konsumiert
+// <0 -> nicht konsumiert (siehe Hilfe zu consume)
+protected varargs int try_consume(object consumer, int testonly) {
+  mapping entry_info = ([P_FOOD:QueryProp(P_FOOD),
+                        P_DRINK:QueryProp(P_DRINK),
+                           P_HP:QueryProp(P_HP),
+                           P_SP:QueryProp(P_SP),
+                       P_POISON:QueryProp(P_POISON),
+                      P_ALCOHOL:QueryProp(P_ALCOHOL),
+                 H_DISTRIBUTION:QueryProp(P_DISTRIBUTION)]);
+
+  if (is_bad()) consume_bad(entry_info);
+
+  int result = (int)consumer->consume(entry_info, testonly);
+  if (!result) {
+    tell_object(consumer,
+      "Da ist was schief gelaufen! Sprich mal mit einem Magier.\n");
+    raise_error("living->consume() mit falschen Parametern aufgerufen:\n"+
+      sprintf("%O\n",entry_info));
+  }
+
+  return result;
+}
+
+// Konsumieren war erfolgreich
+// Hier kann man beim Ueberschreiben noch zusaetzliche Sachen machen
+// P_PORTIONS ist bereits runtergezaehlt, Speise ist aber noch nicht leer!
+// make_empty() wird also eventuell noch danach aufgerufen
+// entspricht alter Func BeimEssen()
+protected void success_consume(object consumer) {
+  tell_room(environment(consumer),eval_property(P_CONSUME_MSG,consumer),({consumer}));
+  tell_object(consumer,eval_property(P_EATER_MSG,consumer));
+}
+
+// Konsumieren war nicht erfolgreich
+// Hier kann man beim Ueberschreiben noch zusaetzliche Sachen machen
+// in reason wird der Grund des Fehlschlagens uebergeben (siehe 'man consume')
+protected void failed_consume(object consumer, int reason) {
+  if (reason & HC_MAX_FOOD_REACHED)
+    tell_object(consumer,eval_property(P_FOOD_FULL_MSG,consumer));
+  else if (reason & HC_MAX_DRINK_REACHED)
+    tell_object(consumer,eval_property(P_DRINK_FULL_MSG,consumer));
+  else if (reason & HC_MAX_ALCOHOL_REACHED)
+    tell_object(consumer,eval_property(P_ALC_FULL_MSG,consumer));
+}
+
+// Konsumiert die Speise
+// Gibt das Ergebnis von living->consume() zurueck:
+// >0 -> erfolgreich konsumiert
+// <0 -> nicht konsumiert (siehe Hilfe zu consume)
+protected int consume(object consumer) {
+  if (!objectp(consumer) || !living(consumer)) {
+    raise_error("argument pl not a living:\n"+
+      sprintf("%O\n",consumer));
+  }
+  // damit abgelaufene Speisen auch wirklich abgelaufen sind
+  if (!safety_check()) return 0;
+
+  if (!is_not_empty()) {
+    notify_fail(eval_property(P_EMPTY_MSG,consumer));
+    return 0;
+  }
+  int result = try_consume(consumer);
+  if (result > 0) {
+    SetProp(P_PORTIONS,QueryProp(P_PORTIONS)-1);
+    success_consume(consumer);
+    if (!is_not_empty()) make_empty();
+  }
+  else if (result < 0) {
+    failed_consume(consumer,result);
+  }
+  return result;
+}
+
+// Futtern
+int cmd_eat(string str) {
+  notify_fail("WAS moechtest Du essen?\n");
+  if (!str || !id(str)) return 0;
+  object env = environment(this_object());
+  if (env != this_player() && QueryProp(P_ENV_MSG)) {
+    notify_fail(eval_property(P_ENV_MSG,env));
+    return 0;
+  }
+
+  if (!is_eatable() && !is_drinkable()) {
+    write("Mit diesem Futter stimmt was nicht!"
+      " Sprich mal mit einem Magier.\n");
+    raise_error("Food ohne Werte.\n");
+    return 0;
+  }
+
+  if (!is_eatable()) {
+    notify_fail(eval_property(P_NOFOOD_MSG,env));
+    return 0;
+  }
+
+  return consume(this_player());
+}
+
+// Trinken
+int cmd_drink(string str) {
+  notify_fail("WAS moechtest Du trinken?\n");
+  if (!str || !id(str)) return 0;
+  object env = environment(this_object());
+  if (env != this_player() && QueryProp(P_ENV_MSG)) {
+    notify_fail(eval_property(P_ENV_MSG,env));
+    return 0;
+  }
+
+  if (!is_eatable() && !is_drinkable()) {
+    write("Mit diesem Futter stimmt was nicht!"
+      " Sprich mal mit einem Magier.\n");
+    raise_error("Food ohne Werte.\n");
+    return 0;
+  }
+
+  if (!is_drinkable()) {
+    notify_fail(eval_property(P_NODRINK_MSG,env));
+    return 0;
+  }
+
+  return consume(this_player());
+}
+
+//============================================================================
+// Kompatibilitaetscode
+// um eventuell vergessenes Food oder Zugriff von aussen auf altes Food
+// mitzubekommen
+
+nosave deprecated int alc, alc_pp, food, food_pp, heal, p, soft, soft_pp, c;
+
+deprecated int eat_this( string str ) {
+  return cmd_eat(str);
+}
+
+deprecated int drink_this(string str) {
+  return cmd_drink(str);
+}
+
+deprecated void MakeDrink() {
+  SetProp(P_DRINK,1);
+  SetProp(P_FOOD,0);
+}
+
+deprecated mixed _query_old_is_food() {
+  return is_eatable();
+}
+
+deprecated void _set_old_is_food(mixed value) {
+  SetProp(P_DRINK,0);
+  SetProp(P_FOOD,value);
+}
+
+deprecated mixed _query_old_is_drink() {
+  return is_drinkable();
+}
+
+deprecated void _set_old_is_drink(mixed value) {
+  SetProp(P_DRINK,value);
+  SetProp(P_FOOD,0);
+}
+
+deprecated mixed _query_old_is_full() {
+  return is_not_empty();
+}
+
+deprecated void _set_old_is_full(mixed value) {
+  SetProp(P_PORTIONS,value);
+}
+
+deprecated mixed _query_old_alc() {
+  return QueryProp(P_ALCOHOL);
+}
+
+deprecated void _set_old_alc(mixed value) {
+  SetProp(P_ALCOHOL,value);
+}
+
+deprecated mixed _query_old_water() {
+  return QueryProp(P_DRINK);
+}
+
+deprecated void _set_old_water(mixed value) {
+  SetProp(P_DRINK,value);
+}
+
+deprecated mixed _query_old_food_size() {
+  return QueryProp(P_FOOD);
+}
+
+deprecated void _set_old_food_size(mixed value) {
+  SetProp(P_FOOD,value);
+}
+
+deprecated mixed _query_old_potion() {
+  return QueryProp(P_PORTIONS);
+}
+
+deprecated void _set_old_potion(mixed value) {
+  SetProp(P_PORTIONS,value);
+}
+
+deprecated mixed _query_old_heal_hp() {
+  return QueryProp(P_HP);
+}
+
+deprecated void _set_old_heal_hp(mixed value) {
+  SetProp(P_HP,value);
+}
+
+deprecated mixed _query_old_heal_sp() {
+  return QueryProp(P_SP);
+}
+
+deprecated void _set_old_heal_sp(mixed value) {
+  SetProp(P_SP,value);
+}
+
+deprecated mixed _query_old_bad() {
+  return is_bad();
+}
+
+deprecated void _set_old_bad(mixed value) {
+  return;
+}
+
+deprecated mixed _query_old_mess() {
+  return QueryProp(P_CONSUME_MSG);
+}
+
+deprecated void _set_old_mess(mixed value) {
+  SetProp(P_CONSUME_MSG,value);
+}
+
+deprecated mixed _query_old_eater() {
+  return QueryProp(P_EATER_MSG);
+}
+
+deprecated void _set_old_eater(mixed value) {
+  SetProp(P_EATER_MSG,value);
+}
+
+deprecated mixed _query_old_poison() {
+  return QueryProp(P_POISON);
+}
+
+deprecated void _set_old_poison(mixed value) {
+  SetProp(P_POISON,value);
+}
+
+// interne Methode
+private mixed get_old_empty(string prop) {
+  mixed props = QueryProp(P_EMPTY_PROPS);
+  if (mappingp(props)) return props[prop];
+  return 0;
+}
+// interne Methode
+private void set_old_empty(string prop, mixed value) {
+  mixed props = QueryProp(P_EMPTY_PROPS);
+  if (!mappingp(props)) props = ([prop:value]);
+  else props[prop] = value;
+  SetProp(P_EMPTY_PROPS,props);
+}
+
+deprecated mixed _query_old_empty_con() {
+  return get_old_empty(P_SHORT);
+}
+
+deprecated void _set_old_empty_con(mixed value) {
+  set_old_empty(P_SHORT,value);
+}
+
+deprecated mixed _query_old_empty_gender() {
+  return get_old_empty(P_GENDER);
+}
+
+deprecated void _set_old_empty_gender(mixed value) {
+  set_old_empty(P_GENDER,value);
+}
+
+deprecated mixed _query_old_empty_id() {
+  return get_old_empty(P_IDS);
+}
+
+deprecated void _set_old_empty_id(mixed value) {
+  set_old_empty(P_IDS,value);
+}
+
+deprecated mixed _query_old_empty_long() {
+  return get_old_empty(P_LONG);
+}
+
+deprecated void _set_old_empty_long(mixed value) {
+  set_old_empty(P_LONG,value);
+}
+
+deprecated mixed _query_old_no_con() {
+  return mappingp(QueryProp(P_EMPTY_PROPS));
+}
+
+deprecated void _set_old_no_con(mixed value) {
+  if (value) SetProp(P_EMPTY_PROPS,([]));
+  else SetProp(P_EMPTY_PROPS,0);
+}
+
+// Mupfel
+deprecated int Essbar() {
+  return 1;
+}
+
+deprecated mixed _query_food_info() {
+  return ([]);
+}
+
+deprecated void _set_food_info(mixed value) {
+  return;
+}
+
+// Troy
+deprecated void food_decay() {
+  return;
+}
+
+deprecated int eat_me(string str) {
+  return 0;
+}
diff --git a/std/gilde.c b/std/gilde.c
new file mode 100644
index 0000000..6fb84cf
--- /dev/null
+++ b/std/gilde.c
@@ -0,0 +1,380 @@
+// MorgenGrauen MUDlib
+//
+// gilde.c -- Standardgilde
+//
+// $Id: gilde.c 8388 2013-02-16 17:28:31Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/room";
+
+#include <properties.h>
+#include <defines.h>
+#include <rooms.h>
+#include <wizlevels.h>
+#include <language.h>
+#include <new_skills.h>
+#include <exploration.h>
+#include <ansi.h>
+#include "/secure/questmaster.h"
+#include "/secure/lepmaster.h"
+#include "/secure/config.h"
+#include <events.h>
+
+#define kosten_0 \
+  ([0:                "Bis Du Dich das naechste Mal hier blicken laesst, solltest Du eine Menge Stufenpunkte sammeln. Sonst wird das nix!",\
+    "zauberer":       "Jetzt fang hier ja nicht an zu jammern. Du hast doch grade erst erhoeht. Fuer die naechste Stufe musst Du erstmal Leistung zeigen!",\
+    "kaempfer":       "Hat grade erst erhoeht und faengt schon an zu jammern wie ein altersschwacher Zauberer. Nimm gefaelligst Haltung an!",\
+    "klerus":         "Wer hat Dir denn das Gehirn gelaeutert? Zeig erstmal was von Deinem Koennen, bevor Du schon wieder erhoehen willst!",\
+    "chaos":          "So chaotisch kannst Du doch nicht sein zu glauben, dass Du schon wieder erhoehen kannst!",\
+    "karate":         "Ruh Deine Knochen erstmal ein bisschen aus. Bis zur naechsten Stufe werden sie noch genug leiden muessen!",\
+    "katzenkrieger":  "Da hilft kein Maunzen und kein Schnurren. Du hast grade erst erhoeht und dabei bleibt das!",\
+    "tanjian":        "Jetzt schrei nicht gleich nach Siamil. Du musst noch eine Menge leisten bis zur naechsten Stufe!",\
+    "bierschuettler": "Du hast das Bier auf Deinen letzten Erfolg noch nicht einmal ausgetrunken. Werd jetzt ja nicht groessenwahnsinnig!",\
+    "werwoelfe" :     "Und wenn Du noch so grimmig guckst und den Mond anheulst. Du hast gerade erst erhoeht. Nun musst Du erstmal wieder etwas tun!",\
+  ])
+#define kosten_20 \
+  ([0:                "Da liegt aber noch ein weiter Weg vor Dir. Aber Du schaffst es ganz sicher!",\
+    "zauberer":       "Du kannst zwar zaubern, aber nicht hexen. Fuer die naechste Stufe musst Du schon noch einiges tun!",\
+    "kaempfer":       "Oh weh, da hast Du aber noch ne Menge vor Dir. Beiss die Zaehne zusammen und los gehts!",\
+    "klerus":         "Du wirst bestimmt noch einige Heiltraenke brauchen bis zur naechsten Stufe!",\
+    "chaos":          "Hast Du versucht Dein Chaos zu beseitigen, oder was hast Du gemacht? Sehr weit gekommen biste jedenfalls noch nicht. Musst Dich schon ein bisschen anstrengen!",\
+    "karate":         "Ganz schoen anstrengend immer nur mit Hand und Fuss zu metzeln, oder? Aber es hat Dich schon etwas naeher an die naechste Stufe rangebracht!",\
+    "katzenkrieger":  "Bisschen weniger Fellpflege betreiben, und ein bisschen mehr Stufenpunkte sammeln. Du hast naemlich noch ein gutes Stueck vor Dir!",\
+    "tanjian":        "Du hast noch ne Menge zu tun bis zur naechsten Stufe. Sterb nicht so oft, aber sowas tun Tanjian ja eh nicht!",\
+    "bierschuettler": "Bier trinken alleine wird Dich nicht weiter bringen, obwohl Du schon ein Stueck weit gekommen bist!",\
+    "werwoelfe" :     "Staerke Deine Willenskraft. Du hast noch einiges vor Dir!",\
+  ])
+#define kosten_40 \
+  ([0:                "Also die Haelfte hast Du schon ungefaehr geschafft. Aber ruh Dich jetzt ja nicht darauf aus. Sieh mal zu, dass Du die zweite Haelfte zur naechsten Stufe auch noch schaffst. Auf einem Bein kann man schliesslich nicht lange stehn!",\
+    "zauberer":       "Teile Llystrathe mit Hilfe. Och schade scheint nicht zu funktionieren, dann musst Du eben selber noch ne Menge Stufenpunkte sammeln!",\
+    "kaempfer":       "Kaempfer schnell an die Waffen! Die Haelfte hast Du ungefaehr geschafft, aber es gibt noch viel zu tun!",\
+    "klerus":         "Beile Dich! Das Boese sitzt Dir im Nacken und Du hast noch ungefaehr die Haelfte vor Dir!",\
+    "chaos":          "Schnapp Dir Deinen Daemon und teil Dir die zweite Haelfte der Arbeit mit ihm!",\
+    "karate":         "Du scheinst eingesehen zu haben, dass hirnloses Draufhauen nix nutzt. Die Haelfte haste ungefaehr schon geschafft!",\
+    "katzenkrieger":  "So ungefaehr bis zur Haelfte hast Du Dich schon an die neue Stufe herangeschlichen!",\
+    "tanjian":        "Man kann den Wert zwischen den beiden Stufen durchaus als ausgewogen bezeichnen. Trotzdem hast Du noch ein Stueck des Weges vor Dir!",\
+    "bierschuettler": "War das Fass nu halb voll oder halb leer? Jedenfalls biste ungefaehr bei der Haelfte angekommen!",\
+    "werwoelfe" :     "Noch einige Gegner werden sich vor Dir fuerchten muessen, obwohl Du schon viel geschafft hast!",\
+  ])
+#define kosten_60 \
+  ([0:                "Also ein bisschen was musst Du noch tun, aber es sieht schon ziemlich gut aus!",\
+    "zauberer":       "Ein bisschen Wille und Magie und Du schaffst es wieder ein Stueck naeher an die neue Stufe heranzukommen!",\
+    "kaempfer":       "Staerke Deinen Kampfwillen. Die groessten Anstrengungen hast Du schon hinter Dir!",\
+    "klerus":         "So nah am Ziel wirst Du doch nicht aufgeben wollen. Hol Dir noch ein bisschen goettliche Unterstuetzung und weiter gehts!",\
+    "chaos":          "Chaotisch sein alleine hilft nicht. Ein bisschen was musst Du schon noch tun!",\
+    "karate":         "Die Handkanten geschaerft, den Gi fest zugebunden. Bald hast Du es geschafft!",\
+    "katzenkrieger":  "Auf Samtpfoten schleichst Du mit grossen Schritten ans Ziel heran. Aber ein bisschen fehlt noch!",\
+    "tanjian":        "Eine Quest hier, ein paar Monster da. Aber immer schoen ausgeglichen bleiben, dann klappts auch weiterhin!",\
+    "bierschuettler": "Noch ein paar kraeftige Schlucke und Du hast es fast geschafft!",\
+    "werwoelfe" :     "Verlass Dein Rudel und zieh hinaus ins Abenteuer. Dann wirst Du bald siegreich heimkehren!",\
+  ])
+#define kosten_80 \
+  ([0:                "Huch na sowas, Du hast die naechste Stufe ja schon fast erreicht. Na also! Der Rest ist ja wirklich nur noch Kleinkram!",\
+    "zauberer":       "Die paar fehlenden Puenktchen haste Dir doch rasch zusammengezaubert!",\
+    "kaempfer":       "Das fehlende bisschen machst Du als gnadenlos guter Kaempfer doch mit Links. Jetzt halt Dich aber mal ran. Oder willst Du Dich auf dem letzten Stueck noch von so einem luschigen Zauberer ueberrunden lassen?",\
+    "klerus":         "Bei Saphina, Kandri und Lembold, Du bist fast bei der naechsten Stufe angekommen!",\
+    "chaos":          "Boese waehrt am laengsten. Fast hast Du es geschafft!",\
+    "karate":         "So viele blaue Flecken wie Du an Armen und Beinen hast, kann die naechste Stufe nicht mehr weit sein!",\
+    "katzenkrieger":  "Pass bloss auf, dass Dich auf den letzten Zentimetern nicht noch ein Hund erwischt!",\
+    "tanjian":        "Siamil wird stolz auf Dich sein. Du hast Dich gegen Gut und Boese durchgesetzt und das Ziel fast erreicht!",\
+    "bierschuettler": "Lange musst Du nicht mehr warten. Kannst ruhig schon mal ein frisches Bier in Auftrag geben!",\
+    "werwoelfe" :     "Noch ein letztes Mal die fuerchterlichen Krallen schaerfen, dann hast Du Deinen neuen Level erreicht!",\
+  ])
+
+void create()
+{
+  if (object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);
+      return;
+  }
+  room::create();
+
+  AddCmd("kosten","kosten");
+  AddCmd("liste","liste");
+  AddCmd("erhoehe","advance");
+  AddCmd(({"treff"}), "GotoMagierTreff");
+ }
+
+void init()
+{
+  int lvl;
+  room::init();
+
+  if (PL && query_once_interactive(PL)
+      && (lvl=PL->QueryProp(P_LEVEL)) <= 6
+      && LEPMASTER->QueryLevel(PL->QueryProp(P_LEP)) > lvl)
+  {
+    tell_object(PL,
+      "\nDu koenntest Deine Stufe mit \"erhoehe spieler\" hier in der Gilde "
+      "erhoehen.\n\n");
+  }
+}
+
+string zeige_reboot()
+{
+  string str;
+  int t,t2;
+
+  t=time()-last_reboot_time();
+  t2=t;
+  str="Seit dem letzten Shutdown sind "+t+" Sekunden vergangen.\n";
+  if (t<60) return str;
+  str+="Das sind ";
+  if (t>=86400)
+  {
+    str+=t/86400+" Tag";
+    if (t>=86400*2)
+      str+="e";
+    str+=", ";
+    t2=t2%86400;
+  }
+  if (t>=3600)
+  {
+    str+=t2/3600+" Stunde";
+    if (t2/3600!=1)
+      str+="n";
+    str+=", ";
+    t2=t2%3600;
+  }
+  if (t>=60)
+  {
+    str+=t2/60+" Minute";
+    if (t2/60!=1)
+      str+="n";
+    str+=" und ";
+    t2=t2%60;
+  }
+  str+=t2+" Sekunde";
+  if (t2!=1)
+    str+="n";
+  str+=".\n";
+  return str;
+}
+
+int seer_cond(int silent)
+{
+  int cond;
+
+  cond=LEPMASTER->QueryReadyForWiz(this_player());
+
+  if (!silent)
+    write(break_string(LEPMASTER->QueryReadyForWizText(this_player()),
+          78, 0, 1));
+
+  return cond;
+}
+
+varargs int kosten(string str)
+{
+  string tmp;
+
+  int lep = PL->QueryProp(P_LEP);
+  int lvl = PL->QueryProp(P_LEVEL);
+  int diff = LEPMASTER->QueryNextLevelLEP(lvl, lep);
+
+  switch ( diff ) {
+    case 101..__INT_MAX__:
+      // Falls mal LEPs abhandengekommen sind...
+      tmp = "Wie bist Du ueberhaupt an Deinen Level gekommen?\n"
+      "Sei froh, dass Du nicht wieder abgestuft wirst.";
+      break;
+
+    case 81..100:
+      tmp=kosten_0[PL->QueryProp(P_GUILD)] || kosten_0[0];
+      break;
+
+    case 61..80:
+      tmp=kosten_20[PL->QueryProp(P_GUILD)] || kosten_20[0];
+      break;
+
+    case 41..60:
+      tmp=kosten_40[PL->QueryProp(P_GUILD)] || kosten_40[0];
+      break;
+
+    case 21..40:
+      tmp=kosten_60[PL->QueryProp(P_GUILD)] || kosten_60[0];
+      break;
+
+    case 1..20:
+      tmp=kosten_80[PL->QueryProp(P_GUILD)] || kosten_80[0];
+      break;
+
+    default:
+      if ( lvl < 9 )
+        tmp = "Probier mal den Befehl 'erhoehe'.";
+      else
+        tmp = "Den Befehl 'erhoehe' kennst Du aber, ja?";
+      break;
+  }
+
+  write( break_string( tmp, 78, 0, BS_LEAVE_MY_LFS ) );
+
+  if (!IS_SEER(this_player()) 
+      && ( (str == "lang") ||
+        (this_player()->QueryProp(P_LEVEL) > 12 && str != "kurz"))) {
+    seer_cond(0);
+    write (break_string("\nMit 'kosten kurz' kannst Du die Angabe der "
+          "Seher-Anforderungen unterdruecken.", 78,0,1));
+  }
+
+  return 1;
+}
+
+// ermittelt Gildentitel zum Level <lev>.
+// Der letzte verfuegbare Gildentitel wird nur fuer Seher vergeben.
+// TODO: mit dem entsprechenden Code aus gilden_ob.c vereinigen?
+string get_new_title(int lev, object pl)
+{
+  mapping titles;
+
+  if (!pl) return 0;
+
+  if (lev<0) lev=0;
+
+  if (pl->QueryProp(P_GENDER) == MALE)
+    titles=(mapping)QueryProp(P_GUILD_MALE_TITLES);
+  else
+    titles=(mapping)QueryProp(P_GUILD_FEMALE_TITLES);
+
+  if (!mappingp(titles) || !sizeof(titles)) return 0;
+
+  int maxlevel = max(m_indices(titles));
+
+  // Level begrenzen. Max-Level fuer Seher.
+  if (lev >= maxlevel)
+    lev = IS_SEER(pl) ? maxlevel : maxlevel-1;
+
+  return titles[lev];
+}
+
+// versucht das Spielerlevel zu erhoehen. Macht nicht den ganzen Krams
+// drumrum, den advance() aus hysterischen Gruenden tut.
+int try_player_advance(object pl) {
+
+  if (PL->QueryProp(P_KILLS)>0)
+    return -1;
+
+  int level = pl->QueryProp( P_LEVEL );
+  if (level == -1) level = 0;
+
+  if (LEPMASTER->QueryNextLevelLEP(level, pl->QueryProp(P_LEP)) > 0)
+      return 0;
+  else
+      ++level;
+
+  pl->SetProp( P_LEVEL, level );
+
+  // Aufstiegs-Event ausloesen
+  EVENTD->TriggerEvent(EVT_LIB_ADVANCE, ([
+        E_OBJECT: PL, E_PLNAME: getuid(PL),
+        E_ENVIRONMENT: environment(PL),
+        E_GUILDNAME: PL->QueryProp(P_GUILD),
+        P_LEVEL: PL->QueryProp(P_LEVEL),
+        ]) );
+
+  // Falls die konkrete Gilde des Spielern irgedwas mit dem Titel in
+  // ABhaengigkeit des Spielerlevels tun will. Ausnahmsweise per call_other,
+  // die Funktion kommt eigentlich aus /std/gilden_ob.c.
+  string gname=(string)pl->QueryProp(P_GUILD);
+  (GUILD_DIR+"/"+gname)->adjust_title(pl);
+
+  return 1;
+}
+
+// "erhoehe"-Kommando.
+// erhoeht das Spielerlevel, falls moeglich. Setzt den passenden
+// Abenteurergildentitel.
+int advance(string arg)
+{
+
+  if (arg &&
+      arg != "stufe" && arg != "spielerstufe" && arg != "spieler"
+      && arg != "spielerlevel")
+    return 0;
+
+  int res = try_player_advance(PL);
+
+  if (!res)
+    return kosten("kurz");
+  else if (res < 0) {
+    notify_fail(break_string(
+        "Du hast einen Mitspieler umgebracht!\n"+
+        "In diesem Fall kannst Du Deine Stufe nicht erhoehen.\n"+
+        "Bitte wende Dich an den Sheriff (oder einen Erzmagier) und bring "
+        "das in Ordnung.\n",78,BS_LEAVE_MY_LFS));
+    say(break_string(PL->Name(WER) 
+          + " hat soeben auf schmerzliche Weise erfahren muessen, dass "
+          "es wirklich nicht foerderlich ist, Mitspieler umzubringen.\n",
+          78), PL);
+    return 0;
+  }
+
+  string name_of_player = PL->name(WER);
+  int level = PL->QueryProp(P_LEVEL);
+  say( name_of_player + " hat jetzt Stufe " + level + " erreicht.\n");
+
+  string title = PL->QueryProp(P_TITLE);
+
+  switch(random(3)) {
+    case 0:
+      write("Du bist jetzt " + name_of_player + " " + title +
+        " (Stufe " + level + ").\n");
+      break;
+
+    case 1:
+      write("Gut gemacht, " + name_of_player + " " + title +
+        " (Stufe " + level + ").\n");
+      break;
+
+    default:
+      write("Willkommen auf Deiner neuen Stufe.\n" +
+        "Du bist jetzt " + name_of_player+" "+ title +
+        " (Stufe " + level + ").\n");
+      break;
+  }
+
+  return 1;
+}
+
+// Spielerkommando fuer Abenteurerliste
+varargs int liste(mixed pl)
+{
+  string str; 
+  if (!this_player()) return 0;
+
+  if(!objectp(pl))
+    if(stringp(pl))
+      pl=find_player(pl)||find_netdead(pl);
+  if(!objectp(pl))
+    pl=PL;
+  if (pl != this_player()) {
+    write ("Du kannst Dir nur Deine eigenen Abenteuer ansehen.\n");
+    return 1;
+  }
+
+  str = QM->liste(pl);
+
+  this_player()->More( str, 0 );
+  return 1;
+}
+
+int GotoMagierTreff()
+{
+  if(IS_LEARNER(this_player()))
+  {
+    write("Ein Zauberspruch zieht vor Deinem geistigen Auge vorbei und Du\n"
+    "sprichst ihn nach.\n");
+    say(PL->name()+" murmelt einen geheimen Zauberspruch und schwebt langsam\n"
+    "zur Decke hinauf und verschwindet durch die Wand.\n");
+    write("Du schwebst langsam zur Decke hinauf und als ob diese nicht da\n"
+    "waere mitten hindurch in den Magiertreff.\n");
+    return (PL->move("/secure/merlin", M_TPORT | M_SILENT ) >= 0);
+  }
+  write("Du springst zur Decke hinauf und nix passiert.\n");
+  return 1;
+}
+
diff --git a/std/gilden_ob.c b/std/gilden_ob.c
new file mode 100644
index 0000000..6805acc
--- /dev/null
+++ b/std/gilden_ob.c
@@ -0,0 +1,462 @@
+// MorgenGrauen MUDlib
+//
+// gilden_ob.c -- Basisfunktionen einer Gilde
+//
+// $Id: gilden_ob.c 8433 2013-02-24 13:47:59Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma no_shadow
+#pragma no_clone
+#pragma range_check
+#pragma pedantic
+
+inherit "/std/restriction_checker";
+#include <properties.h>
+#include <attributes.h>
+#include <new_skills.h>
+#include <defines.h>
+#include <player/fao.h>
+#include <wizlevels.h>
+
+void create() {
+    // P_GUILD_SKILLS sollte nicht direkt von aussen manipuliert werden...
+    SetProp(P_GUILD_SKILLS,([]));
+    Set(P_GUILD_SKILLS,PROTECTED,F_MODE_AS);
+}
+
+mapping _query_guild_skills() {
+    // eine Kopie zurueckliefern, um versehentliche Aenderungen des
+    // Originalmappings in der Gilde zu vermeiden.
+    // Darf nicht ohne weiteres entfernt werden, da es hier im File Code gibt,
+    // der sich auf die Kopie verlaesst.
+    return(deep_copy(Query(P_GUILD_SKILLS)));
+}
+
+string GuildName() {
+  //Gilden muessen Blueprints sein, so. ;-)
+  return object_name(this_object())[8..];
+}
+
+varargs int
+IsGuildMember(object pl) {
+  string plg;
+
+  if (!pl && !(pl=this_player()))
+    return 0;
+  if (!(plg=(string)pl->QueryProp(P_GUILD)))
+    return 0;
+  if (GuildName()!=plg) {
+    _notify_fail("Du gehoerst dieser Gilde nicht an!\n");
+    return 0;
+  }
+  return 1;
+}
+
+int check_cond(mixed cond) {
+  string res;
+
+  if (intp(cond)) {
+    _notify_fail("Dir fehlt noch die noetige Erfahrung. "+
+                "Komm spaeter wieder.\n");
+    return (this_player()->QueryProp(P_XP)>=cond);
+  }
+  if (mappingp(cond)) {
+    res=check_restrictions(this_player(),cond);
+    if (!stringp(res)) return 1;
+    _notify_fail(res);
+    return 0;
+  }
+  return 1;
+}
+
+int can_advance() {
+  int lv;
+  mapping lvs;
+
+  if (!(lv=(int)this_player()->QueryProp(P_GUILD_LEVEL))) return 1;
+  if (!(lvs=QueryProp(P_GUILD_LEVELS))) return 0;
+  return check_cond(lvs[lv+1]); // Bedingung fuer naechsten Level testen.
+}
+
+void adjust_title(object pl) {
+  int lv;
+  mixed ti;
+
+
+  if (!pl ||
+      !(lv=(int)pl->QueryProp(P_GUILD_LEVEL)))
+    return;
+  switch((int)pl->QueryProp(P_GENDER)) {
+  case MALE:
+    ti=QueryProp(P_GUILD_MALE_TITLES);
+    break;
+  case FEMALE:
+    ti=QueryProp(P_GUILD_FEMALE_TITLES);
+    break;
+  default:
+    return;
+  }
+  if (mappingp(ti))
+    ti=ti[lv];
+  if (stringp(ti))
+    pl->SetProp(P_GUILD_TITLE,ti);
+  
+  // Spielertitel nullen, damit der Gildentitel angezeigt wird.
+  if (!IS_SEER(pl) && !FAO_HAS_TITLE_GIFT(pl))
+      pl->SetProp(P_TITLE,0);
+}
+
+void do_advance() {
+  int lv;
+
+  lv=this_player()->QueryProp(P_GUILD_LEVEL)+1;
+  if (lv<1) lv=1;
+  this_player()->SetProp(P_GUILD_LEVEL,lv);
+  adjust_title(this_player());
+}
+
+int try_advance() {
+  if (can_advance()) {
+    if (IsGuildMember(this_player()))
+      do_advance();
+    return 1;
+  }
+  return 0;
+}
+
+int beitreten() {
+  string res;
+  int erg;
+
+  if (res=check_restrictions(this_player(),QueryProp(P_GUILD_RESTRICTIONS))) {
+    // Werden die Beitrittsbedingungen erfuellt?
+    printf("Du kannst dieser Gilde nicht beitreten.\nGrund: %s",res);
+    return -3;
+  }
+  if (erg=(int)GUILDMASTER->beitreten()) {
+    if (erg<0)
+      return erg;
+    if (!(this_player()->QueryProp(P_GUILD_LEVEL)))
+      try_advance(); // Level 1 wird sofort vergeben
+    return 1;
+  }
+  return 0;
+}
+
+varargs int austreten(int loss) {
+    return (int)GUILDMASTER->austreten(loss);
+}
+
+int bei_oder_aus_treten(string str) {
+  if (!str) return 0;
+  if (sizeof(regexp(({lower_case(str)}),
+                    "\\<aus\\>.*gilde\\>.*\\<aus\\>")))
+    return austreten();
+  if (sizeof(regexp(({lower_case(str)}),
+                     "(gilde\\>.*\\<bei\\>|\\<in\\>.*gilde\\>.*\\<ein\\>)")))
+    return beitreten();
+  return 0;
+}
+
+varargs int
+AddSkill(string sname, mapping ski) {
+  mapping skills;
+
+  if (!sname)
+    return 0;
+
+  // per Query() abfragen, hier ist keine Kopie noetig.
+  if (!mappingp(skills=Query(P_GUILD_SKILLS))) {
+    skills=([]);
+    SetProp(P_GUILD_SKILLS,skills);
+  }
+  
+  if (!mappingp(ski))
+      ski=([]);
+  else
+      //Zur Sicherheit Kopie erstellen, wer weiss, was der Eintragende noch
+      //mit seinem Mapping macht...
+      ski=deep_copy(ski);
+
+  if (!stringp(ski[SI_SKILLFUNC]))
+    // Wenn keine Funktion angegeben ist, Funktionsname=Skillname
+    ski[SI_SKILLFUNC]=sname;
+
+  // Gilden-Offsets addieren
+  ski=AddSkillMappings(QueryProp(P_GLOBAL_SKILLPROPS),ski);
+
+  // Skill setzen.
+  skills[sname]=ski;
+  //SetProp() unnoetig, da oben per Query() das Originalmapping erhalten
+  //wurde.
+  //SetProp(P_GUILD_SKILLS,skills);
+  return 1;
+}
+
+
+varargs int
+AddSpell(string verb, mapping ski) {
+  mapping skills;
+
+  if (!verb)
+      return 0;
+  if (!mappingp(ski))
+      ski=([]);
+
+  if (!stringp(ski[SI_SPELLBOOK]) &&
+      !stringp(ski[SI_SPELLBOOK]=QueryProp(P_GUILD_DEFAULT_SPELLBOOK)))
+    // Wenn kein Spellbook angegeben ist muss ein
+    // Default-Spellbook angegeben sein, sonst Fehler
+    return 0;
+  if (file_size(SPELLBOOK_DIR+ski[SI_SPELLBOOK]+".c")<0)
+    return 0; // Spellbook sollte auch existieren...
+
+  return AddSkill(lower_case(verb),ski);
+}
+
+mapping QuerySkill(string skill) {
+  mapping ski;
+
+  // Abfrage per Query(), da vielleicht nur ein Skill gewuenscht
+  // wird und daher die komplette Kopie des Spellmappings in der Query-Methode
+  // unnoetig ist.
+  if (!skill
+      || !(ski=Query(P_GUILD_SKILLS, F_VALUE))
+      || !(ski=ski[skill])) // Gildenspezifische Skilleigenschaften
+    return 0;
+  // hier aber dann natuerlich eine Kopie erstellen. ;-)
+  return(deep_copy(ski));
+}
+
+mapping QuerySpell(string spell) {
+  mapping ski,ski2;
+  string spellbook,sfunc;
+
+  // QuerySkill() hier und QuerySpell() im Spellbook liefern Kopien der
+  // Skillmappings zurueck, Kopieren hier daher unnoetig.
+  if (!spell
+      || !(ski=QuerySkill(spell))
+      || !(spellbook=ski[SI_SPELLBOOK]))
+    return 0;
+  if (!(sfunc=ski[SI_SKILLFUNC]))
+    sfunc=spell;
+  spellbook=SPELLBOOK_DIR+spellbook;
+  if (!(ski2=(mapping)(spellbook->QuerySpell(sfunc))))
+    return 0;
+  return AddSkillMappings(ski2,ski); // Reihenfolge wichtig!
+  // Die Gilde kann Spelleigenschaften neu definieren!
+}
+
+varargs int
+UseSpell(object caster, string spell, mapping sinfo) {
+  mapping ski;
+  string spellbook;
+
+  if (!caster
+      || !spell
+      || !(ski=QuerySkill(spell)) // Existiert dieser Spell in dieser Gilde?
+      || !(spellbook=ski[SI_SPELLBOOK])) // Spellbook muss bekannt sein
+    return 0;
+  if (sinfo)
+    ski+=sinfo;
+  spellbook=SPELLBOOK_DIR+spellbook;
+  // printf("%O %O %O %O\n",spellbook,caster,spell,ski);
+  return (int)spellbook->UseSpell(caster,spell,ski);
+}
+
+static int
+InitialSkillAbility(mapping ski, object pl) {
+  if (!ski || !pl) return 0;
+  return (300*GetOffset(SI_SKILLLEARN,ski,pl)+
+          (200*(int)pl->QueryAttribute(A_INT)*
+           GetFactor(SI_SKILLLEARN,ski,pl))/100);
+}
+
+int
+SkillListe(int what) {
+  int res;
+  // Querymethode erstellt Kopie (wichtig!)
+  mapping allskills=QueryProp(P_GUILD_SKILLS);
+  string *skills=({});
+  string *spells=({});
+
+  if (!mappingp(allskills) || !sizeof(allskills))
+    return 0;
+
+  foreach(string s, mapping sdata: &allskills) {
+    // Lernlevel ermitteln und speichern.
+    mapping tmp=QuerySpell(s);
+    // wenn nix gefunden, dann ist es ein Skill, sonst ein Spell.
+    if (tmp) {
+      spells += ({s});
+      // das Spellbook hat im Zweifel das SI_SKILLINFO
+      sdata[SI_SKILLINFO] = tmp[SI_SKILLINFO];
+    }
+    else {
+      // SI_SKILLINFO steht in diesem Fall schon in sdata...
+      tmp = QuerySkill(s);
+      skills += ({s});
+    }
+    // gucken, obs nen Lernlevel gibt und einfach in sdata schreiben
+    if ( (tmp=tmp[SI_SKILLRESTR_LEARN]) )
+      sdata["_restr_level"] = tmp[P_LEVEL];
+  }
+
+  // jetzt sortieren.
+  closure cl = function int (string a, string b)
+    { return allskills[a]["_restr_level"] > allskills[b]["_restr_level"]; };
+  if (what & 0x01)
+    spells = sort_array(spells, cl);
+  if (what & 0x02)
+    skills = sort_array(skills, cl);
+
+  // und ausgeben
+  cl = function void (string *list)
+    {
+      string tmp="";
+      int lvl;
+      foreach(string sp: list) {
+        lvl = allskills[sp]["_restr_level"];
+        if (lvl>0)
+          tmp += sprintf("%-20s %d\n",sp,lvl);
+        else
+          tmp += sprintf("%-20s\n",sp);
+        if (allskills[sp][SI_SKILLINFO])
+          tmp += break_string(allskills[sp][SI_SKILLINFO], 78,
+                              "    ");
+      }
+      tell_object(PL, tmp);
+    };
+
+  if ((what & 0x01) && sizeof(spells)) {
+    res = 1;
+    tell_object(PL,sprintf(
+        "Du kannst versuchen, folgende Zaubersprueche zu lernen:\n"
+        "%-20s %-3s\n","Zauberspruch","Spielerstufe"));
+    funcall(cl, spells);
+    tell_object(PL,break_string(
+       "Du kannst einen Zauberspruch mit dem Kommando \"lerne\", gefolgt "
+       "vom Zauberspruchnamen lernen.",78));
+  }
+  if ((what & 0x02) && sizeof(skills)) {
+    res = 1;
+    tell_object(PL,sprintf(
+        "Du kannst versuchen, folgende Faehigkeiten zu erwerben:\n"
+        "%-20s %-3s\n","Faehigkeit","Spielerstufe"));
+    funcall(cl, skills);
+  }
+
+  return res;
+}
+
+varargs int
+LearnSpell(string spell,object pl) {
+  mapping ski,restr;
+  string res;
+  mixed learn_initfunc;
+  int abil,diff;
+
+  // Wenn kein pl gesetzt ist, nehmen wir this_player(), das ist der
+  // Normalfall.
+  if (!pl)
+	  pl=this_player();
+  if (!IsGuildMember(pl)) {
+    _notify_fail("Du gehoerst dieser Gilde nicht an!\n");
+    return 0;
+  }
+  _notify_fail("Was moechtest Du lernen?\n");
+  if (!spell)
+    return SkillListe(0x01);
+  spell=lower_case(spell);
+  if (!(ski=QuerySpell(spell)))
+    return 0;
+  if (pl->QuerySkill(spell)) {
+    _notify_fail("Du kannst diesen Spruch doch schon!\n");
+    return 0;
+  }
+  if ((restr=ski[SI_SKILLRESTR_LEARN])
+      && (res=check_restrictions(pl,restr))) {
+    printf("Du kannst diesen Spruch noch nicht lernen.\nGrund: %s",res);
+    return 1;
+  }
+  abil=InitialSkillAbility(ski,pl);
+  if (abil<1) abil=1;
+  if (abil>7500) abil=7500;
+  write("Du lernst einen neuen Zauberspruch.\n");
+  if (!(diff=GetFValueO(SI_DIFFICULTY,ski,pl)))
+    diff=GetFValueO(SI_SPELLCOST,ski,pl);
+  pl->ModifySkill(spell,abil,diff);
+  return 1;
+}
+
+int
+LearnSkill(string skill) {
+  mapping ski,restr;
+  object pl;
+  string res;
+  mixed learn_initfunc;
+  int abil,diff;
+
+  if (!IsGuildMember(pl=this_player())) {
+    _notify_fail("Du gehoerst dieser Gilde nicht an!\n");
+    return 0;
+  }
+  _notify_fail("Was moechtest Du lernen?\n");
+  if (!skill)
+    return SkillListe(0x02);
+  skill=capitalize(skill);
+  if (!(ski=QuerySkill(skill)))
+    return 0;
+  if (pl->QuerySkill(skill)) {
+    _notify_fail("Du hast diese Faehigkeit doch schon!\n");
+    return 0;
+  }
+  if ((restr=ski[SI_SKILLRESTR_LEARN])
+      && (res=check_restrictions(pl,restr))) {
+    printf("Du kannst diese Faehigkeit noch nicht erwerben.\nGrund: %s",res);
+    return 1;
+  }
+  abil=InitialSkillAbility(ski,pl);
+  if (!abil) abil=1;
+  if (abil>MAX_ABILITY) abil=MAX_ABILITY;
+  if (abil<-MAX_ABILITY) abil=-MAX_ABILITY;
+  write("Du erwirbst eine neue Faehigkeit.\n");
+  diff=GetFValueO(SI_DIFFICULTY,ski,pl);
+  pl->ModifySkill(skill,abil,diff);
+  return 1;
+}
+
+int GuildRating(object pl)
+{
+  mapping ski;
+  string *sk;
+  int i, cnt, sum;
+  closure qsa;
+  
+  if (!IsGuildMember(pl)||!query_once_interactive(pl))
+    return 0;
+
+  if (!(ski = QueryProp(P_GUILD_SKILLS)) ||
+      !(qsa = symbol_function("QuerySkillAbility",pl)))
+    return 0;
+
+  sk = m_indices(ski);
+  cnt = sizeof(ski);
+  
+  for (i=cnt-1, sum=0; i>=0; i--)
+    sum += funcall(qsa, sk[i]);
+
+  sum = sum/cnt;
+  if (sum < 0)
+    sum = 0;
+  else if (sum > MAX_ABILITY)
+    sum = MAX_ABILITY;
+  
+  return (int)pl->SetProp(P_GUILD_RATING, sum);
+}
+
+// Wird von /std/player/quest.c aufgerufen, wenn Quest geloest.
+// (Jedes mal - nicht nur beim ersten mal.)
+// Ist zum selbst-ueberschreiben gedacht, sollte aber definiert sein.
+void NotifyGiveQuest(object pl, string key){  }
+
diff --git a/std/gilden_room.c b/std/gilden_room.c
new file mode 100644
index 0000000..763de59
--- /dev/null
+++ b/std/gilden_room.c
@@ -0,0 +1,33 @@
+// MorgenGrauen MUDlib
+//
+// gilden_room.c -- Gildengebaeude
+//
+// $Id: gilden_room.c 6380 2007-07-20 22:32:38Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+// #define NEED_PROTOTYPES
+inherit "/std/gilde";
+inherit "/std/gilden_ob";
+
+#include <properties.h>
+#include <attributes.h>
+#include <new_skills.h>
+
+void create() {
+  gilde::create();
+  gilden_ob::create();
+  AddCmd(({"tritt","trete"}),"bei_oder_aus_treten");
+  AddCmd(({"lern","lerne"}),"LearnSpell");
+}
+
+int advance(string arg) {
+  int r1,r2;
+
+  r1=gilde::advance(arg);
+  r2=gilden_ob::try_advance();
+  return (r1||r2);
+}
diff --git a/std/hook_consumer.c b/std/hook_consumer.c
new file mode 100644
index 0000000..1da03b3
--- /dev/null
+++ b/std/hook_consumer.c
@@ -0,0 +1,19 @@
+// MorgenGrauen MUDlib

+//

+// /std/hook_consumer.c  - used to contain some template for hook consumers,

+//                         not needed anymore.

+//

+

+#pragma strong_types

+#pragma save_types

+#pragma no_clone

+#pragma pedantic

+#pragma range_check

+

+#include <hook.h>

+

+deprecated mixed HookCallback(object hookSource, int hookid, mixed hookData)

+{

+  return ({H_NO_MOD,hookData});

+}

+

diff --git a/std/hook_provider.c b/std/hook_provider.c
new file mode 100644
index 0000000..00b8ca3
--- /dev/null
+++ b/std/hook_provider.c
@@ -0,0 +1,493 @@
+// MorgenGrauen MUDlib
+//
+// /std/hook_provider.c  - Hooksystem
+//
+// $Id: hook_provider.c 9453 2016-02-04 21:22:54Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma range_check
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <hook.h>
+#undef NEED_PROTOTYPES
+
+// get the object fur closure <cl> (where the code is!)
+#define GET_OBJECT(cl) get_type_info(cl,2)
+
+// Struct describing one hook consumer. <cl> will be called when the hook is
+// triggered. The lower <prio>, the earlier the consumer will be called.
+// <type> is a consumertype, e.g. surveyor, listener etc. (see hook.h)
+struct hook_entry_s {
+  closure cl;
+  int prio;
+  int endtime;
+  int type;
+};
+
+// Struct holding the consumers of one specific hook.
+// the value arrays are guarenteed to exist, but may be empty.
+struct hook_s {
+  struct hook_entry_s *surveyors;
+  struct hook_entry_s *hmods;
+  struct hook_entry_s *dmods;
+  struct hook_entry_s *listeners;
+};
+
+/* hook mapping
+   the list of all offered hooks in the following structure:
+   ([hookid: (<hook_s>), ...
+   )]
+*/
+private nosave mapping hookMapping=([]);
+
+protected void CleanHookMapping(int *hookids);
+
+// Debugging - ggf. ueberschreiben
+protected int h_dbg() {return 0;}
+
+void HookTestOffer(int id, int stat){
+  if(h_dbg()) {
+      offerHook(id,stat);
+  }
+}
+
+void HookTestTrigger(int id, mixed data){
+  if(h_dbg()) {
+      HookFlow(id,data);
+  }
+}
+
+// NOTE: if you have the closure, you can call the lfun, even if it is
+// private. Therefore access to this data should be restricted to
+// this_object().
+// These two functions should be used for debugging purposes.
+protected mapping HCopyHookMapping(){
+  return deep_copy(hookMapping);
+}
+
+protected mapping HCopyHookConsumers(int hookid){
+  if(member(hookMapping,hookid)) {
+      CleanHookMapping(({hookid}));
+      return deep_copy(hookMapping[hookid]);
+  }
+  return 0;
+}
+
+// Ggf. zum Ueberschreiben.
+int HConsumerTypeIsAllowed(int type, object consumer){
+  return 1;
+}
+
+int HPriorityIsAllowed(int prio, object consumer){
+  return 1;
+}
+
+// clean internal hook data structures of stale hook consumers.
+protected void CleanHookMapping(int *hookids){
+  // hooks enthaelt die aufzuraeumenden Hooks. Wenn kein Array -> alle Hooks
+  if (!pointerp(hookids))
+    hookids=m_indices(hookMapping);
+
+  foreach(int hookid : hookids) { // alle Hooks
+    struct hook_s hooks = hookMapping[hookid];
+    if (!structp(hooks))
+      continue;
+    // ueber alle Consumertypen laufen
+    foreach (string consumertype: H_CONSUMERNAMES)
+    {
+      // Yeah - compute struct lookup at runtime... ;-)
+      struct hook_entry_s *consumers = hooks->(consumertype);
+      // Hookeintraege / Consumer
+      foreach(struct hook_entry_s h : &consumers) {
+        // alle abgelaufenen Eintraege oder solche mit zerstoerten Objekten
+        // nullen
+        if (!h->cl || h->endtime < time() )
+            h = 0;
+      }
+      // 0 noch rauswerfen.
+      hooks->(consumertype) -= ({0});
+    }
+  }
+}
+
+protected void offerHook(int hookid, int offerstate)
+{
+  H_DMSG(sprintf("offerHook hookid %d offerstate %d\n",hookid,offerstate));
+  if (hookid>0)
+  {
+      if (offerstate) {
+          if (!member(hookMapping,hookid)) {
+              struct hook_s hook = (<hook_s>
+                  surveyors: ({}),
+                  hmods: ({}),
+                  dmods: ({}),
+                  listeners: ({}) );
+              hookMapping[hookid] = hook;
+          }
+      }
+      else {
+          if (member(hookMapping,hookid)) {
+            m_delete(hookMapping,hookid);
+          }
+      }
+  }
+  H_DMSG(sprintf("  result %O\n",hookMapping));
+}
+
+// hookConsumerInfo() liefert Array von hook_entry_s zurueck. D.h. bei Abfrage
+// von Objekten alle Closures dieses Objekts und jede davon erzeugt ein
+// Element hook_entry_s im Ergebnisarray. Bei Abfrage von Closures hat das
+// Array immer genau 1 oder kein Element.
+// WARNING: whoever has a hook_entry_s can change/delete the hook!
+//          NEVER return the original to an external caller!
+// NOTE: whoever has the cl from hook_entry_s can call it, even if the lfun
+//       is private (and this object the only one knowing it).
+protected mixed * hookConsumerInfo(int hookid, object|closure consumer)
+{
+  closure filter_cl;
+
+  if (!member(hookMapping,hookid))
+    return ({});
+
+  // Closure zum Filtern bestimmen - je nachdem, was gesucht wird.
+  if (closurep(consumer))
+  {
+    filter_cl = function int (struct hook_entry_s h)
+                { return h->cl == consumer
+                         && h->endtime >= time(); };
+  }
+  else if (objectp(consumer))
+  {
+    filter_cl = function int (struct hook_entry_s h)
+                { return GET_OBJECT(h->cl) == consumer
+                         && h->endtime >= time(); };
+  }
+  else
+  {
+    return ({});
+  }
+
+  struct hook_s hook = hookMapping[hookid];
+  struct hook_entry_s *result = ({});
+  foreach (string consumertype: H_CONSUMERNAMES )
+  {
+    result += filter(hook->(consumertype), filter_cl);
+  }
+  return result;
+}
+
+int HIsHookConsumer(int hookid, mixed consumer) {
+  return sizeof(hookConsumerInfo(hookid,consumer)) != 0;
+}
+
+int* HListHooks() {
+  return m_indices(hookMapping);
+}
+
+int HUnregisterFromHook(int hookid, mixed consumer) {
+
+  H_DMSG(sprintf("HUnregisterFromHook hookid %d consumer %O\n",hookid,consumer));
+  if (objectp(consumer))
+    consumer = symbol_function("HookCallback", consumer);
+  if (!closurep(consumer))
+    return 0;
+
+  struct hook_entry_s *info = hookConsumerInfo(hookid,consumer);
+
+  // it should never happen that a closure is registered more than once, i.e.
+  // the result contains more than one element.
+  if (sizeof(info)) {
+      struct hook_entry_s h = info[0];
+      h->cl = 0;
+      H_DMSG(sprintf("  result %O\n", hookMapping));
+      // the now invalid h will be cleaned up later.
+      return 1;
+  }
+  return 0;
+}
+
+// surveyors are asked for registration permittance
+protected int askSurveyorsForRegistrationAllowance(
+                  struct hook_entry_s *surveyors, object consumer,int hookid,
+                  int hookprio,int consumertype)
+{
+  H_DMSG(sprintf("askSurveyorsForRegistrationAllowance surveyors %O, "
+        "consumer %O, hookid %d, hookprio %d, consumertype %d\n",
+        surveyors,consumer,hookid,hookprio,consumertype));
+
+  foreach(struct hook_entry_s surveyor : surveyors) {
+    if (closurep(surveyor->cl) && surveyor->endtime >= time())
+    {
+      // surveyor hook gueltig.
+      object sob = GET_OBJECT(surveyor->cl);
+      if (!sob->HookRegistrationCallback(consumer, hookid,
+                        this_object(), hookprio, consumertype))
+        return 0;
+    }
+  }
+  return 1;
+}
+
+int HRegisterToHook(int hookid, mixed consumer, int hookprio,
+                       int consumertype, int timeInSeconds) {
+  int ret, regtime;
+
+  if (!closurep(consumer) && !objectp(consumer))
+    raise_error(sprintf("Wrong argument %.50O to HRegisterToHook(): consumer "
+          "must be closure or object.\n",consumer));
+
+  if (!member(hookMapping, hookid))
+    return -1;
+
+  if (objectp(consumer)) {
+    consumer = symbol_function("HookCallback", consumer);
+    if (!closurep(consumer))
+      return -2;
+  }
+
+  if (timeInSeconds > 0) {
+      regtime=time() + timeInSeconds;
+  }
+  else {
+      regtime=__INT_MAX__;
+  }
+
+  H_DMSG(sprintf("HRegisterToHook hookid %d consumer %O\n hookprio %d "
+        "consumertype %d\n",hookid,consumer,hookprio,consumertype));
+
+  CleanHookMapping(({hookid})); // entfernt ungueltige/abgelaufene Eintraege
+
+  // nur einmal pro closure registrieren!
+  if (HIsHookConsumer(hookid, consumer))
+    return -3;
+
+  // Consumertyp erlaubt?
+  if (H_CONSUMERCHECK(consumertype) == -1
+      || !HConsumerTypeIsAllowed(consumertype,GET_OBJECT(consumer)))
+    return -4;
+
+  // Prioritaet erlaubt?
+  if (H_HOOK_VALIDPRIO(hookprio) == -1
+      || !HPriorityIsAllowed(hookprio, GET_OBJECT(consumer)))
+    return -5;
+
+  struct hook_s hook = hookMapping[hookid];
+
+  // Und surveyors erlauben die Registierung?
+  if (!askSurveyorsForRegistrationAllowance(hook->surveyors,
+                    GET_OBJECT(consumer),hookid,
+                    hookprio,consumertype))
+    return -6;
+
+  string ctypename = H_CONSUMERNAMES[consumertype];
+
+  // get the consumer array
+  struct hook_entry_s *consumers = hook->(ctypename);
+
+  // assemble new hook consumer struct
+  struct hook_entry_s newconsumer = (<hook_entry_s>
+                      cl : consumer,
+                      prio : hookprio,
+                      endtime : regtime,
+                      type : consumertype );
+
+  // consumers enthaelt die Hookeintraege
+  if (sizeof(consumers) < MAX_HOOK_COUNTS[consumertype]) {
+    // max. Anzahl an Eintraegen fuer diesen Typ noch nicht
+    // erreicht, direkt anhaengen.
+    consumers += ({ newconsumer });
+    hook->(ctypename) = consumers;
+    ret=1;
+  }
+  else {
+    // gibt es einen Eintrag mit hoeherem Priowert (niedrigere
+    // Prioritaet), den man ersetzen koennte?
+    // Das Array ist sortiert, mit hoechsten Priowerten am
+    // Ende. Ersetzt werden soll der Eintrag mit dem hoechsten
+    // Zahlenwert, falls der neue Eintrag einen niedrigeren Wert
+    // hat, d.h. es muss nur er letzte Consumer im Array geprueft werden.
+    // Pruefung auf Closureexistenz, falls der Surveyor Objekte
+    // zerstoert (hat)...
+    struct hook_entry_s oh = consumers[<1];
+    if (!oh->cl || oh->prio > newconsumer->prio) {
+      // Found superseedable entry - replace it, but inform the object.
+      H_DMSG("Found superseedable entry\n");
+      consumers[<1] = newconsumer;
+      GET_OBJECT(oh->cl)->superseededHook(hookid, this_object());
+      ret = 1;
+      // nicht noetig, consumers wieder in sein hook_s zu haenngen wegen Array
+      // -> Referenz
+    }
+  }
+
+  // wenn ein Eintrag hinzugefuegt wurde, muss neu sortiert werden
+  if (ret) {
+    hook->(ctypename) = sort_array(consumers,
+        function int (struct hook_entry_s a, struct hook_entry_s b) {
+            return a->prio > b->prio; } );
+  }
+
+  H_DMSG(sprintf("  result %O\n",hookMapping));
+
+  // -7, wenn kein Eintrag mehr frei / zuviele Hooks
+  return (ret > 0 ? 1 : -7);
+}
+
+// Conveniences wrapper for simple listener hooks
+int HRegisterListener(int hookid, mixed consumer)
+{
+  return HRegisterToHook(hookid, consumer, H_HOOK_OTHERPRIO(2), H_LISTENER, 0);
+}
+
+// Cnveniences wrapper for simple modificator hooks
+int HRegisterModifier(int hookid, mixed consumer)
+{
+  return HRegisterToHook(hookid, consumer, H_HOOK_OTHERPRIO(2),
+                         H_HOOK_MODIFICATOR, 0);
+}
+
+// surveyors are asked for cancellation permittance
+protected int askSurveyorsForCancelAllowance(mixed surveyors,
+  object modifiyingOb,mixed data,int hookid,int prio,object hookOb){
+
+  foreach(struct hook_entry_s surveyor : surveyors) {
+    if (closurep(surveyor->cl) && surveyor->endtime >= time())
+    {
+      // surveyor hook gueltig.
+      object sob = GET_OBJECT(surveyor->cl);
+      if (!sob->HookCancelAllowanceCallback(modifiyingOb, hookid,
+                        hookOb, prio, data))
+        return 0;
+    }
+  }
+  return 1;
+}
+
+// surveyors are asked for data change permittance
+protected int askSurveyorsForModificationAllowance(mixed surveyors,
+  object modifiyingOb,mixed data,int hookid,int prio,object hookOb){
+
+  foreach(struct hook_entry_s surveyor : surveyors) {
+    if (closurep(surveyor->cl) && surveyor->endtime >= time())
+    {
+      // surveyor hook gueltig.
+      object sob = GET_OBJECT(surveyor->cl);
+      if (!sob->HookModificationAllowanceCallback(modifiyingOb,
+                        hookid, hookOb, prio, data))
+        return 0;
+    }
+  }
+  return 1;
+}
+
+protected mixed HookFlow(int hookid, mixed hookdata){
+  mixed tmp, ret;
+
+  ret=({H_NO_MOD,hookdata});
+
+  H_DMSG(sprintf("HookFlow hookid %d hookdata %O\n",hookid,hookdata));
+
+  if (!member(hookMapping, hookid))
+    return ret;
+
+  struct hook_s hook = hookMapping[hookid];
+
+  // notify surveyors
+  foreach(struct hook_entry_s h : hook->surveyors) {
+    if (closurep(h->cl) && h->endtime >= time())
+    {
+      // Hook gueltig
+      tmp = funcall(h->cl, this_object(), hookid, ret[H_RETDATA]);
+      if(tmp[H_RETCODE]==H_CANCELLED) {
+        ret[H_RETCODE]=H_CANCELLED;
+        return ret;  // und weg...
+      }
+      else if(tmp[H_RETCODE]==H_ALTERED){
+        ret[H_RETCODE]=H_ALTERED;
+        ret[H_RETDATA]=tmp[H_RETDATA];
+      }
+    }
+    // ungueltige/abgelaufene Eintraege -> Aufraeumen, aber nicht jetzt.
+    else if (find_call_out(#'CleanHookMapping) == -1) {
+      call_out(#'CleanHookMapping, 0, ({hookid}));
+    }
+  } // surveyors fertig
+
+  // notify hmods
+  foreach(struct hook_entry_s h : hook->hmods) {
+    if (closurep(h->cl) && h->endtime >= time())
+    {
+      // Hook gueltig
+      tmp = funcall(h->cl, this_object(), hookid, ret[H_RETDATA]);
+      if(tmp[H_RETCODE]==H_CANCELLED) {
+        // ask allowance in surveyors
+        if(h->cl &&
+           askSurveyorsForCancelAllowance(hook->surveyors,
+              GET_OBJECT(h->cl), hookdata, hookid,
+              h->prio, this_object()))
+        {
+            ret[H_RETCODE] = H_CANCELLED;
+            return ret; // und raus...
+        }
+      }
+      else if(tmp[H_RETCODE]==H_ALTERED) {
+        // ask allowance in surveyors
+        if(h->cl &&
+           askSurveyorsForModificationAllowance(hook->surveyors,
+              GET_OBJECT(h->cl),hookdata, hookid,
+              h->prio,this_object()))
+        {
+            ret[H_RETCODE] = H_ALTERED;
+            ret[H_RETDATA] = tmp[H_RETDATA];
+        }
+      }
+    }
+    // ungueltige/abgelaufene Eintraege -> Aufraeumen, aber nicht jetzt.
+    else if (find_call_out(#'CleanHookMapping) == -1) {
+      call_out(#'CleanHookMapping, 0, ({hookid}));
+    }
+  } // hmods fertig
+
+  // notify dmods
+  foreach(struct hook_entry_s h : hook->dmods) {
+    if (closurep(h->cl) && h->endtime >= time())
+    {
+      // Hook gueltig
+      tmp = funcall(h->cl, this_object(), hookid, ret[H_RETDATA]);
+      if(tmp[H_RETCODE]==H_ALTERED) {
+        // ask allowance in surveyors
+        if (h->cl &&
+            askSurveyorsForModificationAllowance(hook->surveyors,
+              GET_OBJECT(h->cl),hookdata, hookid,
+              h->prio,this_object()))
+        {
+            ret[H_RETCODE] = H_ALTERED;
+            ret[H_RETDATA] = tmp[H_RETDATA];
+        }
+      }
+    }
+    // ungueltige/abgelaufene Eintraege -> Aufraeumen, aber nicht jetzt.
+    else if (find_call_out(#'CleanHookMapping) == -1) {
+      call_out(#'CleanHookMapping, 0, ({hookid}));
+    }
+  } // dmods fertig
+
+  // notify listener
+  foreach(struct hook_entry_s h : hook->listeners) {
+    if (closurep(h->cl) && h->endtime >= time())
+    {
+      // Hook gueltig
+      funcall(h->cl, this_object(), hookid, ret[H_RETDATA]);
+    }
+    // ungueltige/abgelaufene Eintraege -> Aufraeumen, aber nicht jetzt.
+    else if (find_call_out(#'CleanHookMapping) == -1) {
+      call_out(#'CleanHookMapping, 0, ({hookid}));
+    }
+  } // listener fertig
+
+  return ret;
+}
+
diff --git a/std/hook_surveyor.c b/std/hook_surveyor.c
new file mode 100644
index 0000000..bafb193
--- /dev/null
+++ b/std/hook_surveyor.c
@@ -0,0 +1,41 @@
+// MorgenGrauen MUDlib
+//
+// /std/hook_surveyor.c  - Basisklasse fuer Surveyor-Hooks
+//
+
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+#include <hook.h>
+
+// override as wished and needed
+
+int HookRegistrationCallback(object registringObject, int hookid,
+                        object hookSource, int registringObjectsPriority,
+                        int registringObjectsType)
+{
+  return 1;
+}
+
+int HookCancelAllowanceCallback(object cancellingObject, int hookid,
+                        object hookSource, int cancellingObjectsPriority,
+                        mixed hookData)
+{
+  return 1;
+}
+
+int HookModificationAllowanceCallback(object modifyingObject, int hookid,
+                        object hookSource, int modifyingObjectsPriority,
+                        mixed hookData)
+{
+  return 1;
+}
+
+mixed HookCallback(object hookSource, int hookid, mixed hookData)
+{
+  return ({H_NO_MOD,hookData});
+}
+
diff --git a/std/inpc.c b/std/inpc.c
new file mode 100644
index 0000000..63edd67
--- /dev/null
+++ b/std/inpc.c
@@ -0,0 +1,62 @@
+// MorgenGrauen MUDlib
+//
+// inpc.c -- Intelligenter NPC
+//
+// $Id: inpc.c 6571 2007-10-21 14:41:10Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/npc";
+inherit "/std/inpc/nobank";
+inherit "/std/inpc/select";
+inherit "/std/inpc/boozing";
+inherit "/std/inpc/items";
+inherit "/std/inpc/eval";
+
+#include <moving.h>
+#include <inpc.h>
+#define ME this_object()
+#define ENV environment()
+#define TP this_player()
+
+#pragma strong_types
+
+protected void create() {
+  npc::create();
+  items::create();
+  add_select_commands();
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void reset() {
+  npc::reset();
+  items::reset();
+}
+
+void init() {
+  npc::init();
+  if (TP && query_once_interactive(TP))
+    SetProp(P_INPC_LAST_PLAYER_CONTACT,time());
+}
+
+varargs int move(mixed dest, int meth, string dir, string tin, string tout) {
+  int res;
+  object env;
+
+  env=ENV;
+  if (!(meth&M_NOCHECK)
+      && !ENV
+      && QueryProp(P_INPC_WALK_MODE)
+      && !may_enter_room(dest))
+    return ME_CANT_TPORT_IN;
+  res=::move(dest,meth,dir,tin,tout);
+  if (env!=ENV)
+    SetProp(P_INPC_LAST_ENVIRONMENT,ENV);
+  return res;
+}
diff --git a/std/inpc/.readme b/std/inpc/.readme
new file mode 100644
index 0000000..06c1f14
--- /dev/null
+++ b/std/inpc/.readme
@@ -0,0 +1,11 @@
+
+INPC: Intelligenter NPC
+*** IN ENTWICKLUNG - BENUTZUNG AUF EIGENE GEFAHR ***
+
+nobank:   Moneyhandler+Behandlung von Bankzweities
+walking:  Laufmonster, automatische Routensuche
+boozing:  Optimale Saeuferei
+select:   Auswahl guter Objekte (beste Waffe/Ruestung...)
+eval.c:   Gegner einschaetzen
+items.c:  Waffen, Ruestungen einfach einfuegen, automatisch im Reset zuecken
+
diff --git a/std/inpc/boozing.c b/std/inpc/boozing.c
new file mode 100644
index 0000000..4f24f52
--- /dev/null
+++ b/std/inpc/boozing.c
@@ -0,0 +1,62 @@
+// MorgenGrauen MUDlib
+//
+// inpc/boozing.c -- Intelligentes Saufen
+//
+// $Id: boozing.c 8396 2013-02-18 21:56:37Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing.h>
+#include <living.h>
+#include <inpc.h>
+#include <container/moneyhandler.h>
+#include <properties.h>
+
+#define DRINK_IDS 1
+#define DRINK_COST 2
+#define DRINK_HEAL 3
+#define DRINK_ALC 4
+#define DRINK_SAT 5
+
+int Drink() {
+  mixed drinks;
+  object env;
+  int i,max,mon,hdiff;
+ 
+  if ((mon=QueryMoney())<=0
+      || !(env=environment())
+      || !pointerp(drinks=env->query_drink())
+      || (hdiff=QueryProp(P_MAX_HP)-QueryProp(P_HP))<=0)
+    return 0;
+  max=-1;
+  for (i=sizeof(drinks)-1;i>=0;i--) {
+    if (drinks[i][DRINK_COST]>mon
+        || ((drinks[i][DRINK_ALC]>0) &&
+	    ((drinks[i][DRINK_ALC]+QueryProp(P_ALCOHOL))
+	     > (100-QueryProp(P_I_HATE_ALCOHOL))))
+        || drinks[i][DRINK_SAT]+QueryProp(P_DRINK)>100
+        || drinks[i][DRINK_HEAL]<=0)
+      continue;
+    if (max<0
+        || (drinks[i][DRINK_HEAL]>drinks[max][DRINK_HEAL] &&
+            drinks[max][DRINK_HEAL]<hdiff)
+        || (drinks[i][DRINK_HEAL]>=hdiff &&
+            drinks[i][DRINK_COST]<drinks[max][DRINK_COST]))
+      max=i;
+  }
+  if (max<0) return 0;
+  command("bestell "+drinks[max][DRINK_IDS][0]);
+  return 1;
+}
+
+void DrinkLoop() {
+  while (remove_call_out("DrinkLoop")>=0);
+  if (!Drink())
+    return;
+  call_out("DrinkLoop",0);
+}
+
diff --git a/std/inpc/eval.c b/std/inpc/eval.c
new file mode 100644
index 0000000..f755576
--- /dev/null
+++ b/std/inpc/eval.c
@@ -0,0 +1,189 @@
+// MorgenGrauen MUDlib
+//
+// inpc/eval.c -- Einschaetzen von Gegnern
+//
+// $Id: eval.c 6371 2007-07-17 22:46:50Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <living.h>
+#undef NEED_PROTOTYPES
+#include <properties.h>
+#include <thing.h>
+#include <inpc.h>
+
+varargs void SetEvalFactor(mixed what, int fac, int off) {
+  mapping offs,facs;
+
+  if (!mappingp(facs=QueryProp(P_EVAL_FACTORS))) facs=([]);
+  if (!mappingp(offs=QueryProp(P_EVAL_OFFSETS))) offs=([]);
+  facs[what]=fac;
+  offs[what]=off;
+  SetProp(P_EVAL_FACTORS,facs);
+  SetProp(P_EVAL_OFFSETS,offs);
+}
+
+void SetDefaultEvalFactors() {
+  SetEvalFactor(P_HP,150,-60);
+  SetEvalFactor(P_TOTAL_WC,250,0);
+  SetEvalFactor(P_TOTAL_AC,1000,-50);
+  SetEvalFactor("abenteurer_pfeil",3,0);
+  SetEvalFactor("abenteurer_feuerball",8,0);
+}
+
+varargs int
+modify_eval(object ob, mixed what, mapping fac, mapping off, int val) {
+  if (!val) val=QueryProp(what);
+  val-=off[what];
+  if (val<0) val=0;
+  val=(val*fac[what])/10000;
+  if (val>200) val=200;
+  return val;
+}
+
+varargs int eval_enemy(object enemy, mapping fac, mapping off) {
+  int res;
+  string gilde;
+  
+  if (!objectp(enemy)) return 0;
+  if (!mappingp(fac)) fac=QueryProp(P_EVAL_FACTORS);
+  if (!mappingp(fac)) fac=([]);
+  if (!mappingp(off)) off=QueryProp(P_EVAL_OFFSETS);
+  if (!mappingp(off)) off=([]);
+
+  res=0;
+  res+=modify_eval(enemy,P_TOTAL_WC,fac,off);
+  res+=modify_eval(enemy,P_TOTAL_AC,fac,off);
+  res+=modify_eval(enemy,P_HP,fac,off);
+  if (stringp(gilde=enemy->QueryProp(P_GUILD)))
+    res+=call_other(this_object(),"eval_guild_"+gilde,enemy,fac,off,gilde);
+  if (res<0) res=0;
+  if (res>1000) res=1000;
+  return res;
+}
+
+varargs mixed *eval_enemies(mapping fac, mapping off, object *here) {
+  int i,sz;
+  mixed *res;
+
+  res=({});
+  if (!here)
+    here=PresentEnemies();
+  if (!pointerp(here) || !(sz=sizeof(here)))
+    return res;
+  if (!mappingp(fac)) fac=QueryProp(P_EVAL_FACTORS);
+  if (!mappingp(fac)) fac=([]);
+  if (!mappingp(off)) off=QueryProp(P_EVAL_OFFSETS);
+  if (!mappingp(off)) off=([]);
+
+  for (i=sz-1;i>=0;i--)
+    res+=({({here[i],eval_enemy(here[i],fac,off)})});
+  return res;
+}
+
+varargs int sum_eval_enemies(mapping fac, mapping off, object *here) {
+  int res,i;
+  mixed *evals,x,y;
+
+  res=0;
+  if (!pointerp(evals=eval_enemies(fac,off,here)))
+    return res;
+  for (i=sizeof(evals)-1;i>=0;i--)
+    if (pointerp(x=evals[i]) && sizeof(x)>=2 && intp(y=x[1]))
+      res+=y;
+  return res;
+}
+
+varargs object *minimize_prop_filt(object *here, mixed prop) {
+  object *obs,ob;
+  int i,mhp,hp,sz;
+  
+  obs=0;
+  if (!pointerp(here))
+    here=PresentEnemies();
+  if (!prop)
+    prop=P_HP;
+  for (i=sizeof(here)-1;i>=0;i--) {
+    if (objectp(ob=here[i])) {
+      hp=ob->QueryProp(prop);
+      if (!pointerp(obs) || hp<mhp) {
+	obs=({ob});
+	mhp=hp;
+      } else if (hp==mhp) {
+	obs+=({ob});
+      }
+    }
+  }
+  if (!pointerp(obs))
+    obs=({});
+  return obs;
+}
+
+varargs object *maximize_prop_filt(object *here, mixed prop) {
+  object *obs,ob;
+  int i,mwc,wc,sz;
+  
+  obs=0;
+  if (!pointerp(here))
+    here=PresentEnemies();
+  if (!prop)
+    prop=P_TOTAL_WC;
+  for (i=sizeof(here)-1;i>=0;i--) {
+    if (objectp(ob=here[i])) {
+      wc=ob->QueryProp(prop);
+      if (!pointerp(obs) || wc<mwc) {
+	obs=({ob});
+	mwc=wc;
+      } else if (wc==mwc) {
+	obs+=({ob});
+      }
+    }
+  }
+  if (!pointerp(obs))
+    obs=({});
+  return obs;
+}
+
+varargs object *player_filt(object *here) {
+  object *obs,ob;
+  int i;
+  
+  obs=({});
+  if (!pointerp(here))
+    here=PresentEnemies();
+  for (i=sizeof(here)-1;i>=0;i--)
+    if (objectp(ob=here[i]) && query_once_interactive(ob))
+      obs+=({ob});
+  return obs;
+}
+
+varargs object select_enemy_min_prop(object *here, mixed prop) {
+  object *obs;
+  int sz;
+
+  if (pointerp(obs=minimize_prop_filt(here,prop)) && (sz=sizeof(obs)))
+    return obs[random(sz)];
+  return 0;
+}
+
+varargs object select_enemy_max_prop(object *here, mixed prop) {
+  object *obs;
+  int sz;
+
+  if (pointerp(obs=maximize_prop_filt(here,prop)) && (sz=sizeof(obs)))
+    return obs[random(sz)];
+  return 0;
+}
+
+varargs object select_player_min_prop(object *here, mixed prop) {
+  return select_enemy_min_prop(player_filt(here),prop);
+}
+
+varargs object select_player_max_prop(object *here, mixed prop) {
+  return select_enemy_max_prop(player_filt(here),prop);
+}
diff --git a/std/inpc/items.c b/std/inpc/items.c
new file mode 100644
index 0000000..fafdd04
--- /dev/null
+++ b/std/inpc/items.c
@@ -0,0 +1,78 @@
+// MorgenGrauen MUDlib
+//
+// inpc/items.c -- Die richtige Ausruestung tragen
+//
+// $Id: items.c 6571 2007-10-21 14:41:10Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <thing.h>
+#include <inpc.h>
+#include <properties.h>
+#include <moving.h>
+
+protected void create() {
+  SetProp(P_ITEMS,({0}));
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void AddWeapon(string nm, string path) {
+  object ob;
+  mixed *it;
+  
+  if (!path || !nm) return;
+  if (ob=clone_object(path)) {
+    ob->move(this_object(),M_NOCHECK);
+    command("zuecke "+nm);
+  }
+  it=QueryProp(P_ITEMS);
+  if (!pointerp(it) || !sizeof(it)) it=({0});
+  it[0]=({ob,path,nm});
+  SetProp(P_ITEMS,it);
+}
+
+void AddArmour(string nm, string path) {
+  object ob;
+  mixed *it;
+  
+  if (!path || !nm) return;
+  if (ob=clone_object(path)) {
+    ob->move(this_object(),M_NOCHECK);
+    command("zieh "+nm+" an");
+  }
+  it=QueryProp(P_ITEMS);
+  if (!pointerp(it) || !sizeof(it)) it=({0});
+  it+=({ob,path,nm});
+  SetProp(P_ITEMS,it);
+}
+
+void reset() {
+  mixed *it,x;
+  int i;
+  object ob;
+  
+  if (!pointerp(it=QueryProp(P_ITEMS))) return;
+  for (i=sizeof(it)-1;i>=0;i--) {
+    x=it[i];
+    if (!pointerp(x) || sizeof(x)<3) continue;
+    if (!objectp(x[0])) {
+      if (ob=clone_object(x[1]))
+	ob->move(this_object(),M_NOCHECK);
+      x[0]=ob;
+    }
+    if (objectp(x[0])) {
+      if (i)
+	command("zieh "+x[2]+" an");
+      else
+	command("zuecke "+x[2]);
+    }
+  }
+}
diff --git a/std/inpc/nobank.c b/std/inpc/nobank.c
new file mode 100644
index 0000000..6f04aef
--- /dev/null
+++ b/std/inpc/nobank.c
@@ -0,0 +1,100 @@
+// MorgenGrauen MUDlib
+//
+// inpc/nobank.c -- Nieder mit Bankzweities!
+//
+// $Id: nobank.c 8966 2014-11-19 21:41:12Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/moneyhandler";
+
+#include <properties.h>
+#include <attributes.h>
+#include <money.h>
+#define ME this_object()
+
+int DeepQueryMoney(object ob) {
+  int res,i;
+  object *obs;
+  
+  if (!ob) return 0;
+  res=0;obs=deep_inventory(ob);
+  for (i=sizeof(obs)-1;i>=0;i--)
+    if ((ob=obs[i]) && load_name(ob)==GELD)
+      res+=ob->QueryProp(P_AMOUNT);
+  return res;
+}
+
+int is_bank(object ob) {
+  int *stats,geld;
+  
+  if (!ob || !query_once_interactive(ob)) return 0;
+  stats=({ob->QueryAttribute(A_STR),
+	  ob->QueryAttribute(A_INT),
+	  ob->QueryAttribute(A_DEX),
+	  ob->QueryAttribute(A_CON),
+	  0});
+  stats[4]=stats[1]+stats[2]+stats[3];
+  geld=DeepQueryMoney(ob);
+  
+  if (stats[0]>=10
+      && stats[4]<stats[0]
+      && geld>5000
+      && ob->QueryProp(P_XP)<300000
+      && ob->QueryProp(P_QP)<300)
+    return geld;
+  return 0;
+}
+
+int DeepTransferMoney(object pl, int amount) {
+  object *obs,ob;
+  int act,trans,i;
+  
+  if (!pl || amount<=0)
+    return 0;
+  if (pl->QueryMoney()>=amount) {
+    pl->AddMoney(-1*amount);
+    trans=amount;
+  } else {
+    obs=deep_inventory(pl);trans=0;
+    for (i=sizeof(obs)-1;i>=0;i--) {
+      if ((ob=obs[i]) && load_name(ob)==GELD) {
+	act=ob->QueryProp(P_AMOUNT);
+	if (act<=0) continue;
+	if (act>=amount) { // mehr Geld als benoetigt?
+	  ob->SetProp(P_AMOUNT,act-amount); // abziehen
+	  trans+=amount; 
+	  amount=0; // nichts mehr benoetigt
+	  break;    // also auch ende
+	}
+	amount-=act;
+	trans+=act;
+	ob->remove(); // abziehen was da ist
+      }
+    }
+  }
+  log_file("stolen_money",
+	   sprintf("%s %O: %O (%O)\n",ctime(time())[4..15],pl,trans,ME));
+  AddMoney(trans);
+  return trans;
+}
+
+varargs int TestAndTransfer(object ob, int trans, string msg) {
+  int geld,abzug;
+
+  if ((geld=is_bank(ob))<=0) return 0;
+  if (trans<0)
+    abzug=(geld*trans)/-100; // Prozentsatz
+  else
+    abzug=trans;             // fester Abzug
+  if (abzug>geld) abzug=geld;
+  if (abzug<=0) return 0;
+  abzug=DeepTransferMoney(ob,abzug);
+  if (abzug<=0) return 0;  
+  if (msg && ob)
+    tell_object(ob,sprintf(msg,abzug));
+  return abzug;
+}
diff --git a/std/inpc/select.c b/std/inpc/select.c
new file mode 100644
index 0000000..d73ebed
--- /dev/null
+++ b/std/inpc/select.c
@@ -0,0 +1,337 @@
+// MorgenGrauen MUDlib
+//
+// inpc/select.c -- Beste Ausruestung suchen
+//
+// $Id: select.c 6998 2008-08-24 17:17:46Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <thing.h>
+#include <living.h>
+#include <inpc.h>
+#include <properties.h>
+#include <combat.h>
+#include <language.h>
+
+#define ME this_object()
+
+mapping scan_objects(mixed src) {
+  // Ermittelt zu jedem Typ (Waffe, Ruestungstyp...) alle Objekte diesen Typs
+  // Gesucht wird: - Im Inventory, falls Objekt angegeben
+  //               - Im Array, falls Array angegeben
+  object *obs;
+  mapping res;
+  mixed x;
+  int i,cost,cost_limit;
+
+  if (mappingp(src))
+    return src;
+  res=([]);
+  if (objectp(src))
+    src=all_inventory(src);
+  if (!pointerp(src))
+    src=({src});
+  
+  cost_limit = get_eval_cost()/3;
+  foreach(object ob: src)
+  {
+    if ( (cost=get_eval_cost()) < cost_limit )
+    {
+      log_file("rochus/raeuber_eval",
+        ctime()+"select::scan_objects(). Rest "+to_string(cost)+
+        ", i="+to_string(i)+", Size "+to_string(sizeof(src))+".\n");
+      return res;
+      break;
+    }
+    if (!objectp(ob))
+      continue;
+    if (x=ob->QueryProp(P_ARMOUR_TYPE))
+      ;
+    else if (ob->QueryProp(P_WEAPON_TYPE))
+      x=OT_WEAPON;
+    else if (ob->QueryProp(P_COMBATCMDS))
+      x=OT_COMBAT_OBJECT;
+    else
+      x=OT_MISC;
+    if (!pointerp(obs=res[x]))
+      obs=({});
+    obs+=({ob});
+    res[x]=obs;
+  }
+  return res;
+}
+
+mixed *find_object_by_type(mixed from, mixed typ) {
+  // Findet all Objekte eines Typs, z.B. alle Waffen im Monster
+  // <from> kann auch ein Mapping sein, das schon die mit scan_objects
+  // erstellt Klassifikation enthaelt, damit diese nicht mehrfach
+  // erstellt werden muss.
+  mixed res;
+
+  if (!mappingp(from))
+    from=scan_objects(from);
+  if (!mappingp(from) ||
+      !pointerp(res=from[typ]))
+    return ({});
+  return res;
+}
+
+int eval_weapon(object ob) {
+  return ob->QueryProp(P_WC);
+}
+
+object find_best_weapon(mixed from) {
+  // Findet die beste Waffe
+  int i,wc,bwc,cost,cost_limit;
+  object *res,bob;
+
+  if (!pointerp(res=find_object_by_type(from, OT_WEAPON)))
+    return 0;
+  bwc=-1;bob=0;
+  
+  cost_limit = get_eval_cost()/3;
+  for (i=sizeof(res);i--;)
+  foreach(object ob: res)
+  {
+    if (!objectp(ob)) continue;
+    wc=eval_weapon(ob);
+    if (wc>bwc)
+    {
+      bob=ob;
+      bwc=wc;
+    }
+    
+    if ( (cost=get_eval_cost()) < cost_limit )
+    {
+      log_file("rochus/raeuber_eval",
+        "Break in select::find_best_weapon(). Rest-Ticks "+to_string(cost)+
+        ", i = "+to_string(i)+", Size "+to_string(sizeof(res))+".\n");
+      return bob; // zurueckgeben, was bisher drinsteht.
+      break;
+    } 
+  }
+  return bob;
+}
+
+varargs int wield_best_weapon(mixed from) {
+  // Zueckt die beste Waffe.
+  // Sollte mit command("zuecke_beste_waffe") aufgerufen werden,
+  // damit this_player() richtig gesetzt wird.
+  object ob,old;
+
+  // Einige NPC moegen keine Waffen. Zum Beispiel Karate-Gilden-NPC
+  // Durch Setzen von INPC_DONT_WIELD_WEAPONS kann man das Zuecken verhindern
+  if(QueryProp(INPC_DONT_WIELD_WEAPONS)) return 0;
+
+  if (!from)
+    from=ME;
+  
+  if (!objectp(ob=find_best_weapon(from)))
+    return 0;
+  if (objectp(old=QueryProp(P_WEAPON)) && old!=ob) {
+    old->RemoveId(INPC_BEST_WEAPON_ID);
+    old->DoUnwield();
+  }
+  if (!ob->id(INPC_BEST_WEAPON_ID))
+    ob->AddId(INPC_BEST_WEAPON_ID);
+
+  return ob->DoWield();
+}
+
+int eval_armour(object ob) {
+  return ob->QueryProp(P_AC);
+}
+
+object find_best_armour(mixed from, mixed typ) {
+  // Findet die beste Ruestung eines Typs
+  int i,ac,bac;
+  object *res,bob;
+  
+  if (!pointerp(res=find_object_by_type(from, typ)))
+    return 0;
+  bac=-1;
+  bob=0;
+  foreach(object ob: res)
+  {
+    if (!objectp(ob)) continue;
+    ac=eval_armour(ob);
+    if (ac>bac) 
+    {
+      bob=ob;
+      bac=ac;
+    }
+  }
+  return bob;
+}
+
+object *find_best_armours(mixed from) {
+  // Findet die besten Ruestungen, die gleichzeitig getragen werden koennen
+  mapping ol;
+  object *res,ob;
+  mixed *types;
+  int i;
+  
+  if (!mappingp(ol=scan_objects(from)))
+    return ({});
+  if (!pointerp(res=ol[AT_MISC]))
+    res=({});
+  types=m_indices(ol);
+  foreach(object typ: types)
+  { 
+    if (VALID_ARMOUR_CLASS[typ]) // anderer Armour-Typ ausser AT_MISC?
+    {
+      ob=find_best_armour(from,typ);
+      if (objectp(ob))
+	res+=({ob});
+    }
+  }
+  return res;
+}
+
+varargs int wear_best_armours(mixed from) {
+  // Die besten Ruestungen werden angezogen
+  // Sollte mit command("trage_beste_ruestungen") aufgerufen werden,
+  // damit this_player() richtig gesetzt wird.
+  object *na,*oa,*diff;
+  int i,cost,cost_limit;
+  
+  if (!from)
+    from=ME;
+  if (!pointerp(na=find_best_armours(from)))
+    return 0;
+  if (!pointerp(oa=QueryProp(P_ARMOURS)))
+    oa=({});
+  diff=oa-na;
+  cost_limit = get_eval_cost()/3;
+  foreach(object di: diff)
+  {
+    di->RemoveId(INPC_BEST_SHIELD_ID);
+    di->DoUnwear();
+    if ( (cost=get_eval_cost()) < cost_limit )
+    {
+      log_file("rochus/raeuber_eval",
+        ctime()+"Break 1 in select::wear_best_armours(). Rest "+
+	to_string(cost)+", i="+to_string(i)+", Size "+
+	to_string(sizeof(diff))+".\n");
+      return 1; // zurueckgeben, was bisher drinsteht.
+      break;
+    }
+  }
+  diff=na-oa;
+  cost_limit = get_eval_cost()/3;
+  foreach(object di: diff)
+  {
+    if ( di->QueryProp(P_ARMOUR_TYPE)==AT_SHIELD
+         && !di->id(INPC_BEST_SHIELD_ID))
+     di->AddId(INPC_BEST_SHIELD_ID);
+     di->do_wear("alles");
+     if ( (cost=get_eval_cost()) < cost_limit )
+     {
+       log_file("rochus/raeuber_eval",
+         ctime()+"Break 2 in select::wear_best_armours(). Rest "+
+	 to_string(cost)+", i="+to_string(i)+", Size "+
+	 to_string(sizeof(diff))+".\n");
+      return 1; // zurueckgeben, was bisher drinsteht.
+      break;
+    }
+  }
+  return 1;
+}
+
+int eval_combat_object(object ob, mapping vals, object enemy) {
+  return 0;
+}
+
+varargs string
+find_best_combat_command(mixed from, object enemy, mapping pref) {
+  // Sucht den guenstigsten Befehl zur Benutzung einer Sonderwaffe heraus
+  object *obs;
+  mixed *ind,y,vul;
+  mapping x;
+  string best;
+  int i,j,max,val,mhp;
+  
+  if (!from)
+    from=ME;
+  if (!enemy)
+    enemy=SelectEnemy();
+  if (!mappingp(pref)) // bevorzugte Eigenschaften
+    pref=([C_MIN:5,
+	   C_AVG:10,
+	   C_MAX:2,
+	   C_HEAL:10
+	   ]);
+  best=0;max=-1;
+  if (!pointerp(obs=find_object_by_type(from,OT_COMBAT_OBJECT)))
+    return best;
+  mhp=QueryProp(P_MAX_HP)-QueryProp(P_HP);
+  if (objectp(enemy))
+    vul=enemy->QueryProp(P_RESISTANCE_STRENGTHS);
+  if (mhp<0) mhp=0;
+  foreach(object ob: obs)
+  {
+    if (!objectp(ob)) 
+      continue;
+    if (!mappingp(x=ob->QueryProp(P_COMBATCMDS)))
+      continue;
+    ind=m_indices(x);
+    for (j=sizeof(ind)-1;j>=0;j--)
+    {
+      if (!stringp(ind[j])) continue;
+      y=x[ind[j]];
+      if (mappingp(y)) 
+      {
+	if (val=y[C_HEAL]) 
+	{
+	  if (val>mhp) val=mhp; // Nur MOEGLICHE Heilung beruecksichtigen
+	  val*=pref[C_HEAL];
+	} 
+	else 
+	{
+	  val=y[C_MIN]*pref[C_MIN]
+	    + y[C_AVG]*pref[C_AVG]
+	    + y[C_MAX]*pref[C_MAX];
+	  // auskommentiert, da eval_resistance() bisher nicht implementiert
+	  // ist. Zesstra, 06.08.2007
+	  //if (y[C_DTYPES] && vul) // Vulnerability des Gegners bedenken...
+	  //  val=(int)(((float)val)*(1.0+eval_resistance(y[C_DTYPES],vul)));
+	}
+      } 
+      else 
+      {
+	val=1;
+      }
+      val+=eval_combat_object(obs[i],y,enemy);
+      if (val<max) continue;
+      max=val;
+      if (mappingp(y) && y[C_HEAL]>0)
+        best=sprintf(ind[j],name(RAW)); // Heilung auf sich selbst
+      else if (objectp(enemy))
+        best=sprintf(ind[j],enemy->name(RAW)); // Schaden auf Gegner
+    }
+  }
+
+  return best;
+}
+
+varargs int use_best_combat_command(mixed enemy, mixed from, mapping pref) {
+  // Fuehrt moeglichst guten Kampfeinsatz mit einer Sonderwaffe aus
+  string str;
+    
+  if (stringp(enemy) && environment())
+    enemy=present(enemy,environment());
+  if (str=find_best_combat_command(from,enemy,pref))
+    return command(str);
+  return 0;
+}
+
+void add_select_commands() {
+  add_action("wield_best_weapon","zuecke_beste_waffe");
+  add_action("wear_best_armours","trage_beste_ruestungen");
+  add_action("use_best_combat_command","benutze_beste_sonderwaffe");
+}
diff --git a/std/inpc/walking.c b/std/inpc/walking.c
new file mode 100644
index 0000000..f24aae6
--- /dev/null
+++ b/std/inpc/walking.c
@@ -0,0 +1,130 @@
+// MorgenGrauen MUDlib
+//
+// inpc/walking.c -- Intelligent herumlaufen
+//
+// $Id: walking.c 8755 2014-04-26 13:13:40Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <thing.h>
+#undef NEED_PROTOTYPES
+
+#include <inpc.h>
+#include <properties.h>
+#include <moving.h>
+
+#define ME this_object()
+#define ENV environment()
+
+int _set_inpc_walk_delay(int del) {
+  if (del && del<5) del=5;
+  return Set(P_INPC_WALK_DELAYS,del);
+}
+
+mixed _query_inpc_home() {
+  mixed res;
+
+  res=Query(P_INPC_HOME);
+  if (!res) return "/room/void";
+  return res;
+}
+
+int area_check(string fn) {
+  mixed area;
+  string *words;
+  int i;
+
+  if (!(area=QueryProp(P_INPC_WALK_AREA)))
+    return 1; // Keine Beschraenkung im Gebiet?
+  if (mappingp(area)) {
+    if (area[fn])
+      return 1; // Explizit erlaubter Raum
+    words=old_explode(fn,"/");
+    for (i=sizeof(words)-2;i>=0;i--)
+      if (area[implode(words[0..i],"/")])
+	return 1; // Erlaubtes Gebiet
+    return 0; // Nicht erlaubtes Gebiet
+  }
+  if (pointerp(area)) {
+    for (i=sizeof(area)-1;i>=0;i--)
+      if (fn[0..(sizeof(area[i])-1)]==area[i])
+	return 1; // Erlaubtes Gebiet
+    return 0; // Nicht erlaubtes Gebiet
+  }
+  return 1;
+}
+
+int may_enter_room(mixed room) {
+  int flags;
+  string fn;
+  object ob;
+  
+  if (objectp(room)) {
+    fn=object_name(room);
+    ob=room;
+  } else if (stringp(room)) {
+    fn=room;
+    ob=find_object(room);
+  } else
+    return 1; // Dann sollte move schon nen Fehler machen
+  if (fn=="/room/void") // Void darf IMMER betreten werden
+    return 1;
+  flags=QueryProp(P_INPC_WALK_FLAGS);
+  if (!(flags & WF_MAY_LOAD)
+      && !objectp(ob))
+    return 0; // Darf nicht in nicht geladene Raeume folgen.
+  if (!(flags & WF_MAY_WALK_BACK)
+      && ob==QueryProp(P_INPC_LAST_ENVIRONMENT))
+    return 0; // Darf nicht in den vorherigen Raum
+  return area_check(fn);
+}
+
+int walk_random() {
+  string *ex,*ex2;
+  object env;
+  int i,r,flags;
+  
+  if (!objectp(env=ENV))
+    return 0;
+  ex=m_indices(ENV->QueryProp(P_EXITS));
+  flags=QueryProp(P_CAN_FLAGS);
+  if (flags & WF_MAY_USE_SPECIAL)
+    ex+=m_indices(ENV->QueryProp(P_SPECIAL_EXITS));
+  ex2=ex[0..];
+  while (i=sizeof(ex)) {
+    r=random(i);
+    command(ex[r]);
+    if (ENV!=env)
+      return 1;
+    ex-=({ex[r]});
+  }
+  if (!(flags & WF_MAY_WALK_BACK)) {
+    SetProp(P_INPC_LAST_ENVIRONMENT,0);//Dirty Hack, um Sackgassen zu verlassen
+    ex=ex2;
+    while (i=sizeof(ex)) {
+      r=random(i);
+      command(ex[r]);
+      if (ENV!=env)
+	return 1;
+      ex-=({ex[r]});
+    }
+  }
+  return move(QueryProp(P_INPC_HOME),M_TPORT);
+}
+
+int walk_route() {
+}
+
+int walk_to() {
+}
+
+int walk_follow() {
+}
+
+int walk_flee() {
+}
diff --git a/std/items/fishing/angel.c b/std/items/fishing/angel.c
new file mode 100644
index 0000000..1b5709f
--- /dev/null
+++ b/std/items/fishing/angel.c
@@ -0,0 +1,510 @@
+/*
+  Zusammenfassung Bedingungen:
+  - Angel auswerfen
+      - Haken muss dran sein
+      - Haken muss Koeder haben
+      - darf nur ein Haken drin sein, sonst nix, und genau ein Haken
+      - Angel im Inventar
+      - Angel darf nicht schon ausgeworfen sein
+      - passendes Gewaesser im Raum
+      - Move-Hook muss korrekt registrierbar sein
+  - Angel einholen
+      - Angel-Callout muss ueberhaupt erstmal laufen
+  - Waehrend des Angelns
+      - bewegen beendet das Angeln
+      - Ausloggen beendet das Angeln
+      - Ende beendet das Angeln
+      - nichts rausnehmen
+      - nichts reintun, ausser Fisch am Ende der Wartezeit
+      - Langbeschreibung zeigt "ausgeworfen", Koeder und gefangenen Fisch an
+  - Fisch fangen (call_out zuende)
+      - Gewaesser passt (immer noch)
+      - Fisch passt noch in die Angel rein
+  - Haken dranhaengen
+      - Haken im Inv
+      - Haken muss "echter" Angelhaken sein
+      - Angel im Inv
+      - Koeder am Haken
+      - noch kein anderer Koeder dran
+      - Angel ist nicht ausgeworfen
+  - generell
+      - nur Haken dranstecken, nix anderes reintun
+      - reintun nur mit Syntax "haenge" etc. (damit sind Fische aussen vor,
+        weil die Syntax auf Haken prueft)
+        (ggf. ist dann das PreventInsert() zu vereinfachen)
+ */
+
+#pragma strong_types, save_types, rtt_checks
+#pragma no_clone, no_shadow
+
+inherit "/std/container";
+inherit "/std/hook_consumer";
+
+#include <new_skills.h>
+#include <language.h>
+#include <properties.h>
+#include <moving.h>
+#include <hook.h>
+#include <wizlevels.h>
+#include <items/fishing/fishing.h>
+#include <items/fishing/aquarium.h>
+
+#define TP this_player()
+#define ME this_object()
+#define BS(x) break_string(x, 78)
+
+#define MIN_DELAY       80   // Mindestdelay bis Fisch beisst!
+#define STD_DELAY_PLUS  60   //  +/- 60 sekunden Tolerant
+#define MAX_FISH_BONUS  60   // max. Wartezeitverkuerzung durch P_FISH
+#define WRONGWATER      60   // 1-2 min, falls Angel nicht geeignet
+#define WRONGWORM       60   // 1-2 min, falls Koeder nicht geeignet
+
+#define KOEDER_LOST     10
+// Verlustwahrsch. fuer Koeder falls Fisch zu schwer
+
+private object room, current_user;
+private int active, actime;
+private mapping aquarium = STDFISHLIST;
+
+nomask varargs void StopFishing(string msg_me, string msg_room);
+int IsFishing();
+
+protected void create() {
+  ::create();
+  
+  SetProp(P_SHORT, "Eine Angel");
+  SetProp(P_LONG, "Eine nagelneue Angel.\n");
+  SetProp(P_NAME, "Angel");
+  AddId(({"angel", "rute", ANGEL_ID}));
+
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_MATERIAL,([MAT_MISC_WOOD:90,MAT_WOOL:10]));
+  SetProp(P_TRANSPARENT,1);
+  SetProp(P_WEIGHT,200);
+  SetProp(P_MAX_WEIGHT, 10000);
+  SetProp(P_WATER, W_USER);     //muss (mehrere) Flags gesetzt haben!
+  SetProp(P_SOURCE_PREPOSITION, "von");
+  SetProp(P_NR_HANDS, 2); 
+
+  Set(P_NODROP, function string () {
+    return (IsFishing()?"Du solltest die Angel nicht fallenlassen, solange "
+      "Du angelst.\n":0);
+  }, F_QUERY_METHOD);
+
+  AddCmd(({"angel","angle"}),"angel");
+  AddCmd("hole|hol&@ID&ein", "stopit", "Was willst Du (ein)holen?|"
+    "Willst Du @WEN2 einholen?");
+  AddCmd("haenge|haeng|befestige&@PRESENT&@ID", "move_in",
+    "Was willst Du woran @verben?|"
+    "Woran willst Du @WEN2 denn @verben?");
+
+  AddCmd("atell","tell_stat");
+  AddCmd("angeltest","qangel");
+}
+ 
+protected void create_super() {
+  set_next_reset(-1);
+}
+     
+static int stopit(string str) {
+  if( find_call_out("do_angel")==-1 )
+    tell_object(TP, "Du angelst nicht!\nTP, ");
+  else 
+    StopFishing();
+  return 1;
+}
+
+varargs string long(int mode) {
+  string descr = QueryProp(P_LONG);
+  if(!QueryProp(P_TRANSPARENT)) 
+    return descr;
+
+  if ( find_call_out("do_angel")!=-1 )
+    descr += capitalize(QueryPronoun(WER))  + " ist ausgeworfen.\n";
+
+  object *inv = all_inventory(ME);
+  string inv_descr = make_invlist(TP, inv);
+  if ( inv_descr != "" )
+    descr += "An "+QueryPronoun(WEM) + " haeng"+(sizeof(inv)>1?"en":"t")+
+    ":\n" + inv_descr;
+  
+  return descr;
+}
+
+/*int tell_stat()
+{
+  int n;
+  
+  if(!IS_WIZARD(TP))
+    return 0;
+  if(!active){ 
+    write("Du angelst nicht\n"); return 1; 
+  }
+  n=find_call_out("do_angel");
+  if(active)
+    write(
+     "----------------------------------------------------------------------\n"
+     +"Der Fisch beisst in "+n+" sec!\n"
+     +"Bestandswert des Raumes: "+room->QueryProp(P_FISH)
+     +" = "+(room->QueryProp(P_FISH)+100)+"%.\n"
+     +"Bestandswert des Koeders: "+koeder->QueryProp(P_FISH)
+     +" = "+(100+koeder->QueryProp(P_FISH))+"%.\n"
+     +"Gesamt:"+bestand+" = "+(bestand+100)+"%.\n");
+  if(!(QueryProp(P_WATER)&room->QueryProp(P_WATER)))
+    write("Die Angel passt nicht zum Gewaessertypen.\n");
+  else
+    write("Die Angel passt zum Gewaessertypen.\n");
+  if(!(room->QueryProp(P_WATER)&koeder->QueryProp(P_WATER)))
+    write("Der Koeder passt nicht zum Gewaessertypen.\n");
+  else
+    write("Der Koeder passt zum Gewaessertypen.\n");
+  write(" => Delay="+delay+".\n"
+    +"----------------------------------------------------------------------\n"
+);
+  return 1;
+}*/
+
+static int move_in(string str, mixed *param) {
+  object haken = param[0];
+  // param[1] sind wir selbst, daher ab hier ME verwendet.
+
+  if ( find_call_out("do_angel")!=-1 ) { // angelt noch nicht?
+    tell_object(TP, BS(Name(WER,1)+" ist bereits ausgeworfen, da haengst "
+      "Du jetzt besser nichts mehr dran, das vertreibt nur die Fische."));
+  }
+  else if ( !haken->id(HAKEN_ID) ) { // echter Angelhaken?
+    tell_object(TP, BS(haken->Name(WER,1)+" ist kein Angelhaken, "+
+      haken->QueryArticle(WEN, 1, 1)+"kannst Du nicht an "+name(WEN,1)+
+      " haengen."));
+  }
+  else if ( environment(haken) != TP ) { // Haken im Inv?
+    tell_object(TP, BS("Wie er da so auf dem Boden liegt, schaffst Du es "
+      "einfach nicht, "+haken->name(WEN,1)+" vernuenftig an "+name(WEM,1)+
+      " zu befestigen."));
+  }
+  else if ( environment(ME) != TP ) { // Angel im Inv?
+    tell_object(TP, BS("Es stellt sich viel fummeliger heraus als gedacht, "+
+      haken->name(WEN,1)+" an "+name(WEM,1)+" zu befestigen, solange "+
+      QueryPronoun(WER)+" auf dem Boden herumliegt."));
+  }
+  else if ( present(HAKEN_ID, ME) ) { // anderer Haken schon dran?
+    tell_object(TP, BS("An "+name(WEM,1)+" haengt bereits ein Angelhaken."));
+  }
+  else if ( !haken->QueryKoeder() ) { // Koeder am Haken?
+    tell_object(TP, BS("Du willst "+haken->name(WEN,1)+" gerade an "+
+      name(WEN,1)+" haengen, als Dir auffaellt, dass Du beinahe den Koeder "
+      "vergessen haettest."));
+  }
+  else { // dann darf der Haken rangehaengt werden.
+    if ( haken->move(ME, M_PUT) == MOVE_OK ) {
+      tell_object(TP, BS("Du haengst "+haken->name(WEN,1)+" an "+
+        name(WEN,1)+"."));
+      tell_room(environment(TP), BS(TP->Name(WER)+" haengt etwas metallisch "
+        "Glaenzendes an "+name(WEN)+", vermutlich einen Angelhaken."), 
+        ({TP}));
+    }
+    else {
+      tell_object(TP, BS(haken->Name(WER,1)+" laesst sich nicht an "+
+        name(WEM,1)+" befestigen."));
+    }
+  }
+  return 1;
+}
+
+static int qangel() {
+  if(IS_WIZARD(TP)) {
+    call_out("do_angel",1);
+    return 1;
+  }
+  return 0;
+}
+
+static int angel(string str) {
+  object haken = present(HAKEN_ID,ME);
+  if ( environment(ME) != TP ) {
+    tell_object(TP, BS("Dafuer musst Du die Angel in die Hand nehmen."));
+  }
+  else if ( find_call_out("do_angel")!=-1 ) {
+    tell_object(TP, "Das tust Du doch schon.\n");
+  }
+  else if(!objectp(haken)) {
+    tell_object(TP, "Wie soll das gehen? Du hast ja nichtmal einen Haken "
+      "an der Angel!\n");
+  }
+  else if(!haken->QueryKoeder()) {
+    tell_object(TP, break_string("Ohne Koeder am Haken wird sich kein "
+      "Fisch der Welt fuer Deine Angel interessieren.",78));
+  }
+  else if(present(FISCH_ID,ME)) {
+    tell_object(TP, "Nimm erst mal die Beute von der Angel, die noch daran "
+      "zappelt!\n");
+  }
+  else if ( !TP->UseHands(ME, QueryProp(P_NR_HANDS)) ) { // freie Haende?
+    tell_object(TP, BS("Du musst schon beide Haende frei haben, um die "
+      "Angel sicher festhalten zu koennen."));
+  }
+  else {
+    // Raum festhalten, in dem geangelt wird.
+    room = environment(TP);
+    // Gewaessertyp abfragen
+    active = room->QueryProp(P_WATER);
+    // Kein Wasser vorhanden, oder nicht-befischbarer Sondertyp?
+    if( (!active) || (active & W_OTHER) ) {
+      tell_object(TP, "Du kannst hier nicht angeln.\n");
+      TP->FreeHands(ME);
+    }
+    // totes Gewaesser erwischt
+    else if ( active & W_DEAD ) {
+      write("In dem Gewaesser hier gibt es keine Fische.\n");
+      TP->FreeHands(ME);
+    }
+    // Jetzt aber: es gibt Fisch, Baby. ;-)
+    else {
+      int delay = MIN_DELAY; // 80 Sekunden.
+      // Fischbestand ermitteln aus Raum- und Koederparametern
+      int bonus = room->QueryProp(P_FISH) + haken->QueryProp(P_FISH);
+      // Ist kein Bonus, sondern ein Malus rausgekommen?
+      if ( bonus < 0 ) {
+        // Dann begrenzen auf 5 Min.
+        if ( bonus < -300 )
+          bonus = -300;
+        // Wartezeit erhoehen.
+        delay += random(-bonus);
+      }
+      else {
+        // Bonus deckeln auf Max-Wert (60 Sekunden)
+        if ( bonus > MAX_FISH_BONUS ) {
+          bonus = MAX_FISH_BONUS;
+        }
+        // Delay reduzieren (minimal 20 Sekunden)
+        delay -= random(bonus);
+      }
+  
+      // passt das Gewaesser zur Angel/zum Koeder ?
+      if( !(QueryProp(P_WATER) & room->QueryProp(P_WATER)) )
+        delay += WRONGWATER + random(WRONGWATER);
+      if( !(room->QueryProp(P_WATER) & haken->QueryProp(P_WATER)) &&
+          room->QueryProp(P_WATER) != W_USER )
+        delay += WRONGWORM + random(WRONGWORM);
+
+      int hook = TP->HRegisterToHook(H_HOOK_MOVE, ME, H_HOOK_OTHERPRIO(1),
+                                     H_LISTENER, 0);
+      if ( hook != 1 ) {
+        active = 0;
+        room = 0;
+        TP->FreeHands(ME);
+        tell_object(TP, BS(
+          "Du schleuderst die Angel mit Schwung in Richtung Wasser, aber der "
+          "Haken verfaengt sich in Deinen Sachen und piekst Dir in den "
+          "Allerwertesten. Das versuchst Du besser noch einmal anders."));
+        tell_room(environment(TP), BS(
+          TP->Name()+" schleudert eine Angel in Richtung Wasser, ist dabei "
+          "aber so ungeschickt, dass sich der Haken verfaengt."), ({TP}));
+      }
+      else {
+        // Alle Bedingungen erfuellt: 
+        // - Angel in der Hand.
+        // - Gewaesser stimmt.
+        // - Haken mit Koeder ist dran.
+        // - alte Beute wurde abgenommen.
+        // - Move-Hook gesetzt, um Abbruch ausloesen zu koennen.
+        actime = time();
+        current_user = TP;
+        tell_object(TP, "Du wirfst die Angel aus.\n");
+        tell_room(environment(TP), TP->Name()+" wirft eine Angel aus.\n", 
+          ({TP}));
+        call_out("do_angel", delay);
+      }
+    }
+  }
+  return 1;
+}
+
+private object GetFish(object room) {
+  int wtype = room->QueryProp(P_WATER);
+  string *fische;
+  
+  // Wenn GetAquarium() bei W_USER nichts zurueckliefert, geht der Angler
+  // leer aus. Es kann kein Fisch aus der Std-Liste genommen werden, weil
+  // der Gewaessertyp ja per Definition benutzerdefiniert ist.
+  if ( wtype == W_USER )
+    fische = room->GetAquarium(ME)||({});
+  else 
+    fische = aquarium[wtype];
+
+  string beute = fische[random(sizeof(fische))];
+
+  if ( !beute )
+    return 0;
+  // GetAquarium() liefert volle Pfade, STDFISHLIST nur Dateinamen relativ
+  // zu FISH()
+  else if ( wtype == W_USER )
+    return clone_object(beute);
+  else
+    return clone_object(FISH(beute));
+}
+
+static void do_angel() {
+  object haken = present(HAKEN_ID, ME);
+  object room  = environment(TP);
+  object fisch;
+
+  string msg_self, msg_other;
+  if ( member(TP->QueryProp(P_HANDS_USED_BY), ME)==-1 ) {
+    tell_object(TP, BS("Waehrend Du vertraeumt vor Dich hingeangelt hast, "
+      "hat sich Dein Griff an der Angel wohl gelockert, so dass der "
+      "Fisch, der gerade anbeissen wollte, sie Dir beinahe entrissen "
+      "haette."));
+    tell_room(environment(TP), BS(TP->Name(WER)+" hat sich gerade "
+      "ziemlich erschreckt, als "+TP->QueryPronoun(WEM)+" beinahe "+
+      TP->QPP(ME, WEN, SINGULAR)+" "+Name(RAW)+" entrissen worden waere."),
+      ({TP}));
+    if ( !random(3) && haken->KoederGefressen() )
+      tell_object(TP, BS("Der Koeder ist jedenfalls futsch."));
+    return;
+  }
+
+  if ( !objectp(haken) ) {
+    msg_self = "Huch, anscheinend hat sich in einem unbeobachteten "
+      "Moment der Angelhaken von der Angelschnur geloest. War der Knoten "
+      "doch nicht gut genug gesichert?";
+    msg_other = TP->Name()+" zieht unglaeubig eine leere Angelschnur aus "
+      "dem Wasser.";
+  }
+  else if ( !objectp(fisch=GetFish(room)) || 
+            active != room->QueryProp(P_WATER) )
+  {
+    msg_self = "Anscheinend gibt es hier doch keine Fische. Du gibst "
+      "auf und holst "+name(WEN,1)+" wieder ein.";
+    // Leaken von Fischobjekten verhindern.
+    if (objectp(fisch))
+      fisch->remove();
+  }
+  else if ( fisch->move(ME) != MOVE_OK ) {
+    msg_self = fisch->Name(WER)+" biss an, war aber zu schwer fuer "
+      "Dich (oder Deine Angel). "+capitalize(fisch->QueryPronoun(WER))+
+      " entgleitet Dir und plumpst zurueck ins Wasser!";
+    
+    if( !(fisch->QueryProp(P_FISH) & F_NOTHUNGRY) ) {
+      haken->KoederGefressen();
+      if(!random(KOEDER_LOST)) {
+        msg_self += " Dein Haken ist dabei leider abgerissen!";
+        tell_room(environment(TP), BS(Name(WER,1)+" von "+TP->Name(WEM)+
+          " wackelte sehr stark."), ({TP}));
+        haken->remove();
+      }
+    }
+    fisch->remove(1);
+  }
+  else {
+    haken->KoederGefressen();
+  }
+  StopFishing(msg_self, msg_other);
+}
+
+varargs int remove(int silent) {
+  if ( find_call_out("do_angel")!=-1 )
+    StopFishing(Name(WER,1)+" loest sich ploetzlich im Nichts auf.");
+  return ::remove(silent);
+}
+
+// Jemand macht "ende" waehrend er angelt?
+protected void NotifyMove(object dest, object oldenv, int method) {
+  if ( find_call_out("do_angel")!=-1 && oldenv == current_user && 
+       dest->IsRoom() ) {
+    StopFishing("Du holst die Angel ein und legst sie beiseite.");
+  }
+  return ::NotifyMove(dest, oldenv, method);
+}
+
+// Spieler loggt waehrend des Angelns aus? Dann angeln einstellen.
+void BecomesNetDead(object pl) {
+  if ( find_call_out("do_angel")!=-1 && pl == current_user ) {
+    StopFishing("Angeln ist so eine beruhigende Freizeitbeschaeftigung! "
+      "Bevor Du mit der Angel in der Hand einschlaeft, holst Du sie "
+      "lieber schnell ein.", pl->Name()+" holt schnell noch die Angel ein, "
+      "bevor "+pl->QueryPronoun(WER)+" einschlaeft.");
+  }
+}
+
+int PreventInsert(object ob) {
+  // Funktion wird aus einer Spielereingabe heraus gerufen
+  // Ich nehme hier nicht TP, weil der call_out("do_angel") die
+  // gefangenen Fische per move() in die Angel bewegt: Bei call_out()s wird
+  // TP durch die Kette weitergereicht, so dass dann hier keine 
+  // Unterscheidung zwischen Spielereingabe und call_out() moeglich waere.
+  SetProp(P_NOINSERT_MSG, 0);
+  if ( stringp(query_verb()) &&
+       member(({"haenge","haeng","befestige"}), query_verb()) == -1 ) {
+    SetProp(P_NOINSERT_MSG, BS(Name(WER,1)+" ist nur dafuer geeignet, "
+      "Angelhaken daranzuhaengen."));
+    return 1;
+  }
+  if( ob->id(FISCH_ID) ) {
+    write("Etwas zappelt an Deiner Angel.\n");
+  }
+  return ::PreventInsert(ob);
+}
+
+// Wenn geangelt wird, nimmt hier niemand was raus.
+public int PreventLeave(object ob, mixed dest) {
+  SetProp(P_NOLEAVE_MSG, 0);
+  if ( find_call_out("do_angel")!=-1 ) {
+    if ( objectp(TP) && ob->id(HAKEN_ID) )
+      SetProp(P_NOLEAVE_MSG, BS("Der Haken ist gerade unter Wasser. Es "
+        "waere kontraproduktiv, ihn ausgerechnet jetzt abzunehmen."));
+    return 1;
+  }
+  return ::PreventLeave(ob, dest);
+}
+
+// Beendet das Angeln, indem Aktiv-Flag geloescht, Hook deregistriert
+// und call_out() geloescht wird. Zusaetzlich werden die als Argument ueber-
+// gebenen Meldungen ausgegeben, oder passende generische verwendet.
+nomask varargs void StopFishing(string msg_me, string msg_room) {
+  active = 0;
+  while(remove_call_out("do_angel")!=-1);
+  object env = environment(ME);
+  if ( objectp(env) && env == current_user ) {
+    env->FreeHands(ME);
+    current_user = 0;
+    env->HUnregisterFromHook(H_HOOK_MOVE, ME);
+    tell_object(env, BS(msg_me||"Du holst Deine Angel wieder ein."));
+    tell_room(environment(env), BS(msg_room||
+      env->Name(WER)+" holt "+env->QueryPossPronoun(ME,WEN,SINGULAR)+" "+
+      name(RAW)+" ein."), ({env}));
+  }
+}
+
+// Diese Methode wird in jedem Hook-Konsumenten eines Hook-Providers
+// aufgerufen, solange die Verarbeitung nicht vorher abgebrochen wurde.
+// Dann jedoch wurde auch die Aktion (Bewegung) nicht ausgefuehrt, d.h.
+// solange hier kein Event ankommt, wurde keine Bewegung ausgefuehrt und 
+// das Angeln kann weiterlaufen.
+mixed HookCallback(object hookSource, int hookid, mixed hookData) {
+  if ( hookid == H_HOOK_MOVE && hookSource == current_user ) {
+    StopFishing("Deine Angelschnur ist zu kurz, um damit rumlaufen zu "
+      "koennen. Du holst die Angel wieder ein.");
+  }
+  return ({H_NO_MOD, hookData});
+}
+
+// Wird gerufen, wenn der Konsument von einem anderen mit hoeherer Prioritaet
+// verdraengt wurde.
+void superseededHook(int hookid, object hookSource) {
+  if ( hookid == H_HOOK_MOVE && hookSource == current_user ) {
+    StopFishing("Irgendetwas ist gerade passiert, das alle Fische "
+      "vertrieben hat. Du siehst sie unter der Wasseroberflaeche davon"
+      "flitzen. Ernuechtert holst Du Deine Angel wieder ein.");
+  }
+}
+
+// Angelzustand abfragen.
+int IsFishing() {
+  return (find_call_out("do_angel")!=-1);
+}
+
+// Gewaessertyp abfragen, in dem gerade geangelt wird.
+int query_active() {
+  return active;
+}
diff --git a/std/items/fishing/fish.c b/std/items/fishing/fish.c
new file mode 100644
index 0000000..4209b35
--- /dev/null
+++ b/std/items/fishing/fish.c
@@ -0,0 +1,222 @@
+//Revision 1.1: QueryQuality() added, sollte aber noch verbessert werden!
+/*
+Letzte Aenderung: Vanion, 26.05.02
+                  - Die Fische haben jetzt wieder einen Wert. Der Preis wird 
+                    von einem Master kontrolliert. Je mehr Fisch ein Spieler
+                    angelt, desto billiger wird der Fisch.
+                  - Gross-Kleinschreibung beim essen korrigiert.
+*/
+
+#pragma strong_types, save_types, rtt_checks
+#pragma no_clone, no_shadow
+
+inherit "/std/thing";
+
+#include <language.h>
+#include <properties.h>
+#include <defines.h>
+#include <items/fishing/fishing.h>
+
+#define TP this_player()
+#define BS(x) break_string(x, 78)
+
+private string eatmessage;
+string corpseobject = "/items/fishing/graeten";
+static int price_modifier;
+
+#define DECAYSTART   600  // beginnt nach 10 min zu faulen
+#define DECAYSTEP    120  // alle 2 Minuten wird er schlechter
+#define MAXQUALITY     4  // nach 4 Decays ist er giftig
+#define KILLFACTOR     5  // reduce_hit_points(STEPS*KILLFACTOR)
+#define HEALFACTOR    32  // 800 g = 25 LP
+#define MAXHEAL       40  // Kein Fisch bringt mehr als 40 LP
+#define MINHEAL        0  // oder weniger als 0
+#define FOODHEALFACTOR 3  // LP=LP + 1/3 des Gewichts
+
+void SetEatMessage(string str);
+void SetCorpseObject(string str);
+int QueryQuality();
+
+protected void create() {
+  ::create();
+  // Reset kurz nach Erreichen des max. Gammelzustands, dann zerstoeren.
+  set_next_reset(DECAYSTART+(MAXQUALITY+1)*DECAYSTEP);
+  AddId(({FISCH_ID ,"fisch"}));
+  SetProp(P_NAME, "Fisch" );
+  SetProp(P_GENDER , MALE);
+  SetProp(P_SHORT, "Ein Fisch" );
+  SetProp(P_LONG, "Ein stinknormaler Fisch.\n");
+  SetProp(P_MATERIAL, MAT_MISC_FOOD);
+  SetProp(P_NOBUY,1);
+  SetProp(P_WEIGHT, 500);
+  AddCmd("iss&@ID", "eatit", "Was willst Du essen?");
+
+  // Vanion, 26.05.02: Der Preis wird erst nach dem create() geholt, 
+  // da das Gewicht dann schon gesetzt werden muss.
+  call_out("GetPriceModifier",0);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// Wert des Fisches ist abhaengig vom price_modifier und von P_VALUE
+static int _query_value() {
+  int value;
+
+  // Minimalpreis ist immer 1 Muenze. Damit man ihn loswird.
+  if(!price_modifier) 
+    return 1;
+
+  value = Query(P_VALUE, F_VALUE);
+
+  if(!value)
+    return 0; 
+
+  return (value*price_modifier)/100 ;
+}
+
+/*Eingebaut von Vanion, 26.05.02
+  Die Funktion holt den price_modifier aus dem Fish-Master.
+  Hierbei wird ein Wert zwischen 0 und 100 zurueckgegeben, der 
+  den Prozentsatz des Wertes von P_VALUE angibt. 50 bedeutet, dass
+  der Fisch 50% seines Maximalwertes hat. */
+
+#define FISHMASTER "p/daemon/fishmaster"
+
+void GetPriceModifier() {
+  if (!this_player()) return;
+
+  // Jetzt wird der Preis fuer den Fisch bestimmt.
+  price_modifier = FISHMASTER->PriceOfFish(TP, ME);
+  return;
+}
+
+void SetEatMessage(string str) {
+  eatmessage=str;
+}
+
+void SetCorpseObject(string str) {
+  corpseobject=str;
+}
+
+int _set_weight(int gramm) {
+  int tmp = gramm/2 + random(gramm/2); // "nat. Schwankungen" nachbilden
+  Set(P_WEIGHT, tmp, F_VALUE);
+  SetProp(P_VALUE, tmp/4); // Im normalen Laden nur 1/4 Gewicht in Muenzen
+  return tmp;
+}
+
+void init() {
+  ::init();
+  if( QueryProp(P_FISH)&F_REPLACE ) {
+    if(query_once_interactive(environment(ME))) {
+      call_out("ReplaceFish",0);
+    }
+  }
+  return;
+}
+ 
+varargs string long(int mode) {
+  string pron = capitalize(QueryPronoun(WER));
+  string txt = QueryProp(P_LONG);
+ 
+  switch(QueryQuality()) {
+    case 4:  txt += pron+" ist fangfrisch.\n";            break;
+    case 3:  txt += pron+" ist noch recht frisch.\n";     break;
+    case 2:  txt += pron+" ist schon etwas runzlig.\n";   break;
+    case 1:  txt += pron+" sieht schon recht alt aus.\n"; break;
+    case 0:  txt += pron+" ist nicht mehr geniessbar!\n"; break;
+    case -1: txt += pron+" ist vergammelt.\n";            break;
+    default:
+      txt += (pron+" stinkt widerlich und ist bereits voller Maden!\n"+pron+
+        " zerfaellt noch waehrend Du "+QueryPronoun(WEN)+" betrachtest.\n");
+      remove();
+      break;
+  }
+  return(txt);
+}
+
+int _query_fish_age() {
+  return (time()-QueryProp(P_CLONE_TIME));
+}
+
+static int eatit(string str) {
+  string msg_other = TP->Name()+" isst "+name(WEN)+".";
+
+  if ( !eatmessage )
+    eatmessage = "Du isst "+name(WEN,1)+" auf.";
+
+  if ( stringp(corpseobject) ) {
+    object muell=clone_object(corpseobject);
+    if ( muell->move(TP, M_GET) != MOVE_OK ) {
+      muell->move(environment(TP), M_GET);
+    }
+  }
+  
+  // Heilung berechnen.
+  // Heilwert runterskalieren mit Qualitaet, wenn das Mindestalter erreicht
+  // ist, aber nur, wenn der Fisch auch vergammeln kann.
+  int healing = QueryProp(P_WEIGHT)/HEALFACTOR;
+  int age = QueryProp(P_FISH_AGE);
+  if ( age > DECAYSTART && !(QueryProp(P_FISH)&F_NOROTTEN)) {
+    healing = healing * (QueryQuality()*25)/100;
+  }
+  if ( healing > MAXHEAL )
+    healing = MAXHEAL;
+  else if ( healing < MINHEAL )
+    healing = MINHEAL;
+
+  if( age > DECAYSTART + MAXQUALITY*DECAYSTEP) {
+    tell_object(TP, BS(eatmessage + " Dir wird speiuebel, der Fisch war "
+      "zu alt!"));
+    tell_room(environment(TP), BS(msg_other + TP->QueryPronoun(WER)+
+      " verdreht angeekelt die Augen."), ({TP}));
+    TP->reduce_hit_points( ((age-DECAYSTART)/DECAYSTEP)*KILLFACTOR );
+    remove();
+  }
+  else {
+    if ( TP->eat_food(healing*FOODHEALFACTOR) ) {
+      tell_object(TP, BS(eatmessage));
+      tell_room(environment(TP), BS(msg_other), ({TP}));
+      if ( !(QueryProp(P_FISH)&F_NOHEAL) )
+        TP->buffer_hp(healing,5);
+      remove();
+    }
+  }
+  return 1;
+}
+
+// Qualitaet des Fisches ermitteln. Rueckgabewert: 4, 3, 2, 1, 0, -1 ...
+int QueryQuality() {
+  // Alter in Sekunden ueber der Grenze, ab der der Fisch anfaengt zu gammeln
+  int age = QueryProp(P_FISH_AGE)-DECAYSTART;
+  // Wenn die Grenze noch nicht erreicht ist, oder der Fisch generell nicht
+  // gammelt (F_NOROTTEN), dann ist das Alter 0 und die Qualitaet max.
+  if ( age<0 || QueryProp(P_FISH) & F_NOROTTEN ) 
+    age = 0;
+
+  // Qualitaet in DECAYSTEP-Stufen reduzieren (60 s pro Gammelstufe)
+  return MAXQUALITY-(age/DECAYSTEP);
+}
+
+void reset() {
+  if (clonep(ME)) {
+    string msg = BS(Name()+" ist vergammelt und zerfaellt.") ;
+    if ( interactive(environment()) ) {
+      tell_object(environment(), msg);
+    }
+    else if ( environment()->IsRoom() ) {
+      tell_room(environment(), msg);
+    }
+    if ( stringp(corpseobject) ) {
+      object muell=clone_object(corpseobject);
+      if ( muell->move(environment(), M_GET) != MOVE_OK ) {
+        muell->move(environment(environment()), M_GET);
+      }
+    }
+    call_out("remove",0);
+    return; 
+  }
+  return ::reset();
+}
diff --git a/std/items/fishing/haken.c b/std/items/fishing/haken.c
new file mode 100644
index 0000000..66b91f4
--- /dev/null
+++ b/std/items/fishing/haken.c
@@ -0,0 +1,157 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_shadow, no_clone
+
+inherit "/std/thing";
+
+#include <language.h>
+#include <properties.h>
+#include <items/fishing/fishing.h>
+#include <items/flasche.h>
+#include <unit.h>
+
+#define TP this_player()
+#define ME this_object()
+
+int QueryKoeder();
+
+protected void create() {
+  ::create();
+
+  AddId(({HAKEN_ID,"haken","angelhaken"}));
+  SetProp(P_NAME, "Haken");
+  SetProp(P_GENDER, MALE);
+  SetProp(P_ARTICLE, 1);
+  SetProp(P_WEIGHT, 5);
+  SetProp(P_SHORT, "Ein Angelhaken");
+  SetProp(P_LONG, "Ein Angelhaken aus Metall.\n");
+  SetProp(P_LONG_EMPTY,"Vielleicht kannst Du etwas damit aufspiessen?\n");
+
+  // Lang- und Kurzbeschreibungen reagieren auf einen evtl. vorhandenen 
+  // Koeder, allerdings nur auf ein tatsaechlich existierendes Koeder-Objekt,
+  // nicht auf einen per ueberschriebenem QueryKoeder() vorgetaeuschten
+  // Koeder, wie es der Spezialhaken tut.
+  Set(P_SHORT, function string () {
+    object koeder = present(WURM_ID, ME);
+    return Query(P_SHORT,F_VALUE)+
+      (objectp(koeder)?" mit "+koeder->name(WEM):"");
+  }, F_QUERY_METHOD);
+  Set(P_LONG, function string () { 
+    object koeder = present(WURM_ID, ME);
+    return Query(P_LONG,F_VALUE) + (objectp(koeder)?
+      koeder->Name(WER)+" haengt daran.\n":QueryProp(P_LONG_EMPTY));
+  }, F_QUERY_METHOD);
+
+  // P_FISH und P_WATER liefern die Daten eines evtl. vorhandenen Koeders
+  // Wenn kein Koeder dranhaengt, werden P_WATER und P_FISH des Hakens
+  // zurueckgegeben, falls es sich um einen Fake-Koeder handelt wie beim
+  // Spezialhaken; in diesem Fall hat der Haken selbst die Properties 
+  // gesetzt.
+  Set(P_FISH, function int () {
+    object koeder = present(WURM_ID, ME);
+    return (objectp(koeder)?koeder->QueryProp(P_FISH):Query(P_FISH));
+  }, F_QUERY_METHOD);
+  Set(P_WATER, function int () {
+    object koeder = present(WURM_ID, ME);
+    return (objectp(koeder)?koeder->QueryProp(P_WATER):Query(P_WATER));
+  }, F_QUERY_METHOD);
+
+  SetProp(P_MATERIAL,([MAT_STEEL:100]));
+
+  AddCmd(({"spiess","spiesse"}),"spiessauf");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+static int spiessauf(string str) {
+  string haken,wurmname,*substr;
+  object wurm;
+  int amount;
+
+  notify_fail("Was willst Du denn aufspiessen?\n");
+  if (!stringp(str) || !sizeof(str)) 
+    return 0;
+  
+  if( sscanf(str, "%s auf%s", wurmname, haken) != 2 )
+    return 0;
+
+  haken = trim(haken);
+
+  notify_fail("So etwas hast Du nicht bei Dir.\n");
+  if ( !objectp(wurm=present(wurmname,TP)) )
+    return 0;
+
+  notify_fail("Das kannst Du nicht aufspiessen.\n");
+  if( !wurm->id(WURM_ID) )
+    return 0;
+
+  notify_fail("Worauf willst Du "+wurm->name(WEN,1)+" denn spiessen?\n");
+  if ( haken!="" && !id(haken) )
+    return 0;
+
+  notify_fail(break_string("Dazu solltest Du "+name(WEN,1)+" erst einmal "
+    "bei Dir haben.",78));
+  if ( environment(ME) != TP )
+    return 0;
+
+  notify_fail("An dem Haken haengt schon ein Koeder.\n");
+  if ( QueryKoeder() )
+    return 0;
+
+  // Haken und Koeder sind im Inventar und fuehlen sich von der Syntax
+  // angesprochen.
+  if( wurm->IsUnit() ) {
+    // 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 also das hier gesetzte U_REQ wieder
+    // geloescht. Daher muss das jetzt hier als erstes einmal abgefragt
+    // werden...
+    wurm->QueryProp(U_REQ);
+    wurm->SetProp(U_REQ,1);
+  }
+  tell_object(TP, break_string(
+    "Du spiesst "+wurm->name(WEN,1)+" auf "+name(WEN,1)+".",78));
+  tell_room(environment(TP), break_string(
+    TP->Name(WER)+" spiesst "+wurm->name(WEN)+" auf "+name(WEN)+".",78),
+    ({TP}));
+  // M_GET, damit auch P_NODROP-Koeder aufgespiesst werden koennen.
+  if ( wurm->move(ME,M_GET) != MOVE_OK) {
+    tell_object(TP, break_string(
+      "Verdammt! Jetzt hast Du Dir fast in den Finger gestochen! "+
+      wurm->Name(WEN,1)+" hast Du dabei natuerlich verfehlt.", 78));
+  }
+  return 1;
+}
+
+int QueryKoeder(){ 
+  return objectp(present(WURM_ID, ME));
+}
+
+object QueryKoederObject() {
+  return present(WURM_ID,ME);
+}
+
+int MayAddObject(object ob) {
+  return (objectp(ob) && ob->id(WURM_ID) && sizeof(all_inventory(ME))<=1); 
+}
+
+int _query_total_weight() {
+  int tw;
+  foreach(int w: all_inventory(ME)+({ME}))
+    tw += w->QueryProp(P_WEIGHT);
+  return tw;
+}
+
+varargs int remove(int sil) {
+  all_inventory(ME)->remove(); // funktioniert auch mit leeren Arrays
+  return ::remove(sil);
+}
+
+int KoederGefressen() {
+  // Nicht QueryKoeder() pruefen, da das bei Spezialhaken immer 1 ist.
+  object koeder = present(WURM_ID, ME);
+  if ( objectp(koeder) )
+    return koeder->remove();
+  return -1;
+}
diff --git a/std/items/fishing/koeder.c b/std/items/fishing/koeder.c
new file mode 100644
index 0000000..c63317b
--- /dev/null
+++ b/std/items/fishing/koeder.c
@@ -0,0 +1,28 @@
+#pragma strong_types, save_types, rtt_checks
+#pragma no_clone, no_shadow
+
+inherit "/std/thing";
+
+#include <language.h>
+#include <properties.h>
+#include <items/fishing/fishing.h>
+
+protected void create(){
+  ::create();
+  AddId(({WURM_ID,"koeder", "wurm","regenwurm"}));
+  SetProp(P_NAME, "Wurm");
+  SetProp(P_GENDER , MALE);
+  SetProp(P_ARTICLE, 1);
+  SetProp(P_FISH, 0);
+  SetProp(P_WATER, 0);
+  SetProp(P_SHORT, "Ein kleiner Wurm");
+  SetProp(P_LONG, "Ein kleiner Regenwurm.\n");
+  SetProp(P_MATERIAL, MAT_MISC_LIVING);
+  SetProp(P_VALUE, 1);
+  SetProp(P_WEIGHT, 5);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
diff --git a/std/items/flasche.c b/std/items/flasche.c
new file mode 100644
index 0000000..43735fe
--- /dev/null
+++ b/std/items/flasche.c
@@ -0,0 +1,333 @@
+/*OBJ*/
+/* 2013-Mai-23, Arathorn
+   Umfassend ueberarbeitet; setup(), init() und ReplaceWasser() entsorgt,
+   P_WEIGHT und P_LONG dynamisch gebaut, so dass das staendige Umsetzen
+   der Properties mittels setup() nach jedem Fuellen/Leeren entfaellt.
+ */
+
+/* Letzte Aenderung: 4.5.2004, Arathorn.
+   Zeile 149: cont_obj->remove() eingefuegt, damit das geclonte Objekt
+   nach dem Abfragen seines Namens wieder entfernt wird.
+   Update 6.6.2004, Arathorn. Habe um ein destruct() ergaenzt, weil das
+   trotz remove() hartnaeckig weitergebuggt hat. *fluch*
+ */
+
+/* Version 1.3 by Fraggle (17.1.1995) */
+
+ // PreventInsert: verhindert Fuellen wie std/container
+/* Version 1.2 by Fraggle (17.1.1995) */
+
+ // Flasche kann nun mittels environment(TP)->GetLiquid()
+ // etwas anderes als Wasser enthalten, sofern
+ // environment(TP)->QueryProp(P_WATER)==W_OTHER
+ // BUG: auch 1l Methan wiegt 1 Kg, aendere ich spaeter
+
+/* Version 1.1 by Fraggle (17.1.1995) */
+#pragma strong_types,rtt_checks
+#pragma no_clone
+
+inherit "/std/thing";
+#include <language.h>
+#include <properties.h>
+#include <items/flasche.h>
+#include <items/fishing/fishing.h>
+
+#define DEFAULT_LIQ "Wasser"
+#define TP this_player()
+
+private string co_filename;
+public string liquid_name=DEFAULT_LIQ;
+
+string dynamic_long();
+
+protected void create() {
+  ::create();
+
+  AddId(({"wasserbehaelter","behaelter","\nwater_source"}));
+  SetProp(P_NAME, "Wasserbehaelter");
+  SetProp(P_GENDER , MALE);
+  SetProp(P_ARTICLE, 1);
+  SetProp(P_SHORT, "Ein Standard-Wasserbehaelter");
+  SetProp(P_LONG,"none");
+  Set(P_LONG, #'dynamic_long, F_QUERY_METHOD);
+  SetProp(P_LONG_EMPTY,""); // Beschreibung fuer leeren Zustand
+  SetProp(P_LONG_FULL,"");  // Beschreibung fuer gefuellten Zusand
+  SetProp(P_LIQUID,1000);   // Fuellmenge = 1 Liter
+  SetProp(P_WATER,0);       // Flasche ist defaultmaessig leer!
+  SetProp(P_VALUE,10);
+
+  // P_WEIGHT auf Leergewicht der Flasche setzen, QueryMethode liefert
+  // die Summe aus Leergewicht+Fuellung zurueck (Dichte = 1).
+  SetProp(P_WEIGHT,20);
+  Set(P_WEIGHT, function int () {
+     return ( Query(P_WEIGHT,F_VALUE)+
+              (QueryProp(P_WATER)?QueryProp(P_LIQUID):0) );
+     }, F_QUERY_METHOD);
+
+  AddCmd(({"fuell","fuelle"}),"cmd_fuelle");
+  AddCmd(({"leere", "entleere"}),"cmd_leere");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// Behaelter ist gefuellt, dann ist die Langbeschreibung entweder die 
+// P_LONG_FULL, falls angegben, oder die P_LONG + Beschreibung der Fuellung.
+// Genauso, wenn sie leer ist.
+string dynamic_long() {
+  string l=Query(P_LONG,F_VALUE);
+  if ( QueryProp(P_WATER) ) {
+    string lf = QueryProp(P_LONG_FULL);
+    if ( stringp(lf) && sizeof(lf) )
+      l=lf;
+    else
+      l+=capitalize(QueryPronoun(WER))+" enthaelt "+liquid_name+".\n";
+    // Falls die Flasche mit etwas anderem als Wasser gefuellt wird, die
+    // Langbeschreibung fuer "volle Flasche" (P_LONG_FULL) aber nur fuer
+    // Wasser ausgelegt ist, wird "Wasser" durch den Inhalt von liquid_name
+    // ersetzt.
+    if ( liquid_name != DEFAULT_LIQ )
+      l=regreplace(l, DEFAULT_LIQ, liquid_name, 1);
+  } else {
+    string le = QueryProp(P_LONG_EMPTY);
+    if ( stringp(le) && sizeof(le) )
+      l=le;
+    else
+      l+=capitalize(QueryPronoun(WER))+" ist leer.\n";
+  }
+  return l;
+}
+
+// Zum Ueberschreiben! PreventInsert(object ob){return 0;} z.B.
+// macht die Flasche gasdicht.
+// Oder man kann die Flasche verschliessbar machen.
+int PreventInsert(object obj)
+{
+  if(obj->id("gas")) { //default: NICHT Gasdicht!
+    write(obj->Name(WER,1)+" entweicht sofort wieder!\n");
+    return 1;
+  }
+  return 0;
+}
+
+// Transferiert den Inhalt der Flasche an <dest>
+protected int transfer_to(object dest)
+{
+  int water=QueryProp(P_WATER);
+  if (!water)
+  {
+    write(Name(WER,1) + " ist schon leer!\n");
+    return 0;   // War schon leer!
+  }
+  int contents=QueryProp(P_LIQUID);
+
+  if ( water&W_OTHER )
+  {
+    dest->PutLiquid(co_filename);
+  }
+  else
+  {
+    dest->AddWater(contents);
+  }
+  SetProp(P_WATER,0);
+  RemoveId(lower_case(liquid_name));
+  liquid_name=DEFAULT_LIQ;
+  return contents; //gib die ml an Umgebung ab :)
+}
+
+// Entleert die Flasche ins Environment der Flasche, allerdings nicht, wenn
+// dies ein Lebewesen ist, dann wird das Environment von dem genommen.
+// TODO: Eine Flasche in einem Paket leeren wurde dann in das paket entleeren.
+// Das waere an sich sogar richtig... Nur: gewollt? Alternative koennen wir
+// auch das aeusserste Environment nehmen, was nicht lebt.
+public int empty()
+{
+  if (environment())
+  {
+    // Environment des Benutzers finden.
+    object env = environment();
+    while (living(env))
+      env=environment(env);
+    return transfer_to(env);
+  }
+  return 0;
+}
+
+// Fuellt die Flasche aus <src>
+protected int fill_bottle(object src)
+{
+  int liquidtype = src->QueryProp(P_WATER);
+  if(liquidtype)
+  {
+    if(QueryProp(P_WATER)) {
+      write(Name(WER,1)+" ist bereits voll!\n");
+      return 1;
+    }
+    // Wasser von Umgebung abziehen!
+    // Man kann als Magier die Funktion AddWater(int n) dazu benuetzten,
+    // beispielsweise eine Pfuetze zu leeren, ...
+    src->AddWater(-QueryProp(P_LIQUID));
+    object cont_obj;
+    if(liquidtype&W_OTHER)
+    {
+      // Mittels GetLiquid() kann die Flasche mit was anderem als Wasser
+      // gefuellt werden.
+      co_filename=src->GetLiquid();
+      if (co_filename)
+      {
+        cont_obj=clone_object(co_filename);
+        if(PreventInsert(cont_obj))
+        {
+          // Hier passiert eigentlich das gleiche wie nach dem ifblock, aber
+          // auch noch Funktion beenden.
+          // TODO: Rueckgaenig machen von AddWater()?
+          // TODO: Die Meldung aus dem PreventInsert() muesste eigentlich
+          // _vorher_ noch mit einer Befuellmeldung begleitet werden.
+          cont_obj->remove(1);
+          if ( objectp(cont_obj) )
+            cont_obj->move("/room/muellraum",M_PUT);
+          cont_obj=0;
+          return 0;
+        }
+        else
+          liquid_name=cont_obj->name(WEN);
+        // In jedem Fall wird das Objekt wieder zerstoert - es wurde nur fuer
+        // das Ermitteln des liquid_name benutzt... Weia.
+        if ( cont_obj ) cont_obj->remove();
+        if ( cont_obj ) cont_obj->move("/room/muellraum",M_PUT);
+      }
+    }
+    SetProp(P_WATER,liquidtype);
+    AddId(lower_case(liquid_name));
+    //wie praktisch, 1 ml == 1 g :) // Aber nur fuer Wasser, du VOGEL! :-|
+    return 1;
+  }
+  else {
+    write("Du findest hier nichts, was Du in "+name(WEN,1)+
+      " fuellen koenntest!\n");
+    return 0;
+  }
+  return 0;
+}
+
+static int cmd_leere(string str)
+{
+  object dest;
+  notify_fail("Was willst Du denn (wo hinein) leeren?\n");
+  if (!str)
+    return 0;
+
+  string strbottle,strobj;
+  // leere flasche
+  if (id(str))
+  {
+    //NOOP
+  }
+  // leere flasche in xxx
+  else if (sscanf(str,"%s in %s",strbottle,strobj)==2)
+  {
+    if (!id(strbottle))
+      return 0;
+    dest = present(strobj, environment(this_player()))
+           || present(strobj, this_player());
+    if (!dest)
+      return 0;
+  }
+  else
+    return 0;
+  // Syntaxpruefung fertig.
+
+  if(!QueryProp(P_WATER))
+  {
+    write("Da ist kein "+liquid_name+" drin!\n");
+    return 1;
+  }
+
+  if (dest)
+  {
+    write(break_string("Du leerst "+name(WEN,1)+ " in "
+          + dest->name(WEN) + ".",78));
+    say(break_string(TP->name()+" leert "+name(WEN,0)
+          + " in " + dest->name(WEN) + ".",78),TP);
+    transfer_to(dest);
+    return 1;
+  }
+  write(break_string("Du leerst "+name(WEN,1)+".",78));
+  say(break_string(TP->name()+" leert "+name(WEN,0)+".",78),TP);
+  empty();
+  return 1;
+}
+
+public int cmd_fuelle(string str)
+{
+  string strbottle,strobj;
+
+  notify_fail("Was willst Du denn (womit) fuellen?\n");
+  if(!str)
+    return 0;
+
+  // fuelle flasche
+  if (id(str))
+  {
+    if (fill_bottle(environment(this_player())))
+    {
+      write(break_string("Du fuellst etwas "+liquid_name+" in "
+            +name(WEN,1)+".",78));
+      say(break_string(TP->Name(WER)+" fuellt etwas "
+            +liquid_name+" in "+name(WEN)+".",78), TP);
+    }
+    return 1;
+  }
+  // fuelle flasche aus xxx
+  // fuelle flasche mit xxx
+  // fuelle xxx in flasche
+  // fuelle flasche in xxx
+  // fuelle xxx aus flasche
+  // fuelle xxx mit flasche
+  else if (sscanf(str,"%s in %s",strobj,strbottle)==2
+           || sscanf(str,"%s mit %s",strbottle,strobj)==2
+           || sscanf(str,"%s aus %s",strbottle,strobj)==2)
+  {
+    object obj;
+    // Flasche befuellen?
+    if (id(strbottle)
+        && ( obj=present(strobj, environment(this_player())) 
+                 || present(strobj, this_player()) )
+       )
+    {
+      if (fill_bottle(obj))
+      {
+        write(break_string(
+              "Du fuellst etwas "+liquid_name+" aus " + obj->name(WEM,1) 
+              + " in "+name(WEN,1)+".",78));
+        say(break_string(TP->Name(WER)+" fuellt etwas "+liquid_name+ " aus " 
+              + obj->name(WEM,1) + " in "+name(WEN)+".",78), TP);
+      }
+      return 1;
+    }
+    // anderes Gefaess befuellen?
+    else if (id(strobj)
+        && ( obj=present(strbottle, environment(this_player())) 
+                 || present(strbottle, this_player()) )
+       )
+    {
+      if (transfer_to(obj))
+      {
+        write(break_string(
+              "Du fuellst etwas "+liquid_name+" aus " + name(WEM,1)
+              + " in "+obj->name(WEN,1)+".",78));
+        say(break_string(TP->Name(WER)+" fuellt etwas "+liquid_name+ " aus " 
+              + name(WEM,1) + " in "+obj->name(WEN)+".",78), TP);
+      }
+      return 1;
+    }
+  }
+  // Syntax passt nicht.
+  return 0;
+}
+
+int IsBottle() {
+   return 1;
+}
+
diff --git a/std/items/kraeuter/kraut.c b/std/items/kraeuter/kraut.c
new file mode 100644
index 0000000..2ce5045
--- /dev/null
+++ b/std/items/kraeuter/kraut.c
@@ -0,0 +1,195 @@
+// (c) September 2000 by Padreic (Padreic@mg.mud.de)
+
+#pragma strong_types, save_types, rtt_checks
+
+inherit "/std/thing";
+
+#include <properties.h>
+#include <items/kraeuter/kraeuter.h>
+
+#define PLANT_LIFETIME  (24*3600)
+#define FRESH_TIME      (6*3600)
+// Die plantID wird fuer ungueltig erschaffene Kraeuter auf -1 gesetzt.
+#define DRIED_PLANT     -1
+
+private int age=time();
+// enthaelt die Nummer des Krauts
+private int plantId;
+// enthaelt den Pfad des clonenden Objekts
+private string cloner;
+// Qualitaet des Krautes.
+private int quality=100;
+
+// File kann ein name sein, dessen Eigenschaften der VC konfigurieren sein
+// oder 0, wenn er selber ermitteln soll, was fuer ein File er gerade erzeugt
+// hat.
+// Sprich: real existierende Files auf der Platte muessen da ihren Namen
+// angeben, Standardfall ist aber, dass man 0 angibt und der VC weiss, was er
+// gerade erzeugt hat.
+void customizeMe(string file)
+{
+  if (stringp(file)) 
+    file=explode(file, "/")[<1];
+  KRAEUTERVC->CustomizeObject(file);
+}
+
+protected void create()
+{
+  if (object_name(this_object()) == __FILE__[0..<3])
+  {
+    set_next_reset(-1);
+    return;
+  }
+  ::create();
+ 
+  Set(P_QUALITY, function int () { return quality; }, F_QUERY_METHOD);
+  SetProp(P_WEIGHT, 120);
+  SetProp(P_VALUE, 70);
+  SetProp(P_MATERIAL, MAT_MISC_PLANT);
+}
+
+protected void create_super()
+{
+  set_next_reset(-1);
+}
+
+public string short()
+{
+  string str=QueryProp(P_SHORT);
+  if (!stringp(str)) 
+      return 0;
+  if (plantId==-1)
+      return str+" (unwirksam).\n";
+  else if (age==DRIED_PLANT)
+     return str+" (getrocknet).\n";
+  else if (age+FRESH_TIME+PLANT_LIFETIME<time())
+     return str+" (verfault).\n";
+  return str+".\n";
+}
+
+// Liefert einen Skalierungsfaktor zurueck, der mit dem aktuellen Wert von
+// P_QUALITY verrechnet wird. Rueckgabewerte:
+// Pflanze getrocknet oder nicht aelter als 6 h : 100
+// Pflanze aelter als 6, aber juenger als 30 h : 99...1
+// Pflanze aelter als 30 h: 0.
+// DryPlant() zerstoert das Kraut, wenn P_QUALITY unter 1 faellt.
+public int PlantQuality()
+{
+  int factor;
+  // schon getrocknet oder nicht aelter als 6 h? 
+  // Dann keine weitere Reduktion.
+  if ( age == DRIED_PLANT || age+FRESH_TIME > time()) 
+    factor = 100;
+  // >30 Stunden nach dem Pfluecken ist das Kraut verschimmelt.
+  else if ( age + FRESH_TIME + PLANT_LIFETIME < time() ) 
+    factor = 1;
+  // Zeit zwischen 6 und 30 Stunden nach dem Pfluecken in 99 gleichmaessige
+  // Abschnitte unterteilen. 24 h sind 86400 s, 86400/99 = 873.
+  else
+    factor=(time()-age-FRESH_TIME)/873;
+
+  return QueryProp(P_QUALITY)*factor/100;
+}
+
+// Wie lange (in Sekunden) ist das Kraut noch haltbar?
+public int TimeToLive()
+{
+  if ( age == DRIED_PLANT )
+    return __INT_MAX__;
+  return age-time()+PLANT_LIFETIME;
+}
+
+//TODO: vielleicht etwas zufall? Sonst Kraeuterqualitaet hieran ermittelbar.
+static int _query_value()
+{
+  int val = Query(P_VALUE,F_VALUE)*PlantQuality()/100;
+  if (plantId<=0 || val = 0)
+    return 0;
+  return val-val%10;
+}
+
+static string _query_nosell() 
+{
+  if (age != DRIED_PLANT)
+    return "Mit ungetrockneten Kraeutern handele ich nicht. Die verderben "
+      "immer so schnell im Lager, und dann werde ich sie nicht wieder los.";
+  return 0;
+}
+
+// Mit DryPlant() wird die Pflanze getrocknet. Als Argument wird der Prozent-
+// wert uebergeben, auf den die Qualitaet sinken soll. Als Ausgangswert
+// dieser Berechnung wird der Rueckgabewert von PlantQuality() verwendet.
+// Hintergrund: Die Qualitaet des Krauts sinkt im ungetrockneten Zustand
+// ueber einen Zeitraum von 24 h kontinuierlich von 100 auf 1 ab, sobald
+// es aelter als 6 Stunden ist. Danach ist es verschimmelt, was aber seiner
+// verbleibenden "Wirkung" keinen Abbruch tut.
+// Es wird die zum Zeitpunkt des Trocknungsvorganges gueltige Qualitaet
+// also sozusagen "eingefroren" und entsprechend dem Rueckgabewert von
+// PlantQuality() heruntergerechnet.
+
+// Diese Funktion kann natuerlich auch ueberschrieben werden, wenn bestimmte
+// Kraeuter erst durch trocknen in der Qualitaet steigen.
+
+// TODO: Ist das Argument "qual" dabei prozentual aufzufassen, oder 
+// soll nur ein noch zu bestimmender Festwert abgezogen werden?
+// Arathorn: Es bleibt jetzt erstmal prozentual.
+
+#define DRYING_ALLOWED ({PLANTMASTER, "/items/kraeuter/trockner"})
+
+public void DryPlant(int qual) 
+{
+  // Keine mehrfache Trocknung zulassen.
+  if ( age == DRIED_PLANT )
+    return;
+ 
+  // Nur bestimmte Objekte duerfen Trocknungen ausloesen.
+  if ( member(DRYING_ALLOWED, load_name(previous_object())) == -1 )
+    return;
+
+  // Qualitaet auf 100 deckeln.
+  if ( qual>100 )
+    qual = 100;
+  
+  // Qualitaet mittels PlantQuality() runterskalieren.
+  qual = PlantQuality()*qual/100;
+  
+  if ( qual < 1 ) {
+    if(objectp(this_player()))
+      tell_object(this_player(), 
+        Name(WER,1)+" zerfaellt in unzaehlige feine Kruemel.\n");
+    remove();
+    return;
+  }
+  // Kraut als getrocknet kennzeichnen.
+  age=DRIED_PLANT;
+  quality = qual;
+}
+
+/* Funktionen zum Initialisieren der Pflanze */
+// Der Kraeutermaster prueft den Clone auf Gueltigkeit. Eine einmal gesetzte
+// ID kann auf diesem Weg nicht mehr geaendert werden.
+nomask int SetPlantId(int new_id)
+{
+  if (plantId != 0)
+    return -1;
+  cloner=0;
+  age=time();
+  if (catch(cloner=call_other(PLANTMASTER, "CheckPlant", new_id)) || !cloner)
+    new_id = -1;
+  return (plantId=new_id);
+}
+
+nomask int QueryPlantId()
+{
+  return plantId;
+}
+
+nomask string QueryCloner()
+{
+  return cloner;
+}
+
+nomask int QueryDried() 
+{
+  return (age == DRIED_PLANT);
+}
diff --git a/std/items/kraeuter/trank.c b/std/items/kraeuter/trank.c
new file mode 100644
index 0000000..f3c0efe
--- /dev/null
+++ b/std/items/kraeuter/trank.c
@@ -0,0 +1,1147 @@
+//TODO:
+
+#pragma strong_types,rtt_checks
+
+inherit "/std/thing";
+
+#include <properties.h>
+#include <defines.h>
+#include <items/kraeuter/kraeuter.h>
+#include <items/kraeuter/trankattribute.h>
+#include <hook.h>
+#include <class.h>
+#include <new_skills.h>
+#include <wizlevels.h>
+
+#ifndef BS
+#  define BS(x)             break_string(x, 78)
+#endif
+
+#define allowed(x) (object_name(x)==PLANTMASTER)
+#define DRINK_POTION "lib_kraeutertrank_trinken"
+// for debug
+#define private public
+
+// Ablaufzeit des Tranks, ab dann keine Wirkung mehr
+private nosave int expiry;
+// Ablaufzeit der wirkungen des Trankes (0, wenn Trank nicht getrunken)
+private nosave int duration;
+// Trankattribute, vom Krautmaster schon skaliert, gekappt, beschraenkt auf
+// die jeweils max. positiven Effekte
+private nosave mapping data;
+// Klassen, die bei Schutz oder verstaerktem Schaden betroffen sind. Werte
+// ist der Schutz- oder Attackebonus/-malus.
+private nosave mapping att_classes;
+private nosave mapping prot_classes;
+// max. Summe von Giftstufen (P_POISON und P_LEVEL/10 bei CL_POISON).
+private nosave int prot_poison;
+// max. geheilte Summe von Krankheitslevel (P_LEVEL/5 in CL_DISEASE).
+private nosave int prot_disease;
+// nach Wirkung absteigend sortierte Liste der Effekte, wird beim ersten
+// Aufruf von DetermineStrongesEffect() befuellt.
+private nosave string* sorted_effects;
+
+mixed _query_data() {return data;}
+int _set_data(mixed d) {data=d; expiry = __INT_MAX__; return data!=0;}
+private string effect2colour();
+
+protected void create()
+{
+  if (object_name(this_object()) == __FILE__[0..<3])
+  {
+    set_next_reset(-1);
+    return;
+  }
+  ::create();
+  SetProp(P_GENDER, FEMALE);
+  SetProp(P_NAME, "Glasflasche");
+  SetProp(P_NAME_ADJ, ({"klein"}));
+  SetProp(P_VALUE, 10);
+  SetProp(P_WEIGHT, 100);
+  SetProp(P_KILL_NAME, "Ein Kraeutertrank");
+  SetProp(P_KILL_MSG, "%s hat sich wohl beim Anruehren vertan.");
+  AddId(({"glasflasche", "flasche"}));
+  AddAdjective(({"klein", "kleine"}));
+  AddCmd("trink|trinke&@ID", "cmd_trinken",
+    "Was willst Du trinken?");
+}
+
+protected void create_super()
+{
+  set_next_reset(-1);
+}
+
+static string _query_short()
+{
+  if (!clonep(ME))
+    return "Eine kleine Glasflasche";
+  return Name(WEN, 0)+(data==0 ? "" : " (gefuellt)");
+}
+
+static string _query_long()
+{
+  if (data==0)
+    return break_string(
+      "Eine kleine leere Glasflasche, die mit einem Korken verschlossen "
+      "ist.\n"
+      "Flaschen wie diese werden ueblicherweise verwendet, um darin "
+      "magische Traenke abzufuellen.", 78, 0, BS_LEAVE_MY_LFS);
+
+  return break_string(
+    "Eine kleine Glasflasche, die mit einer "+effect2colour()+"en "
+    "Fluessigkeit gefuellt ist.\n"
+    "Sie ist mit einem Korken verschlossen, um den Inhalt der "
+    "Flasche zu schuetzen.",78, 0, BS_LEAVE_MY_LFS);
+}
+
+private string num2desc(int bumms)
+{
+  switch(abs(bumms))
+  {
+    case 0..499:
+      return "ein wenig";
+    case 500..999:
+      return "so einiges";
+    case 1000..1499:
+      return "erheblich";
+    case 1500..2000:
+      return "unglaublich";
+  }
+  return "ungenehmigt viel"; // kommt hoffentlich nicht vor.
+}
+
+varargs private string DetermineStrongestEffect(int pos)
+{
+  // globale Werteliste befuellen, wenn da noch nichts drinsteht.
+  if ( !pointerp(sorted_effects) ) {
+    sorted_effects = sort_array(m_indices(data) & T_KRAUT_EFFECTS,
+      function int (string a, string b) {
+        return (abs(data[a])<=abs(data[b]));
+      });
+  }
+
+  // Zur Indizierung des Arrays muss <pos> bei Null starten, es wird
+  // aber mit der Bedeutung einer Ordinalzahl (erste, zweite, dritte, ...)
+  // uebergeben. Daher um 1 reduzieren.
+  --pos;
+
+  string ret;
+
+  // Im Array muss mindestens ein Eintrag stehen, sonst gibt's gar keinen
+  // Effekt.
+  if ( sizeof(sorted_effects) )
+  {
+    // Wenn der angefragte Index ausserhalb der Arraygrenzen liegt, wird
+    // angenommen, dass der erste bzw. letzte Eintrag gesucht waren.
+    if ( pos < 0 )
+      ret = sorted_effects[0];
+    else if ( pos >= sizeof(sorted_effects) )
+      ret = sorted_effects[<1];
+    else
+      ret = sorted_effects[pos];
+  }
+  return ret;
+}
+
+// Liefert zu dem maximal wirksamen Effekt die Farbe des Trankes zurueck.
+private string effect2colour()
+{
+  // Ist die Haltbarkeit schon abgelaufen, wird der Trank farblos.
+  if ( time() > expiry )
+    return "farblos";
+
+  // Namen des staerksten Effekts holen.
+  string effect = DetermineStrongestEffect(1);
+  mapping colours = ([
+    T_CARRY:              "trueb braun",
+    T_DAMAGE_ANIMALS:     "blutrot",
+    T_DAMAGE_MAGIC:       "oktarinfarben",
+    T_DAMAGE_UNDEAD:      "anthrazitfarben",
+    T_FLEE_TPORT:         "schwefelgelb",
+    T_FROG:               "schlammgruen",
+    T_HEAL_DISEASE:       "perlmuttfarben",
+    T_HEAL_POISON:        "gruen",
+    T_HEAL_SP:            "blau",
+    T_HEAL_LP:            "scharlachrot",
+    T_PROTECTION_ANIMALS: "metallisch grau",
+    T_PROTECTION_MAGIC:   "violett",
+    T_PROTECTION_UNDEAD:  "strahlend weiss",
+    T_SA_SPEED:           "orangefarben",
+    T_SA_SPELL_PENETRATION: "stahlblau",
+    T_SA_DURATION:        "pinkfarben",
+  ]);
+  string ret = colours[effect];
+  return stringp(ret) ? ret : "farblos";
+}
+
+// Wird gerufen, wenn die Wirkung des Trankes ablaufen soll.
+private void terminate_effects()
+{
+  tell_object(environment(),
+      "Die letzten Wirkungen des Kraeutertrankes klingen ab.\n");
+  remove(1);
+}
+
+// Von den Hooks H_HOOK_ATTACK_MOD und H_HOOK_DEFEND gerufen, erhoeht oder
+// verringert den Schaden gegen Lebenwesen bestimmter Klassen (Keys in
+// <classes>). Der Malus/Bonus steht als Wert zum jeweiligen Key in dem
+// Mapping.
+mixed hcallback(object hookSource, int hookid, mixed hookData)
+{
+    if (hookSource != environment())
+        return ({H_NO_MOD,hookData});
+    switch(hookid)
+    {
+      case H_HOOK_ATTACK_MOD:
+        foreach(string class, int modval : att_classes)
+        {
+          if (hookData[SI_ENEMY]->is_class_member(class))
+          {
+            // Yeah. Treffer. Schaden erhoehen oder verringern... ;)
+            hookData[SI_SKILLDAMAGE] += modval;
+            // Ende. keine weiteren Klassen pruefen.
+            return ({H_ALTERED, hookData});
+          }
+        }
+        break;
+      case H_HOOK_DEFEND:
+        // hookData: ({dam,dam_type,spell,enemy})
+        foreach(string class, int modval : prot_classes)
+        {
+          if (hookData[3]->is_class_member(class))
+          {
+            // Yeah. Treffer. Schaden erhoehen oder verringern... ;)
+            hookData[0] += modval;
+            // Ende. keine weiteren Klassen pruefen.
+            return ({H_ALTERED, hookData});
+          }
+        }
+        break;
+      case H_HOOK_INSERT:
+        // Wenn die Giftschutzkapazitaet noch ausreicht, wird das reinkommende
+        // Objekt zerstoert (und die Kapazitaet reduziert).
+        // hookData: neues object
+        if (prot_poison > 0
+            && hookData->is_class_member(CL_POISON))
+        {
+          // kostet ein Zehntel des Levels, aber min. 1.
+          int p=hookData->QueryProp(P_LEVEL) / 10 + 1;
+          if (p < prot_poison)
+          {
+            hookData->remove(1);
+            prot_poison-=p;
+            return ({H_CANCELLED, hookData});
+          }
+        }
+        // Wenn die Krankheitsschutzkapazitaet noch ausreicht, wird das reinkommende
+        // Objekt zerstoert (und die Kapazitaet reduziert).
+        // hookData: neues object
+        if (prot_disease > 0
+            && hookData->is_class_member(CL_DISEASE))
+        {
+          // kostet ein Fuenftel des Levels, aber min. 1.
+          int lvl = hookData->QueryProp(P_LEVEL) / 5 + 1;
+          if (lvl < prot_disease)
+          {
+            hookData->remove(1);
+            prot_disease-=lvl;
+            return ({H_CANCELLED, hookData});
+          }
+        }
+        break;
+      case H_HOOK_POISON:
+        // hookData: poisonval
+        // Alle Giftlevel werden reduziert auf 0 und von prot_poison
+        // abgezogen. Wenn das 0 ist, endet der Giftschutz.
+        if (prot_poison>0)
+        {
+          if (hookData < prot_poison)
+          {
+            prot_poison-=hookData;
+            hookData = 0;
+          }
+          else
+          {
+            hookData -= prot_poison;
+            prot_poison=0;
+          }
+          return ({H_ALTERED, hookData});
+        }
+        break;
+    }
+    return ({H_NO_MOD, hookData});
+}
+
+private int reg_hook(int hook, int hooktype)
+{
+  // Wenn schon registriert, zaehlt das auch als Erfolg.
+  if (environment()->HIsHookConsumer(hook, #'hcallback))
+    return 1;
+  int res = environment()->HRegisterToHook(hook, #'hcallback,
+                 H_HOOK_OTHERPRIO(0), hooktype, duration);
+  if (res <= 0)
+  {
+    // wenn andere Fehler als -7 (zuviele Hooks registriert) vorkommen:
+    // Fehlermeldung ausgeben
+    if (res != -7)
+      tell_object(environment(),break_string(sprintf(
+          "Technischer Hinweis, den Du an einen Magier weitergeben "
+          "solltest: Beim Registrieren des Hooks %d gab es Fehler: %d\n",
+          hook, res),78));
+    return 0;
+  }
+  return 1;
+}
+
+// effekt: Wirkungswert des Tranks (muss negativ sein)
+// type: 1 fuer Gift, 0 fuer Krankheit
+private void ticktack(int effekt, int type)
+{
+  // Schaden tickt alle 5 Sekunden
+  int delay = 5;
+  // Der halbe Betrag des negativen Effekts wird als Schaden am
+  // Spieler verursacht.
+  // Berechnung: (Schaden pro Sekunde) * (Delay in Sekunden)
+  // in float rechnen ist hier sinnvoll, inkl. aufrunden (durch die +0.5)
+//  int dam = to_int(((-0.5*effekt)/duration)*delay + 0.5);
+  int dam = to_int(0.5*abs(effekt)/data[T_EFFECT_DURATION]*delay + 0.5);
+
+  if (type)
+    tell_object(environment(),
+      break_string("Gift pulsiert brennend durch Deine Adern.",78));
+  else
+    tell_object(environment(),
+      break_string("Du fuehlst Dich schwach und elend, eine Erkrankung "
+      "zehrt an Deinen Kraeften.",78));
+
+  environment()->do_damage(dam, this_object());
+  call_out(#'ticktack, delay, effekt, type);
+}
+
+private int act_attr_heal_poison(int effekt)
+{
+  int erfolgreich;
+  tell_object(environment(), break_string(
+     "Du fuehlst, wie der Trank wie Feuer durch Deinen Koerper schiesst "
+     "und kruemmst Dich vor Schmerzen. Doch Momente spaeter laesst die "
+     "Tortur nach.",78));
+
+  // max. 40 Giftlevel heilen...
+  prot_poison = effekt / 50;
+
+  if (prot_poison < 0)
+  {
+    tell_object(environment(), BS(
+      "Bah! Der Trank schmeckt widerlich bitter. Wenn der mal nicht "
+      "giftig war."));
+    call_out(#'ticktack, 5, effekt, 1); // 1 => Gift
+    return 1;
+  }
+  
+  // ab jetzt nur noch positive Wirkungen.
+
+  // P_POISON zuerst.
+  int poison = environment()->QueryProp(P_POISON);
+  if (poison)
+  {
+    if (poison <= prot_poison)
+    {
+      prot_poison -= poison;
+      environment()->SetProp(P_POISON,0);
+      if (!environment()->QueryProp(P_POISON))
+        ++erfolgreich;
+    }
+    else
+    {
+      poison -= prot_poison;
+      prot_poison=0;
+      environment()->SetProp(P_POISON, poison);
+      // Wenn erfolgreich, direkt Meldung und raus.
+      if (environment()->QueryProp(P_POISON) == poison)
+      {
+        tell_object(environment(), break_string(
+           "Ueberrascht stellst Du fest, dass Du Dich "
+           "besser fuehlst - der Trank hat Deine Vergiftung offenbar "
+           "gelindert.",78));
+        return 1;
+      }
+    }
+  }
+
+  // wenn Trank immer noch positiv (also noch WIrkung uebrig)
+  if (prot_poison > 0)
+  {
+    // Als naechstes Objekte suchen.
+    object *ob = filter_objects(all_inventory(environment()),
+                                "is_class_member", CL_POISON);
+    foreach(object o: ob)
+    {
+      // Entgiften kostet ein Zehntel des Levels, aber min. 1.
+      poison = o->QueryProp(P_LEVEL);
+      if (poison <= prot_poison*10)
+      {
+        prot_poison -= poison/10 + 1;
+        o->SetProp(P_LEVEL, 0);
+        if (o->remove())
+          ++erfolgreich;
+      }
+      else
+      {
+        poison -= prot_poison * 10;
+        prot_poison = 0;
+        o->SetProp(P_LEVEL, poison);
+        if (o->QueryProp(P_LEVEL) == poison)
+          ++erfolgreich;
+      }
+      if (prot_poison <= 0)
+        break;
+    }
+  }
+
+  if (erfolgreich)
+  {
+    tell_object(environment(), break_string(
+      "Ueberrascht stellst Du fest, dass Du Dich viel besser fuehlst - der "
+      "Trank wirkt offenbar gegen Vergiftungen.",78));
+  }
+  else
+  {
+    tell_object(environment(), break_string(
+      "Eine Ahnung sagt Dir, dass der Trank irgendeine positive "
+      "Wirkung hat."));
+  }
+
+  // ggf. an die Hooks registrieren, wenn noch Schutzwirkung uebrig ist.
+  if (prot_poison > 0)
+  {
+    // Rueckgabewerte von HRegisterToHook speichern...
+    int *res = ({ reg_hook(H_HOOK_POISON, H_DATA_MODIFICATOR) });
+    res += ({ reg_hook(H_HOOK_INSERT, H_HOOK_MODIFICATOR) });
+    // Wenn alle versuchten Registrierungen erfolgreich waren...? Ansonsten
+    // andere Meldung... Ich bin noch nicht gluecklich, das hier so explizit
+    // auszugeben, aber ich weiss gerade sonst nicht, wie man drauf kommen
+    // soll, dass es ein Problem gibt.
+    if (sizeof(res) == sizeof(res & ({1})))
+      tell_object(environment(),
+         "Vielleicht haelt diese Wirkung ja sogar noch etwas an?\n");
+    else
+    {
+      // zumindest ein erfolg?
+      if (member(res, 1) > -1)
+        tell_object(environment(),
+            "Vielleicht haelt ein Teil dieser Wirkung ja sogar noch etwas an?\n");
+    }
+  }
+  return 1;
+}
+
+private int act_attr_heal_disease(int effekt)
+{
+  int erfolgreich;
+
+  // max. 40 Krankheitslevel heilen...
+  prot_disease = effekt / 50;
+
+  if (prot_disease > 0)
+  {
+     tell_object(environment(), break_string(
+       "Du fuehlst, wie der Trank in Deinem Bauch eine wohlige Waerme "
+       "verbreitet und laechelst unwillkuerlich.",78));
+   
+    // Objekte suchen.
+    object *ob = filter_objects(all_inventory(environment()),
+                                "is_class_member", CL_DISEASE);
+    foreach(object o: ob)
+    {
+      // Heilen kostet ein Fuenftel des Levels, aber min. 1.
+      int disease = o->QueryProp(P_LEVEL);
+      if (disease <= prot_disease*5)
+      {
+        prot_disease -= disease/5 + 1;
+        o->SetProp(P_LEVEL, 0);
+        if (o->remove())
+          ++erfolgreich;
+      }
+      else
+      {
+        disease -= prot_disease * 5;
+        prot_disease = 0;
+        o->SetProp(P_LEVEL, disease);
+        if (o->QueryProp(P_LEVEL) == disease)
+          ++erfolgreich;
+      }
+      if (prot_disease <= 0)
+        break;
+    }
+  }
+  else
+  {
+    tell_object(environment(), BS(
+      "Der Trank schmeckt eklig faulig. Dein Magen rebelliert umgehend. "
+      "Du kannst Deinen Brechreiz gerade noch unterdruecken, fuehlst "
+      "Dich aber krank."));
+    call_out(#'ticktack, 5, effekt, 0); // 0 => Krankheit
+    return 1;
+  }
+
+  if (erfolgreich)
+  {
+    tell_object(environment(), break_string(
+      "Entspannt durchatmend stellst Du fest, dass Du Dich viel besser fuehlst - der "
+      "Trank wirkt offenbar gegen Krankheiten.",78));
+  }
+  else
+  {
+    tell_object(environment(), break_string(
+      "Eine Ahnung sagt Dir, dass der Trank irgendeine positive "
+      "Wirkung hat."));
+  }
+
+  // ggf. an die Hooks registrieren.
+  if (prot_disease > 0)
+  {
+    // Registrierung erfolgreich...? Ansonsten andere Meldung... Ich bin
+    // noch nicht gluecklich, das hier so explizit auszugeben, aber ich
+    // weiss gerade sonst nicht, wie man drauf kommen soll, dass es ein
+    // Problem gibt.
+    if (reg_hook(H_HOOK_INSERT, H_HOOK_MODIFICATOR)==1)
+      tell_object(environment(),
+         "Vielleicht haelt diese Wirkung ja sogar noch etwas an?\n");
+
+  }
+  return 1;
+}
+
+private string num2desc_fight(int bumms)
+{
+  switch(abs(bumms))
+  {
+    case 0..499:
+      return "ein wenig";
+    case 500..999:
+      return "spuerbar";
+    case 1000..1499:
+      return "deutlich";
+    case 1500..2000:
+      return "erheblich";
+  }
+  return "ungenehmigt viel"; // kommt hoffentlich nicht vor.
+}
+
+// AN: Tiere sind: CL_ANIMAL, CL_FISH, CL_FROG, CL_INSECT, CL_MAMMAL,
+// CL_MAMMAL_LAND, CL_MAMMAL_WATER, CL_REPTILE, CL_ARACHNID, CL_BIRD
+private int act_attr_dam_animals(int effekt)
+{
+  if (reg_hook(H_HOOK_ATTACK_MOD, H_DATA_MODIFICATOR) == 1)
+  {
+    if (!mappingp(att_classes)) att_classes=m_allocate(1);
+    att_classes[CL_ANIMAL] = effekt/20;
+    tell_object(environment(), break_string(
+        "Du spuerst in Dir ein seltsames Verlangen aufsteigen, auf die Jagd "
+        "zu gehen - als wuerde Artemis persoenlich Deine Angriffe "
+        + num2desc_fight(effekt)
+        + " verbessern.",78));
+    return 1;
+  }
+  return 0;
+}
+
+// AN: Magische Wesen sollen sein: CL_ELEMENTAL, CL_ILLUSION, CL_SHADOW
+// CL_DRAGON, CL_DEMON, CL_SHAPECHANGER, CL_HARPY
+private int act_attr_dam_magical(int effekt)
+{
+  if (reg_hook(H_HOOK_ATTACK_MOD, H_DATA_MODIFICATOR) == 1)
+  {
+    if (!mappingp(att_classes)) att_classes=m_allocate(4);
+    att_classes[CL_DRAGON] = att_classes[CL_ELEMENTAL]
+                            = att_classes[CL_SHADOW]
+                            = att_classes[CL_ILLUSION]
+                            = effekt/20;
+    tell_object(environment(), break_string(
+        "Merkwuerdig. Du hast gerade das Gefuehl, als fiele Dir der "
+        "Kampf gegen von Hekate beschenkte Wesen "
+        + num2desc_fight(effekt)
+        + " leichter.",78));
+    return 1;
+  }
+  return 0;
+}
+
+// AN: Untote sollen sein: CL_SKELETON, CL_GHOUL, CL_GHOST, CL_VAMPIRE
+// CL_ZOMBIE, CL_UNDEAD
+// Bloed ist nur, dass CL_UNDEAD alle anderen enthaelt bis auf CL_GHOST.
+// Also lassen wir die Unterscheidung und schrittweise Freischaltung
+// erst einmal sein.
+private int act_attr_dam_undead(int effekt)
+{
+  if (reg_hook(H_HOOK_ATTACK_MOD, H_DATA_MODIFICATOR) == 1)
+  {
+    // Zombies, Skelette, Ghule...
+    if (!mappingp(att_classes)) att_classes=m_allocate(1);
+    att_classes[CL_UNDEAD] =  effekt/20;
+    tell_object(environment(), break_string(
+        "Auf einmal hast Du den Eindruck, dass die Kreaturen des "
+        "Hades Deinen Angriffen "
+        + num2desc_fight(effekt)
+        + " weniger entgegen zu setzen haben.",78));
+    return 1;
+  }
+  return 0;
+}
+
+private int act_attr_prot_animals(int effekt)
+{
+  if (reg_hook(H_HOOK_DEFEND, H_DATA_MODIFICATOR) == 1)
+  {
+    if (!mappingp(prot_classes)) prot_classes=m_allocate(1);
+    prot_classes[CL_ANIMAL] = effekt/20;
+    tell_object(environment(), break_string(
+        "Du hast das Gefuehl, dass Artemis ihre schuetzende Hand "
+        + num2desc_fight(effekt)
+        + " ueber Dich haelt.",78));
+    return 1;
+  }
+  return 0;
+}
+
+private int act_attr_prot_magical(int effekt)
+{
+  if (reg_hook(H_HOOK_DEFEND, H_DATA_MODIFICATOR) == 1)
+  {
+    if (!mappingp(prot_classes)) prot_classes=m_allocate(4);
+    prot_classes[CL_DRAGON] = prot_classes[CL_ELEMENTAL]
+                            = prot_classes[CL_SHADOW]
+                            = prot_classes[CL_ILLUSION]
+                            = effekt/20;
+    tell_object(environment(), break_string(
+        "Du hast das Gefuehl, dass von Hekate beschenkte Wesenheiten Dir "
+        +num2desc_fight(effekt)
+        + " weniger anhaben koennen.",78));
+    return 1;
+  }
+  return 0;
+}
+
+private int act_attr_prot_undead(int effekt)
+{
+  if (reg_hook(H_HOOK_DEFEND, H_DATA_MODIFICATOR) == 1)
+  {
+    // Zombies, Skelette, Ghule...
+    if (!mappingp(prot_classes)) prot_classes=m_allocate(1);
+    prot_classes[CL_UNDEAD] =  effekt/20;
+    tell_object(environment(), break_string(
+        "Du bist ploetzlich zuversichtlich, Angriffen der Kreaturen "
+        "des Hades "
+        + num2desc_fight(effekt)
+        + " besser widerstehen zu koennen.",78));
+    return 1;
+  }
+  return 0;
+}
+
+
+private int act_attr_tragen(int effekt)
+{
+  if ( IS_LEARNER(environment()) )
+    tell_object(environment(), sprintf("effekt: %d\n",effekt));
+  SetProp(P_WEIGHT, QueryProp(P_WEIGHT) - effekt*4);
+  tell_object(environment(),
+      BS("Du fuehlst Dich ploetzlich "+num2desc(effekt)
+        + (effekt>0 ? " entlastet" : " belastet") + "."));
+  return 1;
+}
+
+// Berechnet eine max. Verzoegerung der Wirkung abhaengig von der Wirkung und
+// einer Wirkschwelle. Der rel. Abstand von der Wirkschwelle (relativ zum max.
+// moeglichen Abstand) wird hierbei genutzt. Ausserdem ist die max.
+// Verzoegerung natuerlich die Wirkungsdauer des Trankes.
+// <duration> muss im Trank schon gesetzt sein.
+private int calc_max_delay(int effekt, int wirkschwelle)
+{
+  int abstand = abs(effekt - wirkschwelle);
+  if (!duration) duration = time()+600;
+  if ( IS_LEARNER(environment()) )
+    printf("calc_max_delay: %d\n",((duration-time()) * abstand) /
+      (2000-abs(wirkschwelle)));
+  return ((duration-time()) * abstand) / (2000-abs(wirkschwelle));
+}
+
+//TODO: die Zeitverzoegerung ist nen netter Effekt, aber zeitvergzoegerte
+//Tports sind oefter keine gute Idee -> noch pruefen
+//TODO: Die Zeitverzoegerung bei NO_TPORT_OUT auch nochmal pruefen
+private int act_attr_flee_tport(int effekt)
+{
+  // effekt > 0 teleportiert sofort zu halbwegs sicheren Orten
+  // -750 <= effekt < 0 teleportiert auch sofort zu nicht so dollen Orten
+  // -2000 <= effekt < -750 teleportiert spaeter zu bloeden Orten
+  if (effekt < -750 && effekt >= -2000)
+  {
+    // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem
+    // effekt rufen, der eine instantane Reaktion ausloest.
+    // effekt - 2000 ist ein Hack, damit nicht nochmal verzoegert wird.
+    call_out(#'act_attr_flee_tport,
+        random(calc_max_delay(effekt, -750))+1, effekt - 2000);
+    tell_object(environment(),
+        "Deine Umgebung fuehlt sich nach und nach unwirklicher an.\n");
+    return 1;
+  }
+
+  // Effekte instantan ausloesen.
+
+  // wenn hier kein P_NO_TPORT erlaubt ist, mal gucken, ob wir spaeter noch
+  // zeit haben.
+  if (environment(environment())->QueryProp(P_NO_TPORT)==NO_TPORT_OUT)
+  {
+    tell_object(environment(),
+        BS("Irgendetwas haelt Dich an diesem Ort fest."));
+    int delay = duration - time();
+    // wenn noch genug Restzeit, nochmal versuchen, sonst nix machen.
+    if (delay>10)
+    {
+      // AN/TODO: Kann das nicht auch ziemlich lang sein?
+      call_out(#'act_attr_flee_tport, random(delay), effekt);
+    }
+    return 0;
+  }
+
+  // den Hack von oben rueckgaengig machen, wir brauchen den originalen
+  // Effektwert zurueck.
+  if (effekt < -2000)
+    effekt += 2000;
+
+  string* dests;
+  if ( effekt > 0 )
+  {
+    switch(effekt)
+    {
+      case 0..499:
+        dests = ({"/d/inseln/zesstra/vulkanweg/room/r1",
+                  "/d/fernwest/li/xian/lab2/grab2",
+                  "/d/ebene/miril/schloss/heide11",
+                  "/d/polar/alle/room/weg4_15",
+                  "/d/dschungel/arathorn/tempel/room/t4-5",
+                  "/d/anfaenger/arathorn/minitut/room/huette_"+
+                    environment()->query_real_name(),
+                 });
+        break;
+      case 500..749:
+        dests = ({"/d/ebene/bertram/ebene/wasser8",
+                  "/d/ebene/room/gebuesch2_3",
+                  "/d/polar/alle/room/eiswueste/eiswueste[4,6]",
+                 });
+        if (environment()->QueryProp(P_REAL_RACE)!="Dunkelelf")
+          dests += ({"/d/unterwelt/padreic/kneipe/kneipe"});
+        break;
+      case 750..999:
+        dests = ({"/d/gebirge/silvana/cronoertal/room/tf4",
+                  "/d/inseln/schiffe/floss",
+                  "/d/polar/humni/hexen/room/leuchtkammer",
+                  "/d/polar/gabylon/temong/rooms/anlegestelle",
+                 });
+        break;
+      case 1000..1249:
+        dests = ({"/d/fernwest/shinobi/konfu_quest/room/insel4",
+                  "/d/fernwest/ulo/mura/tokoro/haus4",
+                  "/d/ebene/room/Halle/shalle14",
+                 });
+        break;
+      case 1250..1499:
+        dests = ({"/d/gebirge/silvana/cronoertal/room/tf4",
+                  "/gilden/zauberer",
+                  "/d/gebirge/georg/room/w11"
+                 });
+        break;
+      case 1500..1749:
+        dests = ({"/gilden/bierschuettler",
+                  "/gilden/kaempfer",
+                  "/d/wald/gundur/hobbitdorf/schrein",
+                  "/d/vland/morgoth/room/city/rathalle",
+                 });
+        break;
+      default:
+        dests = ({environment()->QueryProp(P_START_HOME)||
+                    environment()->QueryDefaultHome(),
+                  environment()->QueryDefaultHome(),
+                  "/d/ebene/room/PortVain/po_haf2",
+                  "/d/gebirge/room/he3x3",
+                  "/d/ebene/room/huette",
+                 });
+        break;
+    }
+  }
+  else if ( effekt < 0 )
+  {
+    switch(effekt)
+    {
+      case -499..0:
+        dests = ({"/d/polar/bendeigid/rooms/neskaya/neskay12",
+                  "/players/ketos/gl/room/gl1x1",
+                  "/d/inseln/zesstra/vulkanweg/room/r10",
+                  "/d/vland/morgoth/room/kata/ktanke",
+                  "/d/ebene/zardoz/burg/feuerrau",
+                 });
+        break;
+      case -999..-500:
+        dests = ({"/d/ebene/room/Hoehle/hg6",
+                  "/d/gebirge/silvana/warok/room/l3/wa_3x7",
+                  "/d/vland/morgoth/room/kata/xkat03",
+                  "/d/vland/morgoth/room/kata/kata5",
+                  "/d/wald/yoru/ort/dravi/weg5",
+                  "/d/wald/atamur/room/e83",
+                 });
+        break;
+      case -1499..-1000:
+        dests = ({"/d/polar/bendeigid/rooms/pass/pass_e1",
+                  "/d/ebene/room/gebuesch",
+                  "/d/gebirge/silvana/warok/room/l1/wa_1x6",
+                  "/d/vland/morgoth/room/kata/gkat17",
+                  "/d/wald/atamur/room/e12",
+                 });
+        if (environment()->QueryProp(P_REAL_RACE)=="Dunkelelf")
+          dests += ({"/d/unterwelt/padreic/kneipe/kneipe"});
+        break;
+      case -1749..-1500:
+        dests = ({"/d/dschungel/wurzel/t2",
+                  "/d/vland/morgoth/room/kata/ukat26",
+                  "/d/vland/morgoth/room/kata/h12",
+                  "/d/gebirge/boing/sm/l5/m5x2",
+                 });
+        if (environment()->QueryProp(P_GUILD)=="chaos")
+          dests+=({"/gilden/klerus"});
+        break;
+      default:
+        dests = ({"/d/ebene/rochus/room/sp10",
+                  "/d/ebene/rochus/quest_3player/room/schacht10",
+                 });
+        if ( IS_SEER(environment()) )
+          dests += ({"/d/wald/paracelsus/froom/sch_6e",
+                     "/d/wald/paracelsus/froom/sch_9x",
+                     "/d/wald/paracelsus/froom/sch2_6d",
+                    });
+        break;
+    }
+  }
+  tell_object(environment(),
+      BS("Eine Kraft zerrt an Dir, die Welt verschwimmt..."));
+  if (environment()->move(dests[random(sizeof(dests))],M_TPORT) == MOVE_OK)
+    tell_object(environment(),
+        "Einen Moment spaeter bist Du ganz woanders.\n");
+  else
+    tell_object(environment(),
+        "Aber sonst passiert nichts.\n");
+  return 1;
+}
+
+private int act_attr_change_dimension(int effekt)
+{
+  // nur effekt >= 1000 aendern die Dimension instantan. ;-)
+  if (effekt > 0 && effekt < 1000)
+  {
+    // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem
+    // effekt rufen, der eine instantane Reaktion ausloest.
+    call_out(#'act_attr_change_dimension,
+        random(calc_max_delay(effekt,1000))+1, 1000);
+    tell_object(environment(),BS("Um Dich herum wird alles "
+        "langsam grauer.\n"));
+    return 1;
+  }
+  // nur -600 < effekt < 0 aendern die Dimension instantan ;-)
+  if (effekt < -600)
+  {
+    // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem
+    // effekt rufen, der eine instantane Reaktion ausloest.
+    call_out(#'act_attr_change_dimension,
+        random(calc_max_delay(effekt, -600))+1, -601);
+    tell_object(environment(),BS("Um Dich herum wird alles "
+        "langsam grauer.\n"));
+    return 1;
+  }
+  // Effekte instantan ausloesen.
+  // wenn hier kein Para-Trans erlaubt ist, mal gucken, ob wir spaeter noch
+  // zeit haben.
+  if (environment(environment())->QueryProp(P_NO_PARA_TRANS))
+  {
+    int delay = duration - time();
+    // wenn noch genug Restzeit, nochmal versuchen, sonst nix machen.
+    if (delay>10)
+    {
+      call_out(#'act_attr_change_dimension, random(delay), effekt);
+      tell_object(environment(), BS("Die Welt um Dich wird ein "
+            "wenig grauer."));
+    }
+    return 0;
+  }
+  if ( effekt > 0 )
+  {
+    if ( environment()->QueryProp(P_PARA) > 0 )
+    {
+      environment()->SetProp(P_PARA, 0);
+      tell_object(environment(), BS("Fuer einen kurzen Moment siehst Du nur "
+          "ein graues Wabern um Dich herum, bevor die Welt wieder "
+          "normal aussieht.\n"));
+    }
+    else
+    {
+      tell_object(environment(), BS("Fuer einen kurzen Moment sieht alles um "
+            "Dich herum grau aus."));
+    }
+  }
+  else if ( effekt < 0 )
+  {
+    if ( !environment()->QueryProp(P_PARA) )
+    {
+      environment()->SetProp(P_PARA, 1);
+       tell_object(environment(), BS("Fuer einen kurzen Moment siehst Du nur "
+          "ein graues Wabern um Dich herum, bevor die Welt wieder "
+          "normal aussieht. Aber es bleibt ein ungutes Gefuehl.\n"));
+    }
+    else {
+      tell_object(environment(), BS("Fuer einen kurzen Moment sieht alles um "
+            "Dich herum grau aus."));
+    }
+  }
+  return 1;
+}
+
+private int act_attr_defrog(int effekt)
+{
+  // nur effekt > 1000 entfroscht instantan. ;-)
+  if (effekt > 0 && effekt < 1000)
+  {
+    // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem
+    // effekt rufen, der eine instantane Reaktion ausloest.
+    call_out(#'act_attr_defrog, random(calc_max_delay(effekt,1000))+1, 1000);
+    tell_object(environment(),BS(
+        "Du hoerst ploetzlich lautes Gequake, was langsam immer leiser "
+        "wird.\n"));
+    return 1;
+  }
+  // nur -500 < effekt < 0 froscht instantan ;-)
+  if (effekt < -500)
+  {
+    // Verzoegerung ausrechnen und dies hier per call_out nochmal mit einem
+    // effekt rufen, der eine instantane Reaktion ausloest.
+    call_out(#'act_attr_defrog, random(calc_max_delay(effekt, -500))+1, -501);
+    tell_object(environment(),BS(
+        "Du hoerst ploetzlich ganz leisess Gequake, was langsam immer lauter "
+        "wird.\n"));
+    return 1;
+  }
+
+  // Effekte instantan ausloesen.
+  if ( effekt > 0 )
+  {
+    if ( environment()->QueryProp(P_FROG) )
+    {
+      environment()->SetProp(P_FROG,0);
+      tell_object(PL, "Du fuehlst Dich deutlich weniger gruen.\n");
+    }
+    else
+    {
+      tell_object(environment(), break_string("Die Welt um Dich herum verliert "
+        "ploetzlich alle gruenen Farbtoene, die bald darauf allmaehlich "
+        "zurueckkehren.",78));
+    }
+  }
+  else if ( effekt < 0 ) {
+    if ( !environment()->QueryProp(P_FROG) ) {
+      environment()->SetProp(P_FROG, 1);
+      tell_object(environment(), "Quak!\n");
+    }
+    else {
+      tell_object(environment(), break_string("Deine Sicht verschwimmt, alles wird "
+        "intensiv gruen. Merkwuerdig. Zum Glueck ist das schnell wieder "
+        "vorbei.",78));
+    }
+  }
+  return 1;
+}
+
+private int act_attr_heal_lp(int effekt)
+{
+  if (effekt > 0)
+  {
+    tell_object(environment(),
+        BS("Du fuehlst Dich schlagartig "+num2desc(effekt)
+          + " besser."));
+    environment()->restore_hit_points(effekt/10);
+  }
+  else
+  {
+    tell_object(environment(),
+        BS("Du fuehlst Dich schlagartig "+num2desc(effekt)
+          + " schlechter."));
+    environment()->do_damage(-effekt/10);
+  }
+  return 1;
+}
+
+private int act_attr_heal_sp(int effekt)
+{
+  if (effekt > 0)
+  {
+    tell_object(environment(),
+        BS("Du fuehlst Dich schlagartig "+num2desc(effekt)
+          + " konzentrierter."));
+    environment()->restore_spell_points(effekt/10);
+  }
+  else
+  {
+    tell_object(environment(),
+        BS("Du fuehlst Dich schlagartig "+num2desc(effekt)
+          + " unkonzentrierter."));
+    environment()->reduce_spell_points(-effekt/10);
+  }
+  return 1;
+}
+
+
+private int modify_sa(string sa, int effekt)
+{
+  string msg;
+  switch (sa)
+  {
+    case SA_SPEED:
+      msg = "Du wirst gerade " + num2desc(effekt)
+            +(effekt>0 ? " schneller." : " langsamer.");
+      effekt = (effekt * 30) / 2000;
+      break;
+    case SA_DURATION:
+      msg = "Du wirst gerade " + num2desc(effekt)
+            +(effekt>0 ? " ausdauernder."
+                       : " weniger ausdauernd.");
+      effekt = (effekt * 25) / 2000;
+      break;
+    case SA_SPELL_PENETRATION:
+      msg = "Deine geistige Durchsetzungskraft hat sich gerade "
+            + num2desc(effekt)
+            +(effekt>0 ? " verbessert." : " verschlechtert.");
+      effekt = (effekt * 30) / 2000;
+      break;
+  }
+  if (environment()->ModifySkillAttribute(sa, effekt, duration-time()) ==
+        SA_MOD_OK)
+  {
+    tell_object(environment(),BS(msg));
+    return 1;
+  }
+  return 0;
+}
+
+private int act_attr_change_sa_speed(int effekt)
+{
+  return modify_sa(SA_SPEED, effekt);
+}
+private int act_attr_change_sa_duration(int effekt)
+{
+  return modify_sa(SA_DURATION, effekt);
+}
+private int act_attr_change_sa_spell_penetration(int effekt)
+{
+  return modify_sa(SA_SPELL_PENETRATION, effekt);
+}
+
+
+// Spieler MUSS das environment() sein!
+private void effekt()
+{
+  // Als erstes die Wirkungsdauer verwursten, einige Effekte brauchen
+  // <duration>.
+  // Wann laufen die Effekte denn spaetenstens ab?
+  duration = time() + data[T_EFFECT_DURATION];
+  call_out(#'terminate_effects, data[T_EFFECT_DURATION]);
+
+  // nur echte wirkungen beruecksichtigen, keine "Metadaten"
+  mapping effects = data & T_KRAUT_EFFECTS;
+  // fuer Spieler wird der Paratrans nicht stattfinden.
+  if (!IS_SEER(environment()))
+    m_delete(data, T_CHANGE_DIMENSION);
+
+  // Waehrend der Wirkung ist dieses Objekt schonmal unsichtbar.
+  SetProp(P_INVIS, 1);
+  // Gewicht nullen, bevor die Wirkungen aktiviert werden, da die
+  // Wirkung von T_CARRY ansonsten wieder deaktiviert wuerde. 
+  SetProp(P_WEIGHT, 0);
+  // neue, leere Flasche ins Inventar des Spielers bewegen.
+  clone_object(TRANKITEM)->move(environment(),M_NOCHECK|M_SILENT);
+
+  // auftrennen in positive und negative Effekte. Keys mit Wert 0 werden
+  // ignoriert.
+  mapping peff = filter(effects, function int (string k, int val)
+                        {return val > 0;});
+  mapping neff = filter(effects, function int (string k, int val)
+                        {return val < 0;});
+  // erst die positiven, dann die negativen Wirkungen aktivieren
+  // fuer jede Wirkung wird eine lfun act_<trankattribut>() gerufen, z.B.
+  // act_attr_tragen() (-> act_T_CARRY() )
+  mapping notactivated =
+          filter(peff, function int (string k, int val)
+              {return !funcall(symbol_function("act_"+k,this_object()), val);})
+         +
+          filter(neff, function int (string k, int val)
+              {return !funcall(symbol_function("act_"+k,this_object()), val);});
+  // Meldungen ausgeben ueber nicht aktivierte Wirkungen?
+  // TODO
+}
+
+static int cmd_trinken(string str, mixed *param)
+{
+  if (environment(this_object()) != PL)
+  {
+    write("Aus auf dem Boden liegende Flaschen trinkt es sich so "
+       "schlecht!\n");
+  }
+  else if (data==0)
+  {
+    write("Die Flasche ist leer, Du muesstest erst etwas hineinfuellen.\n");
+  }
+  else if (time()>expiry) 
+  {
+    write(BS("Irgendwie passiert nichts. Hattest Du vielleicht doch nur "
+      "klares Wasser abgefuellt?"));
+    data=0;
+  }
+  else {
+    // Die Sperrzeit muss hier separat berechnet werden, weil eine
+    // Anpassung der Wirkungsdauer im Krautmaster dazu fuehren wuerde,
+    // dass es keine instantan wirkenden Traenke mehr gaebe.
+    int blocktime = max(60+random(60), data[T_EFFECT_DURATION]);
+    if(environment()->check_and_update_timed_key(blocktime, DRINK_POTION)==-1)
+    {
+      write(BS("Du oeffnest die Flasche und trinkst ihren Inhalt aus."));
+      say(BS(PL->Name(WER, 0)+" oeffnet eine Flasche und trinkt sie in einem "
+        "Schluck aus."));
+      effekt();
+      // TODO: reicht das hier aus, oder muss das noch an anderen Stellen 
+      // eingebaut werden?
+      RemoveId(({"trank","kraeutertrank"}));
+    }
+    else {
+      tell_object(environment(), BS(
+        "Der letzte Trank wirkt noch ein wenig nach. Du kannst Deinem "
+        "Magen nicht so bald schon den naechsten zumuten."));
+    }
+  }
+  return 1;
+}
+
+public nomask int Fill(object *plants)
+{
+  if (!pointerp(plants)) return -1;
+  if (data) return -2;  // schon voll
+  data = PLANTMASTER->make_potion(plants);
+  AddId(({"trank","kraeutertrank"}));
+  if (mappingp(data))
+  {
+    // Pflanzen zerstoert der Master, wenns geklappt hat.
+    expiry=time()+data[T_EXPIRE];
+    SetProp(P_WEIGHT, 300);
+    return 1;
+  }
+  return -3;
+}
+
+void NotifyPlayerDeath(object vic, object killer, int exp)
+{
+  call_out("remove",1);
+}
diff --git a/std/kraeuterstore.c b/std/kraeuterstore.c
new file mode 100644
index 0000000..996a1d6
--- /dev/null
+++ b/std/kraeuterstore.c
@@ -0,0 +1,35 @@
+// (c) 2003 by Padreic (padreic@mg.mud.de)
+// Stdstore fuer Kraeuterlaeden!
+
+inherit "/std/store";
+
+#include <properties.h>
+#include <bank.h>
+
+void create()
+{
+   if (object_name(this_object()) == __FILE__[0..<3])
+   {
+     set_next_reset(-1);
+     return;
+   }
+   ::create();
+   SetProp(P_MIN_STOCK, 50);
+   // in jedem Reset verschwinden nur 2% aller Objekte
+   SetProp(P_STORE_CONSUME, 2);
+}
+
+protected void create_super()
+{
+  set_next_reset(-1);
+}
+
+void reset()
+{
+   ::reset();
+   // damit die Kraeuter im Laden nicht schimmeln, ggf. trocknen...
+   object *obs = filter( all_inventory(), function int (object ob) {
+                   return ob->TimeToLive()<3600;} );
+   obs->DryPlant(95);
+}
+
diff --git a/std/lightsource.c b/std/lightsource.c
new file mode 100644
index 0000000..e4b2245
--- /dev/null
+++ b/std/lightsource.c
@@ -0,0 +1,313 @@
+// MorgenGrauen MUDlib
+//
+// lightsource.c -- standard light source
+//
+// $Id: lightsource.c 7806 2011-07-11 20:28:17Z Zesstra $
+
+// std/lightsource.c
+//
+// Original von Miril April '92
+// Neufassung von Sir Dezember '92 ( unter Boings Aegide :)
+// Version : 1.0
+//
+// Bugfixes von Sir Juni '93
+// Version : 1.2
+//
+// Wichtige Funktionen, die man in create() aufrufen sollte :
+//
+// Setzen der Brennstoffmenge :  
+// SetProp( P_FUEL, int )       int gibt die Brennzeit in Sekunden an
+//
+// Setzen der Beschreibung wenn Objekt angezuendet( default : "brennend" ) :
+// SetProp( P_LIGHTDESC, string )
+//
+// Legt fest, ob ein Leuchtobjekt nach seinem Abbrennen destructed wird
+// SetProp( P_DO_DESTRUCT, int )
+//
+// Legt fest, wie hell die Fackel leuchtet.
+// SetProp( P_LIGHT, int )
+//
+// Ansonsten sind die Standardfuktionen wie z.B. SetProp( P_GENDER, MALE ) 
+// aufzurufen
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "std/thing" ;
+
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+
+#define TRUE  1
+#define FALSE 0
+#define bool  int
+
+#define DFLT_FUEL       20 // 20 Sekunden
+#define DFLT_LIGHTDESC  "brennend"
+#define DFLT_LIGHT      2
+
+// So ergibt sich die Moeglichkeit sie wieder auzufuellen
+#define CALL_OUT_TIME   100
+
+#undef DEBUG
+
+// TODO: Private waer langfristig nett...
+nosave int  fuel, max_fuel;
+nosave bool lighted ;
+nosave int  call_time ;
+
+void create()
+{
+  thing::create() ;
+  
+  SetProp( P_NAME, "Lichtquelle" ) ;
+  SetProp( P_SHORT, "Eine Lichtquelle" ) ;
+  SetProp( P_LONG, "Du siehst nichts besonderes.\n" ) ;
+  SetProp( P_GENDER, FEMALE ) ;
+  SetProp( P_FUEL, DFLT_FUEL ) ;
+  SetProp( P_LIGHTDESC, DFLT_LIGHTDESC ) ;
+  SetProp( P_LIGHT, DFLT_LIGHT );
+  SetProp( P_DO_DESTRUCT, TRUE ) ;
+  SetProp( P_VALUE, 5 ) ;
+  
+  AddId( ({ "lichtquelle", "\nlichtquelle" }) ) ;
+  AddCmd( ({ "zuende", "zuend" }), "light" );
+  AddCmd( ({ "loesche", "loesch" }), "extinguish" );
+}
+
+void test_remove()
+{  if (QueryProp(P_DO_DESTRUCT)) remove();  }
+
+/*
+ * Lichtquelle anzuenden
+ */
+bool light(string str)
+{
+  string tmp ;
+  object env;
+
+  _notify_fail( "Zuende was an?\n" ) ;
+
+  if( (!str) || (!sscanf( str, "%s an", tmp )) || (!id( tmp )) )
+    return FALSE ;
+  
+  if( environment() != PL ) // Player hat es nicht
+  {
+    _notify_fail( "Erstmal musst Du " + name( WEN, 0 ) + " haben.\n" ) ;
+    return FALSE ;
+  }
+  
+  if( lighted )
+  {
+    _notify_fail( CAP( name( WER, 1 ) ) + " brennt schon.\n" ) ;
+    return FALSE ;
+  }
+
+  if( fuel <= 0 )
+  {
+    _notify_fail( CAP( name( WER, 1 ) ) + " ist abgebrannt.\n" ) ;
+    test_remove() ;
+    return FALSE ;
+  }
+
+  lighted = TRUE ;
+  env=this_object();
+  while (objectp(env=environment(env)))
+      // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
+      // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
+      // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
+      // da um jedes bisschen Rechenzeit geht.
+      // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
+      //
+      // Tiamak
+      env->_set_last_content_change();
+  call_time = (fuel < CALL_OUT_TIME)? fuel : CALL_OUT_TIME ;
+  call_out( "out_of_fuel", call_time ) ;
+  if( (int)PL->QueryProp(P_PLAYER_LIGHT) == 1 )
+    write( "Du kannst wieder etwas sehen.\n" ) ;
+  else write( "Ok.\n" ) ;
+  say((string)PL->Name(WER)
+      + " zuendet " + name( WEN, 0 ) + " an.\n" ) ;
+
+  return TRUE ;
+}
+
+bool extinguish(string str)
+{
+  int ti;
+  object env;
+
+  _notify_fail( "Welche Lichtquelle moechtest Du loeschen?\n" ) ;
+
+  if( (!str) || (!id( str )) )
+    return FALSE ;
+  
+  if( !lighted )
+  {
+    _notify_fail( CAP( name( WER, 1 ) ) + " brennt gar nicht.\n" ) ;
+    return FALSE ;
+  }
+
+  if( environment() != PL )
+  {
+    _notify_fail( "Erstmal musst Du " + name( WEN, 0 ) + " haben.\n" ) ;
+    return FALSE ;
+  }
+
+  if( ( ti = remove_call_out( "out_of_fuel" ) ) == -1 )
+    ti = 0 ;
+
+  fuel -= (call_time - ti) ;
+  lighted = FALSE ;
+  env=this_object();
+  while (objectp(env=environment(env)))
+      // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
+      // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
+      // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
+      // da um jedes bisschen Rechenzeit geht.
+      // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
+      //
+      // Tiamak
+      env->_set_last_content_change();
+  if ( this_player()->QueryProp(P_PLAYER_LIGHT) == 0 )
+  {
+    write( "Es wird dunkel.\n" ) ;
+    say((string)PL->Name(WER) 
+	+ " macht das Licht aus.\n" ) ;
+  }
+  else
+  {
+    write( "Ok.\n" ) ;
+    say((string)PL->Name(WER)
+	+ " loescht " + name( WEN, 0 ) + " aus.\n" ) ;
+  }
+
+  if( fuel <= 0 ) test_remove() ;
+  return TRUE ;
+}
+
+bool unlight()
+{
+  int ti;
+  object env;
+
+  if( !lighted )
+    return FALSE ;
+
+  if( ( ti = remove_call_out( "out_of_fuel" ) ) == -1 )
+    ti = 0 ;
+
+  fuel -= (call_time - ti) ;
+  lighted = FALSE ;
+  env=this_object();
+  while (objectp(env=environment(env)))
+      // Kommentar siehe oben ;^)
+      env->_set_last_content_change();
+  if( fuel <= 0 ) test_remove() ;
+  return TRUE ;
+}
+
+void out_of_fuel()
+{
+  int i;
+  object *inv, env;
+  
+  fuel -= call_time ;
+
+  if (fuel>0) {
+    call_out( "out_of_fuel", call_time ) ;
+    return ;
+  }
+  lighted=FALSE;
+  env=this_object();
+  while (objectp(env=environment(env)))
+      // Immer noch nicht wirklich sauber. Aber Begruendung siehe oben.
+      env->_set_last_content_change();
+
+  if (environment())
+  {
+    if ( living(environment()) && environment(environment()) )
+    {
+      inv=(users() & all_inventory(environment(environment())))-({ environment() });
+      for (i=sizeof(inv)-1; i>=0; i--) 
+        if (inv[i]->QueryProp(P_PLAYER_LIGHT)<=0)
+          tell_object(inv[i], "Es wird dunkel als " + environment()->name(WESSEN) +
+            " " + QueryProp(P_NAME) + " erlischt.\n" ) ;
+        else tell_object(inv[i], CAP( name( WER, 0 ) ) + " erlischt.\n" ) ;
+      if (environment()->QueryProp(P_PLAYER_LIGHT)<=0)
+        tell_object(environment(), 
+          CAP( name( WER, 1 ) ) + " erlischt und es wird dunkel.\n" ) ;
+      else tell_object(environment(), CAP( name( WER, 1 ) ) + " erlischt.\n" ) ;    
+    }
+    else
+    {
+      inv=(users() & all_inventory(environment()));
+      for (i=sizeof(inv)-1; i>=0; i--) 
+        if (inv[i]->QueryProp(P_PLAYER_LIGHT)<=0)
+          tell_object(inv[i], "Es wird dunkel als " + name(WER,1)
+          + " erlischt.\n" ) ;
+        else tell_object(inv[i], CAP( name( WER, 0 ) ) + " erlischt.\n" ) ;
+    }
+  }  
+  test_remove() ;
+}
+
+// Brennstoff nachfuellen
+void AddFuel(int f)
+{  fuel += f ;  }
+
+// Property - Funktionen
+
+static void _set_lighted(bool l)
+{  lighted = l ;  }
+
+bool _query_lighted()
+{  return lighted ;  }
+
+static void _set_fuel(int f)
+{
+  if (f>max_fuel) max_fuel=f;
+  fuel = f ;
+}
+
+static int _query_fuel()
+{
+  int tmp;
+
+  if ((tmp=find_call_out("out_of_fuel"))>=0)
+    return fuel-call_time+tmp;
+  else return fuel;
+}
+
+static string _query_lightdesc()
+{
+  string *tmp;
+  int n,i;
+  
+  tmp=Query(P_LIGHTDESC);
+  if (!pointerp(tmp)) return (string)tmp;
+  n=sizeof(tmp);
+  i=n*_query_fuel()/max_fuel;
+  if (i>=n) i=n-1;
+  return tmp[i];
+}
+
+static int _query_light()
+{   return (lighted ? Query(P_LIGHT) : 0);  }
+
+/*
+ * short() ueberladen
+ */
+string short()
+{
+  string s;
+  string desc;
+
+  if(!(s=QueryProp(P_SHORT)))
+    return 0;
+  return s + ( (lighted)? (" ("+QueryProp( P_LIGHTDESC ) +").\n") : ".\n" );
+}
diff --git a/std/living/attributes.c b/std/living/attributes.c
new file mode 100644
index 0000000..90027a4
--- /dev/null
+++ b/std/living/attributes.c
@@ -0,0 +1,671 @@
+// MorgenGrauen MUDlib
+//
+// living/attributes.c -- attributes for living objects
+//
+// $Id: attributes.c 8375 2013-02-12 21:52:58Z Zesstra $
+
+#include <sys_debug.h>
+
+// attribute handling
+//
+//   filter_ldfied: str, dex, con, int
+//
+// In den Attributen und Abilites werden Rassenspzifische und erworbene
+// Faehigkeiten (mit autoload-Eigenschaft) abgepseichert.
+//
+// Funktionen:
+//   SetAttribute( attr, val ) (Erzeuge und) setze das Attribut auf val.
+//   QueryAttribute( attr )    Gebe den Wert des Attributes zurueck.
+//
+//   Wenn ein Objekt eine Funktion _filterattr_<name> beitzt, wird beim Setzen
+//   des Attributes <name>, der vorgeschlagene Wert uebergeben und der von
+//   dieser Funktion zurueckgegebene gesetzt. (fuer ueberpruefungszwecke)
+//   Gleiches gilt fuer _filterabil_<name>.
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <attributes.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+
+#include <config.h>
+#include <properties.h>
+
+mapping attributes; // Dies sind die mit ZTs veraenderbaren Attribute
+mapping attributes_modifier; // Modifier sollen gespeichert werden
+nosave mixed* attributes_timed_mods; //P_TIMED_ATTR_MOD
+nosave mapping attributes_offsets; // Offsets NICHT speichern!
+nosave mapping used_attributes_offsets; // Zur Beschleunigung der Berechnung
+nosave object* all_modifiers; // objekte mit P_X/M_ATTR_MOD, P_X/M_HEALTH_MOD
+nosave object* invalid_modifiers; // objekte welche das limit ueberschreiten
+nosave int cumulative_mod; // bilanz der P_X/M_ATTR_MOD
+nosave int hp_off;
+nosave int sp_off;
+
+
+nomask public int SetTimedAttrModifier(string key, mapping modifier, 
+    int outdated, object dependent, mixed notify) {
+  if(!key || key=="" || !modifier || (outdated>0 && outdated<time()) || 
+      (notify && !stringp(notify) && !objectp(notify)) ) {
+    return TATTR_INVALID_ARGS;
+  }
+  
+  
+  if(member(attributes_timed_mods[TATTR_ENTRIES],key)) {
+    // change entry
+    attributes_timed_mods[TATTR_ENTRIES][key,TATTR_MOD]=modifier;
+    attributes_timed_mods[TATTR_ENTRIES][key,TATTR_OUTDATED]=outdated;
+    attributes_timed_mods[TATTR_ENTRIES][key,TATTR_DEPENDENT]=dependent;
+    attributes_timed_mods[TATTR_ENTRIES][key,TATTR_NOTIFY]=notify;
+  }
+  else {
+    // add entry
+    attributes_timed_mods[TATTR_ENTRIES]+=([key:modifier;outdated;dependent;notify]);
+  }
+  
+  // add outdate
+  if(outdated>0 && member(attributes_timed_mods[TATTR_OUTDATE],outdated)==-1)
+  {
+    attributes_timed_mods[TATTR_OUTDATE]+=({outdated});
+    attributes_timed_mods[TATTR_OUTDATE]=
+      sort_array(attributes_timed_mods[TATTR_OUTDATE],#'<);
+  }
+  
+  // add dependent
+  if(objectp(dependent))
+  {
+  	if(member(attributes_timed_mods[TATTR_DEPENDENTS],key))
+  	{
+	  attributes_timed_mods[TATTR_DEPENDENTS][key]=dependent;
+  	}
+  	else
+  	{
+          attributes_timed_mods[TATTR_DEPENDENTS]+=([key:dependent]);
+  	}
+  }
+  else
+  {
+  	// remove previously set dependent
+  	if(member(attributes_timed_mods[TATTR_DEPENDENTS],key))
+  	{
+	  attributes_timed_mods[TATTR_DEPENDENTS]-=([key]);
+  	}
+  }
+  
+  UpdateAttributes();
+
+  return TATTR_OK;
+}
+
+nomask public mapping QueryTimedAttrModifier(string key)
+{
+  int outdated;
+  object dependent;
+  mixed notify;
+  mapping mod;
+  
+  if(!key || key=="")
+  {
+    return ([]);
+  }
+
+  if(!member(attributes_timed_mods[TATTR_ENTRIES],key))
+  { 
+    return ([]);
+  }
+
+  mod=deep_copy(attributes_timed_mods[TATTR_ENTRIES][key,TATTR_MOD]);
+  outdated=attributes_timed_mods[TATTR_ENTRIES][key,TATTR_OUTDATED];
+  dependent=attributes_timed_mods[TATTR_ENTRIES][key,TATTR_DEPENDENT];
+  notify=attributes_timed_mods[TATTR_ENTRIES][key,TATTR_NOTIFY];
+  
+  return ([key:mod;outdated;dependent;notify ]);
+}
+
+nomask public int DeleteTimedAttrModifier(string key)
+{
+  if(!key || key=="")
+  {
+    return TATTR_INVALID_ARGS;
+  }
+
+  if(!member(attributes_timed_mods[TATTR_ENTRIES],key))
+  { 
+    return TATTR_NO_SUCH_MODIFIER;
+  }
+  
+  attributes_timed_mods[TATTR_DEPENDENTS]-=([key]);
+  attributes_timed_mods[TATTR_ENTRIES]-=([key]);
+  UpdateAttributes();
+  
+  return TATTR_OK;
+}
+
+nomask protected void attribute_hb()
+{
+  int now,i,k,update,outdated;
+  string* keys;
+  mapping tonotify;
+	
+  // initialize
+  now=time();
+  tonotify=([]);
+
+  keys=m_indices(attributes_timed_mods[TATTR_ENTRIES]);
+  
+  // delete outdated 
+  for(i=sizeof(attributes_timed_mods[TATTR_OUTDATE])-1;i>=0;i--)
+  {
+  	outdated=attributes_timed_mods[TATTR_OUTDATE][i];
+  	if(outdated>now)
+  	{
+  	  break;
+  	}
+  	
+  	for(k=sizeof(keys)-1;k>=0;k--)
+  	{
+  	  if(attributes_timed_mods[TATTR_ENTRIES][keys[k],TATTR_OUTDATED]==outdated)
+  	  {
+  	    // bei fehlendem notifier wurde das zum verhaengnis
+  	    // dank an gloinson
+  	    /*
+  	    if(objectp(attributes_timed_mods[TATTR_ENTRIES][keys[k],TATTR_NOTIFY]))
+  	    {
+  	    */
+  	    tonotify+=([keys[k]:attributes_timed_mods[TATTR_ENTRIES][keys[k],TATTR_NOTIFY]]);
+  	    //}
+  	    
+	    attributes_timed_mods[TATTR_DEPENDENTS]-=([keys[k]]);
+ 	    attributes_timed_mods[TATTR_ENTRIES]-=([keys[k]]);
+  	    keys-=({keys[k]});
+  	  }
+  	}
+  	
+  	attributes_timed_mods[TATTR_OUTDATE]-=({outdated});
+  }
+  
+  // delete depending
+  keys=m_indices(attributes_timed_mods[TATTR_DEPENDENTS]);
+  for(i=sizeof(keys)-1;i>=0;i--)
+  {
+  	if(!objectp(attributes_timed_mods[TATTR_DEPENDENTS][keys[i]]))
+  	{
+  	    // siehe oben
+  	    /*
+  	    if(objectp(attributes_timed_mods[TATTR_ENTRIES][keys[i],TATTR_NOTIFY]))
+  	    {
+  	    */
+  	    tonotify+=([keys[i]:attributes_timed_mods[TATTR_ENTRIES][keys[i],TATTR_NOTIFY]]);
+  	    //}
+
+	    attributes_timed_mods[TATTR_DEPENDENTS]-=([keys[i]]);
+ 	    attributes_timed_mods[TATTR_ENTRIES]-=([keys[i]]);
+  	    keys-=({keys[i]});
+  	}
+  }
+  
+  
+  // update
+  if(sizeof(tonotify))
+  {
+    UpdateAttributes();
+    call_out(#'notifyExpiredModifiers,0,tonotify);
+  }
+}
+
+nomask protected void notifyExpiredModifiers(mapping nots)
+{
+  int i;
+  string* keys;
+  while(remove_call_out(#'notifyExpiredModifiers)!=-1);
+  
+  if(!nots)
+  {
+    return;
+  }
+  
+  keys=m_indices(nots);
+  for(i=sizeof(nots)-1;i>=0;i--)
+  {
+  	if(nots[keys[i]] && 
+	    ( objectp(nots[keys[i]])||stringp(nots[keys[i]]) ) )
+  	{
+  	   call_other(nots[keys[i]],"NotifyTimedAttrModExpired",keys[i]);
+  	}
+  }
+}
+
+
+// invalide modifier benachrichtigen
+nomask protected void notifyInvalidModifiers() {
+
+  while(remove_call_out(#'notifyInvalidModifiers)!=-1);
+  
+  if(!invalid_modifiers) {
+    invalid_modifiers=({});
+  }
+  
+  call_other(invalid_modifiers,"NotifyXMAttrModLimitViolation");
+
+}
+
+
+// welche Modifier / modif. Objekte wirken nun?
+protected nomask void calculate_valid_modifiers() {
+  closure qp;
+  mapping res;	
+  string key;	
+  int wert;
+  // Unterscheidung Bonus <-> Malus, weil der Malus voll eingehen soll	
+  int hp_malus, sp_malus;
+ 
+  used_attributes_offsets=([]);
+  cumulative_mod=0;
+  hp_off=sp_off=0;
+  invalid_modifiers=({});
+
+  // rassenspezifische boni P_ATTRIBUTES_OFFSETS  
+  if ( mappingp(attributes_offsets) )
+    used_attributes_offsets+=attributes_offsets;
+
+  if (!pointerp(all_modifiers) || !sizeof(all_modifiers)) {
+    // in diesem Fall koennen wir hier direkt mit dieser Funktion Schluss
+    // machen. ;-)
+    return;
+  }
+  else
+    all_modifiers-=({0}); //zerstoerte Objekte rauswerfen.
+    
+  // einmal ueber alle modifizierenden Objekt iterieren und aufaddieren
+  foreach(object ob: all_modifiers) {
+    qp = symbol_function("QueryProp",ob);
+ 
+    if (!objectp(ob) || environment(ob)!=this_object()) {
+      all_modifiers-=({ob});
+      continue;
+    }
+ 		
+    // ext. Attribut-Modifier
+    if ( mappingp(res=funcall(qp,P_X_ATTR_MOD)) ) {		
+      foreach(key, wert: res) {
+        cumulative_mod+=wert;
+      }
+    }
+
+    // magic Attribut-Modifier
+    if ( mappingp(res=funcall(qp,P_M_ATTR_MOD))
+        && ( funcall(qp,P_WORN)
+            || funcall(qp,P_WIELDED) ) ) {		
+      foreach(key, wert: res) {
+        cumulative_mod+=wert;
+      }
+    }
+    // Magic Health-Modifier 		
+    if ( mappingp(res=funcall(qp,P_M_HEALTH_MOD))
+        && ( funcall(qp,P_WORN)
+            || funcall(qp,P_WIELDED) ) ) {
+      if (res[P_HP] < 0)
+        hp_malus += res[P_HP];
+      else
+        hp_off+=res[P_HP];
+
+      if (res[P_SP] < 0)
+        sp_malus += res[P_SP];
+      else
+        sp_off+=res[P_SP];
+      }
+	      
+    // external Health Modifier
+    if ( mappingp(res=funcall(qp,P_X_HEALTH_MOD)) ) {
+      if (res[P_HP] < 0)
+        hp_malus += res[P_HP];
+      else
+        hp_off+=res[P_HP];
+
+      if (res[P_SP] < 0)
+        sp_malus += res[P_SP];
+      else
+        sp_off+=res[P_SP];	    
+    }	
+  
+  } // Ende 1. foreach()
+  
+ 
+  // und nochmal, soviele Objekt wieder rausschmeissen, bis das Limit wieder
+  // unterschritten wird. (Verbesserungsvorschlaege erwuenscht.) :-(
+  foreach(object ob: all_modifiers) {
+
+    qp = symbol_function("QueryProp",ob);
+ 
+    if ( mappingp(res=funcall(qp,P_X_ATTR_MOD)) ) {
+      if(cumulative_mod>CUMULATIVE_ATTR_LIMIT) { 
+        invalid_modifiers+=({ob});	  
+	foreach(key, wert: res) {
+          cumulative_mod-=wert;
+        }
+      }
+      else {
+        add_offsets(res);
+      }
+    }
+
+    if ( mappingp(res=funcall(qp,P_M_ATTR_MOD))
+        && ( funcall(qp,P_WORN)
+            || funcall(qp,P_WIELDED) ) ) {
+      if(cumulative_mod>CUMULATIVE_ATTR_LIMIT) {     
+        if(member(invalid_modifiers,ob)==-1) {
+          invalid_modifiers+=({ob});
+        }			
+	foreach(key, wert: res) {
+          cumulative_mod-=wert;
+        }
+      }
+      else {
+        add_offsets(res);
+      }
+    }
+  }
+
+  // HEALTH_MOD werden durch eine Formel 'entschaerft', damit man nur schwer
+  // das Maximum von 150 erreichen kann. (Formel von Humni, beschlossen auf 3.
+  // EM-Treffen)
+  // Mali gehen aber voll ein
+  hp_off = (int)(150 - (150*150.0/(150 + hp_off))) + hp_malus;
+  sp_off = (int)(150 - (150*150.0/(150 + sp_off))) + sp_malus;
+
+  /* alte Version
+    hp_off += hp_malus;
+    sp_off += sp_malus;
+  */
+ 
+  // notify invalid modifiers
+  if(sizeof(invalid_modifiers)>0) {
+    call_out(#'notifyInvalidModifiers,0);
+  }
+}
+
+// abmelden eines modifiers 
+nomask public void deregister_modifier(object modifier)
+{ 
+  if (!pointerp(all_modifiers)) {
+    return;
+  }
+
+  // valid object?
+  if (!objectp(modifier) || member(all_modifiers,modifier)==-1) {
+    return;
+  }
+  
+  all_modifiers-=({modifier});
+  if (invalid_modifiers) {
+    invalid_modifiers-=({modifier});
+  }
+}
+
+// anmelden eines modifiers 
+nomask public void register_modifier(object modifier) { 
+  closure qp;
+  
+  if (!pointerp(all_modifiers)) {
+    all_modifiers=({});
+  }
+
+  // valid object?
+  if (!objectp(modifier) || environment(modifier)!=this_object() || 
+      member(all_modifiers,modifier)!=-1) {
+    return;
+  }
+  
+  qp = symbol_function("QueryProp",modifier);
+  // modifier after all? Die P_M_* muessen getragen/gezueckt sein.
+  if(mappingp(funcall(qp,P_X_ATTR_MOD)) ||
+     mappingp(funcall(qp,P_X_HEALTH_MOD)) ||
+    ((mappingp(funcall(qp,P_M_ATTR_MOD)) ||
+       mappingp(funcall(qp,P_M_HEALTH_MOD))) &&
+      (funcall(qp,P_WORN) || funcall(qp,P_WIELDED)) ) ) {
+    all_modifiers+=({modifier});
+  }
+}
+
+protected void add_offsets(mapping arr) {
+  mixed *ind;
+  int i;
+  
+  if ( !mappingp(arr) )
+    return;
+
+  foreach(string key, int wert: arr) {
+    used_attributes_offsets[key]+=wert;
+  }
+}
+
+public void UpdateAttributes() {
+  mixed   *ind;
+  int     i;
+  
+  // alle gueltigen Modifier ermitteln aus Objekten im Inventar.
+  calculate_valid_modifiers();
+
+  // persistente modifier drauf (frosch zb)
+  // aus P_ATTRIBUTES_MODIFIERS
+  if ( mappingp(attributes_modifier) ) {
+    foreach(mixed key, mapping wert: attributes_modifier) {
+      add_offsets(wert); // Modifier addieren...		
+    }
+  }
+  // timed modifier drauf aus P_TIMED_ATTR_MOD
+  ind=m_indices(attributes_timed_mods[TATTR_ENTRIES]);
+  for ( i=sizeof(ind)-1 ; i>=0 ; i-- ) {
+    // Modifier addieren...
+    add_offsets(attributes_timed_mods[TATTR_ENTRIES][ind[i],0]);
+  }
+  // Bei Monstern werden die HP/SP ueblicherweise selbst gesetzt
+  if ( !query_once_interactive(this_object()))
+    return;
+
+  SetProp(P_MAX_HP, QueryAttribute(A_CON)*8+42+hp_off);
+  SetProp(P_MAX_SP, QueryAttribute(A_INT)*8+42+sp_off);
+  
+  if(QueryProp(P_HP)>QueryProp(P_MAX_HP)) {
+    SetProp(P_HP,QueryProp(P_MAX_HP));
+  }
+  
+  if(QueryProp(P_SP)>QueryProp(P_MAX_SP)) {
+    SetProp(P_SP,QueryProp(P_MAX_SP));
+  }
+
+  GMCP_Char( ([ A_INT: QueryAttribute(A_INT),
+                A_DEX: QueryAttribute(A_DEX),
+                A_STR: QueryAttribute(A_STR),
+                A_CON: QueryAttribute(A_CON) ]) );
+}
+
+
+protected void create() {
+  hp_off=sp_off=0;
+  used_attributes_offsets=([]);
+  all_modifiers=({});
+  invalid_modifiers=({});
+  cumulative_mod=0;
+  
+  Set(P_ATTRIBUTES_MODIFIER, attributes_modifier=([])); // nicht geschuetzt
+  Set(P_ATTRIBUTES_OFFSETS, attributes_offsets=([]));
+  Set(P_ATTRIBUTES_OFFSETS, PROTECTED, F_MODE);
+  Set(P_ATTRIBUTES, attributes=([]));
+  Set(P_ATTRIBUTES, PROTECTED, F_MODE);
+
+  Set(P_TIMED_ATTR_MOD, attributes_timed_mods=({ ({}),([]),([]) }));
+  Set(P_TIMED_ATTR_MOD, NOSETMETHOD|SECURED, F_MODE_AS);
+}
+
+static mixed _query_timed_attr_mod() {
+  mixed ret;
+  return Set(P_TIMED_ATTR_MOD,	    
+      ({attributes_timed_mods[0],
+	deep_copy(attributes_timed_mods[1]),
+	deep_copy(attributes_timed_mods[2])}));
+}
+
+static mapping _set_attributes(mapping arr) 
+{
+  Set(P_ATTRIBUTES, attributes=arr);
+  UpdateAttributes();
+  return arr;
+}
+
+static mapping _query_attributes() 
+{
+  return deep_copy(Set(P_ATTRIBUTES, attributes)); 
+}
+
+static mapping _set_attributes_offsets(mapping arr) 
+{
+  Set(P_ATTRIBUTES_OFFSETS, attributes_offsets=arr);
+  UpdateAttributes();
+  return attributes_offsets;
+}
+
+static mapping _query_attributes_offsets() 
+{ 
+  return deep_copy(Set(P_ATTRIBUTES_OFFSETS, attributes_offsets));
+}
+
+static mixed _set_attributes_modifier(mixed arr) 
+{ string fn;
+  mixed pre;
+  mapping map_ldfied;
+  
+  if ( pointerp(arr) && (sizeof(arr)>=2) )
+  {
+    pre=arr[0];
+    map_ldfied=arr[1];
+  }
+  else 
+  {
+    pre=previous_object();
+    map_ldfied=arr;
+  }
+
+  if ( objectp(pre) )
+    fn=old_explode(object_name(pre),"#")[0];
+  else
+    fn=pre;
+
+  if ( !stringp(fn) )
+    return 0;
+
+  // wenn Modifier kein mapping oder ein leeres Mapping: loeschen
+  if ( !mappingp(map_ldfied) || !sizeof(map_ldfied))
+    m_delete(attributes_modifier,fn);
+  else
+    attributes_modifier[fn]=map_ldfied;
+
+  Set(P_ATTRIBUTES_MODIFIER, attributes_modifier);
+  UpdateAttributes();
+  return attributes_modifier[fn];
+}
+
+static mapping _query_attributes_modifier() 
+{
+  return deep_copy(attributes_modifier);  
+}
+
+public int SetAttr(string attr, int val) 
+{ closure filter_ldfied;
+
+  if ( filter_ldfied = symbol_function("_filterattr_"+attr, this_object()) ) 
+    val = funcall(filter_ldfied, val );
+
+  attributes[attr] = val;
+  UpdateAttributes();
+  return val;
+}
+
+public int SetAttribute(string attr, int val) 
+{
+  return SetAttr(attr, val-used_attributes_offsets[attr]);
+}
+
+// Diese Funktion sollte zum Abfragen verwendet werden:
+public int QueryAttribute(string attr) 
+{ int re;
+
+  re=attributes[attr]+used_attributes_offsets[attr];
+
+  if ( query_once_interactive(this_object()) && (re>30) )
+    re=30;
+
+  return re;
+}
+
+public int QueryRealAttribute(string attr) 
+{
+  return attributes[attr];
+}
+
+public int SetRealAttribute(string attr, int val) 
+{
+  return SetAttr(attr, val);
+}
+
+public int QueryAttributeOffset(string attr) 
+{
+  return used_attributes_offsets[attr];
+}
+
+public status TestLimitViolation(mapping check)
+{
+  int k,test;
+  mixed* ind;
+    
+  if(!check || !mappingp(check))
+  {
+    return 0;
+  }
+
+  test=0;
+  ind=m_indices(check);
+  for( k=sizeof(ind)-1 ; k>=0 ; k-- )
+  {
+    test+=check[ind[k]];
+  }
+  
+  test+=cumulative_mod;
+  if(test>CUMULATIVE_ATTR_LIMIT)
+  {
+    return 1;
+  }
+  
+  return 0;
+}
+
+/*
+ *------------------------------------------------------------
+ * attributes compatibility functions
+ */
+
+protected int _filterattr_str(int val)
+{
+  return ( (val<0) ? 0 : (val>20) ? 20 : val );
+}
+
+protected int _filterattr_dex(int val)
+{
+  return ( (val<0) ? 0 : (val>20) ? 20 : val );
+}
+
+protected int _filterattr_int(int val)
+{
+  return ( (val<0) ? 0 : (val>20) ? 20 : val );
+}
+
+protected int _filterattr_con(int val)
+{
+  return ( (val<0) ? 0 : (val>20) ? 20 : val );
+}
diff --git a/std/living/clothing.c b/std/living/clothing.c
new file mode 100644
index 0000000..56aff44
--- /dev/null
+++ b/std/living/clothing.c
@@ -0,0 +1,96 @@
+// MorgenGrauen MUDlib
+//
+// living/clothing.c -- Modul fuer Bekleidungsfragen. ;-)
+//
+// $Id: armour.c,v 3.8 2003/08/25 09:36:04 Rikus Exp $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+#include <living/clothing.h>
+#include <living/combat.h>
+#include <armour.h>
+
+protected void create() {
+  SetProp(P_CLOTHING, ({}));
+  Set(P_CLOTHING, PROTECTED, F_MODE_AS);
+}
+
+public object *FilterClothing(closure filterfun, varargs mixed* extra) {
+  if (!closurep(filterfun))
+    return ({});
+  return apply(#'filter, QueryProp(P_CLOTHING), filterfun, extra);
+}
+
+public object *FilterArmours(closure filterfun, varargs mixed* extra) {
+  if (!closurep(filterfun))
+    return ({});
+  return apply(#'filter, QueryProp(P_ARMOURS)-({0}), filterfun, extra);
+}
+
+public int WearClothing(object ob) {
+  object *clothing = QueryProp(P_CLOTHING);
+  if (member(clothing, ob) != -1)
+    return 0;
+  clothing += ({ob});
+  SetProp(P_CLOTHING, clothing);
+  return 1;
+}
+
+public int WearArmour(object ob) {
+  if (!VALID_ARMOUR_TYPE(ob->QueryProp(P_ARMOUR_TYPE)))
+    return 0;
+
+  object *armours = QueryProp(P_ARMOURS);
+  if (member(armours, ob) != -1)
+    return 0;
+
+  armours += ({ob});
+  SetProp(P_ARMOURS, armours);
+  return 1;
+}
+
+public int Wear(object ob) {
+  // reihenfolge ist wichtig! Ruestung sind _auch_ Kleidung, aber Kleidung
+  // keine Ruestung.
+  if (ob->IsArmour())
+    return WearArmour(ob);
+  else if (ob->IsClothing())
+    return WearClothing(ob);
+  return 0;
+}
+
+public int UnwearClothing(object ob) {
+object *clothing = QueryProp(P_CLOTHING);
+  if (member(clothing, ob) == -1)
+    return 0;
+  clothing -= ({ob});
+  SetProp(P_CLOTHING, clothing);
+  return 1;
+}
+
+public int UnwearArmour(object ob) {
+  object *armours = QueryProp(P_ARMOURS);
+  if (member(armours, ob) == -1)
+    return 0;
+
+  armours -= ({ob});
+  SetProp(P_ARMOURS, armours);
+  return 1;
+}
+
+public int Unwear(object ob) {
+  // reihenfolge ist wichtig! Ruestung sind _auch_ Kleidung, aber Kleidung
+  // keine Ruestung.
+  if (ob->IsArmour())
+    return UnwearArmour(ob);
+  else if (ob->IsClothing())
+    return UnwearClothing(ob);
+  return 0;
+}
+
diff --git a/std/living/combat.c b/std/living/combat.c
new file mode 100644
index 0000000..880f3f6
--- /dev/null
+++ b/std/living/combat.c
@@ -0,0 +1,2311 @@
+// MorgenGrauen MUDlib
+//
+// living/combat.c -- Basis-Kampfmodul
+//
+// $Id: combat.c 9568 2016-06-05 18:53:10Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/skill_utils";
+inherit "/std/living/inventory";
+inherit "/std/living/team";
+
+#include <sys_debug.h>
+#include <debug_message.h>
+
+#define NEED_PROTOTYPES
+#include <hook.h>
+#include <living/skills.h>
+#include <thing/properties.h>
+#include <player/comm.h>
+#include <living/skill_attributes.h>
+#include <combat.h>
+#include <living.h>
+#undef NEED_PROTOTYPES
+
+#include <config.h>
+#include <properties.h>
+#include <language.h>
+#include <wizlevels.h>
+#include <attributes.h>
+#include <new_skills.h>
+
+#include <defines.h>
+
+#include <sensitive.h>
+
+#define HUNTTIME 300 //300 HBs sind 10 Minuten
+#define RNAME(x) capitalize(getuid(x))
+
+// 'private'-Prototypes
+private string _kill_alias( string str );
+
+// globale Variablen
+nosave mapping enemies;
+private nosave string magic_attack;
+private nosave int attack_busy;
+nosave int no_more_attacks;
+private nosave int remaining_heart_beats;
+private nosave int att2_time;
+private nosave string last_attack_msg;
+private nosave object *missing_attacks;
+private nosave mapping peace_tries;
+// Cache fuer QueryArmourByType()
+private nosave mapping QABTCache;
+
+protected void create()
+{
+  Set(P_WIMPY, SAVE, F_MODE_AS);
+  Set(P_TOTAL_AC, PROTECTED, F_MODE_AS);
+  Set(P_HANDS, SAVE, F_MODE_AS);
+  Set(P_SHOW_ATTACK_MSG,SAVE,F_MODE_AS);
+  Set(P_RESISTANCE, ({}));
+  Set(P_VULNERABILITY, ({}));
+  Set(P_GUILD_PREPAREBLOCK, SAVE, F_MODE_AS);
+  // Kein Setzen von P_ARMOURS von aussen per Set(). (Per SetProp() geht es
+  // durch die Setmethode).
+  Set(P_ARMOURS,PROTECTED,F_MODE_AS);
+  SetProp(P_ARMOURS, ({}));
+  attack_busy=100;
+  att2_time=0;
+  enemies=([]);
+  peace_tries=([]);
+  team::create(); 
+  offerHook(H_HOOK_DEFEND,1);
+  offerHook(H_HOOK_ATTACK,1);
+  offerHook(H_HOOK_ATTACK_MOD,1);
+}
+
+#undef DEBUG
+#define DEBUG(x) if (find_object("zesstra")) \
+  tell_object(find_player("zesstra"),x)
+
+public void UpdateResistanceStrengths() { 
+  mapping resmods, strmap;
+
+  if ( !mappingp(resmods=Query(P_RESISTANCE_MODIFIER)) )
+    return;
+
+  //erstmal die alte Aufsummation loeschen
+  m_delete(resmods,"me");
+
+  //wenn jetzt leer: Abbruch, keine Res-Modifier da.
+  if (!sizeof(resmods))
+      return(Set(P_RESISTANCE_MODIFIER,0));
+
+  strmap = ([]);
+
+  // ueber alle gesetzten ResModifier gehen
+  foreach(string mod, mapping resmap, object ob: resmods) {
+    if ( !mappingp(resmap) || !sizeof(resmap)
+        || !objectp(ob) ) {
+      m_delete(resmods, mod);
+      continue; // Resi ungueltig, weg damit.
+    }
+    // jetzt noch ueber die Submappings laufen, die die Resis der Objekte
+    // beinhalten.
+    foreach(string reskey, float resi: resmap) {
+        strmap[reskey] = ((strmap[reskey]+1.0)*(resi+1.0))-1.0;
+    }
+  }
+
+  if ( !sizeof(strmap) )
+    Set(P_RESISTANCE_MODIFIER, 0);
+  else
+    Set(P_RESISTANCE_MODIFIER, resmods+([ "me" : strmap; 0 ]) );
+}
+
+public varargs int AddResistanceModifier(mapping mod, string add)
+{ string  key;
+  mapping res;
+
+  if ( !mappingp(mod) || !sizeof(mod) || !previous_object() )
+    return 0;
+
+  key = explode(object_name(previous_object()),"#")[0];
+
+  if ( add )
+    key += ("#"+add);
+
+  res = Query(P_RESISTANCE_MODIFIER);
+  mod = deep_copy(mod);
+
+  if ( !mappingp(res) )
+    res = ([ key : mod; previous_object() ]);
+  else
+    res += ([ key : mod; previous_object() ]);
+
+  Set(P_RESISTANCE_MODIFIER, res);
+  UpdateResistanceStrengths();
+
+  return 1;
+}
+
+public varargs void RemoveResistanceModifier(string add)
+{ string  key;
+  mapping res;
+
+  if ( !previous_object() )
+    return;
+
+  key = explode(object_name(previous_object()),"#")[0];
+
+  if ( add )
+    key += ("#"+add);
+
+  if ( !mappingp(res = Query(P_RESISTANCE_MODIFIER)) )
+    return;
+
+  m_delete(res, key);
+  Set(P_RESISTANCE_MODIFIER, res);
+  UpdateResistanceStrengths();
+}
+
+// veraltete Prop, aus Kompatibilitaetsgruenden noch vorhanden.
+static mixed _set_resistance(mixed arg)
+{ int     i;
+  mapping resimap;
+  mixed   old;
+
+  if ( !pointerp(arg) )
+    arg=({arg});
+
+  if ( !mappingp(resimap=Query(P_RESISTANCE_STRENGTHS)) )
+    resimap=([]);
+
+  if ( pointerp(old=QueryProp(P_RESISTANCE)) )
+    for ( i=sizeof(old)-1 ; i>=0 ; i-- )
+      resimap[old[i]]=(1.0+((float)resimap[old[i]]))*2.0-1.0;
+
+  for ( i=sizeof(arg)-1 ; i>=0 ; i-- )
+    resimap[arg[i]]=(1.0+((float)resimap[arg[i]]))*0.5-1.0;
+
+  SetProp(P_RESISTANCE_STRENGTHS,resimap);
+
+  return Set(P_RESISTANCE,arg);
+}
+
+// veraltete Prop, aus Kompatibilitaetsgruenden noch vorhanden.
+static mixed _set_vulnerability(mixed arg)
+{ int     i;
+  mapping resimap;
+  mixed   old;
+
+  if ( !pointerp(arg) )
+    arg=({arg});
+
+  if ( !mappingp(resimap=Query(P_RESISTANCE_STRENGTHS)) )
+    resimap=([]);
+
+  if ( pointerp(old=QueryProp(P_VULNERABILITY)) )
+    for ( i=sizeof(old)-1 ; i>=0 ; i-- )
+      resimap[old[i]]=(1.0+((float)resimap[old[i]]))*0.5-1.0;
+
+  for ( i=sizeof(arg)-1 ; i>=0 ; i-- )
+    resimap[arg[i]]=(1.0+((float)resimap[arg[i]]))*2.0-1.0;
+
+  SetProp(P_RESISTANCE_STRENGTHS,resimap);
+
+  return Set(P_VULNERABILITY,arg);
+}
+
+
+/** kill - Kampf starten.
+ * Fuegt ob der Feindesliste hinzu.
+ */
+public int Kill(object ob)
+{ int   res, arena;
+  int|string no_attack;
+
+  if ( !objectp(ob) )
+    return 0;
+
+  if ( ob->QueryProp(P_GHOST) )
+  {
+    tell_object(ME,ob->Name(WER)+" ist doch schon tot!\n");
+    return -1;
+  }
+
+  if ( no_attack = ob->QueryProp(P_NO_ATTACK) )
+  {
+    if ( stringp(no_attack) )
+      tell_object(ME, no_attack);
+    else
+      tell_object(ME, ob->Name(WER,1)+" laesst sich nicht angreifen!\n");
+
+    return -2;
+  }
+
+  if ( QueryProp(P_NO_ATTACK) )
+    return -3;
+
+  res=InsertEnemy(ob);
+  if (res)
+    tell_object(ME, "Ok.\n");
+  else if (IsEnemy(ob))
+    tell_object(ME, "Jajaja, machst Du doch schon!\n");
+  //else //kein gueltiger Feind, ob wurde nicht eingetragen.
+
+  if ( !res )
+    return -4;  // nicht aendern ohne Kill in player/combat.c
+
+  return 1;
+}
+
+public int InsertSingleEnemy(object ob)
+{
+    if ( !living(ob) )
+        return 0;
+
+    // Wie lange verfolgt dieses Living? Wenn Prop nicht gesetzt oder 
+    // ungueltig, d.h. kein int oder <= 0, dann wird die Defaultzeit genommen.
+    int hunttime = (int)QueryProp(P_HUNTTIME);
+    hunttime = (intp(hunttime) && hunttime > 0) ? 
+                 hunttime / __HEART_BEAT_INTERVAL__ : HUNTTIME;
+
+    // Auch wenn ein Objekt schon als Gegner eingetragen ist, trotzdem die
+    // HUNTTIME erneuern. Das "return 0;" muss aber bleiben, da der Gegner
+    // ja nicht neu ist.
+    //
+    // 09.12.2000, Tiamak
+    if ( member( enemies, ob ) ) {
+        enemies[ob,ENEMY_HUNTTIME] = hunttime;
+        return 0;
+    }
+
+    if ( (ob==ME) || QueryProp(P_NO_ATTACK)
+         || !objectp(ob) || (ob->QueryProp(P_NO_ATTACK))
+         || (ob->QueryProp(P_GHOST)) )
+        return 0;
+
+    object team;
+    if ( ( query_once_interactive(ME) || query_once_interactive(ob) )
+         && objectp(team=Query(P_TEAM)) && (team==(ob->Query(P_TEAM))) )
+        return 0;
+
+    set_heart_beat(1);
+
+    last_attack_msg=0;
+
+    enemies[ob,ENEMY_HUNTTIME] = hunttime;
+    Set(P_LAST_COMBAT_TIME,time());
+
+    return 1;
+}
+
+public int InsertEnemy(object ob)
+{ int res;
+
+  if ( res=InsertSingleEnemy(ob) )
+    InsertEnemyTeam(ob);
+
+  return res;
+}
+
+public object QueryPreferedEnemy()
+{ int    sz,r;
+  <int|object>*  pref;
+  object enemy;
+
+  enemy=0;
+  if ( pointerp(pref=QueryProp(P_PREFERED_ENEMY)) && ((sz=sizeof(pref))>1)
+      && intp(r=pref[0]) && (random(100)<r) )
+  {
+    enemy=pref[1+random(sz-1)];
+
+    if ( !objectp(enemy) )
+    {
+      pref-=({enemy});
+
+      if ( sizeof(pref)<2 )
+        pref=0;
+
+      SetProp(P_PREFERED_ENEMY,pref);
+
+      return 0;
+    }
+
+    if ( !IsEnemy(enemy) )
+      return 0;
+  }
+
+  return enemy;
+}
+
+public object *PresentEnemies() {
+
+  object *here=({});
+  object *netdead=({});
+  mixed no_attack;
+  foreach(object pl: enemies) {
+    if (no_attack = (mixed)pl->QueryProp(P_NO_ATTACK)) {
+      m_delete(enemies,pl);
+      // Und auch im Gegner austragen, sonst haut der weiter zu und dieses
+      // Living wehrt sich nicht. (Zesstra, 26.11.2006)
+      if (stringp(no_attack)) {
+        tell_object(ME, no_attack);
+        pl->StopHuntFor(ME, 1);
+      }
+      else pl->StopHuntFor(ME, 0);
+    }
+    else if ( environment()==environment(pl) )
+    {
+      if ( interactive(pl) || !query_once_interactive(pl) )
+        here+=({pl});
+      else
+        netdead+=({pl});
+    }
+  }
+
+  if ( !sizeof(here) ) // Netztote sind nur Feinde, falls keine anderen da sind
+    return netdead;
+
+  return here;
+}
+
+public varargs object SelectEnemy(object *here)
+{ object enemy;
+
+  if ( !pointerp(here) )
+    here=PresentEnemies();
+
+  if ( !sizeof(here) )
+    return 0;
+
+  if ( !objectp(enemy=QueryPreferedEnemy())
+      || (environment(enemy)!=environment()) )
+    enemy=here[random(sizeof(here))];
+
+  return enemy;
+}
+
+protected void heart_beat() {
+  int hbs; 
+  // leider rufen viele Leute einfach das geerbte heart_beat() obwohl sie
+  // schon tot sind und damit das Objekt zerstoert ist.
+  if (!living(this_object()))
+      return;
+
+  // Paralyse pruefen, ggf. reduzieren
+  int dis_attack = QueryProp(P_DISABLE_ATTACK);
+  if ( intp(dis_attack) && (dis_attack > 0) )
+  {
+      SetProp(P_DISABLE_ATTACK, --dis_attack);
+  }
+
+  // Attacken ermitteln: SA_SPEED + Rest aus dem letzten HB
+  hbs = remaining_heart_beats + QuerySkillAttribute(SA_SPEED);
+  if ( hbs <= 0 )
+    hbs = 100; 
+
+  // P_ATTACK_BUSY bestimmen
+  if ( attack_busy > 99) 
+   attack_busy = 100 + hbs;
+  else
+    attack_busy += 100 + hbs;
+  // mehr fuer Seher...
+  if ( IS_SEER(ME) )
+    attack_busy+=(100+QueryProp(P_LEVEL)); 
+  // max. 500, d.h. 5 Attacken
+  if ( attack_busy>500 )
+    attack_busy=500;
+
+  // unganzzahligen Rest fuer naechsten HB speichern
+  remaining_heart_beats = hbs % 100;
+  hbs /= 100; // ganze Attacken. ;-)
+
+  if ( hbs > 10 ) // nicht mehr als 10 Attacken.
+    hbs = 10; 
+
+  // Falls jemand seit dem letzten HB in diesem Living von aussen Attack()
+  // gerufen hat (nicht ExtraAttack()), wird jetzt was abgezogen.
+  hbs -= no_more_attacks;
+  no_more_attacks = 0;
+
+  // Wie lange verfolgt dieser NPC? Wenn Prop nicht gesetzt oder ungueltig,
+  // d.h. kein int oder <= 0, dann wird die Defaultzeit genommen.
+  int hunttime = (int)QueryProp(P_HUNTTIME);
+  hunttime = (intp(hunttime) && hunttime > 0) ? 
+              hunttime / __HEART_BEAT_INTERVAL__ : HUNTTIME;
+  // erstmal die Hunttimes der Gegner aktualisieren und nebenbei die
+  // anwesenden Feinde merken. Ausserdem ggf. Kampf abbrechen, wenn
+  // P_NO_ATTACK gesetzt ist.
+  // eigentlich ist diese ganze Schleife fast das gleiche wie
+  // PresentEnemies(), aber leider muessen ja die Hunttimes aktulisiert werde,
+  // daher kann man nicht direkt PresentEnemies() nehmen.
+  // TODO: Diese Schleife und PresentEnmies() irgendwie vereinigen.
+  object *netdead=({});
+  object *here=({});
+  object enemy;
+  mixed no_attack;
+  foreach(enemy, int htime: &enemies) { // Keys in enemy
+    // ggf. Meldungen ausgeben und Gegner austragen und dieses Living beim
+    // Gegner austragen.
+    if (no_attack=enemy->QueryProp(P_NO_ATTACK)) {
+      m_delete(enemies,enemy);
+        if ( stringp(no_attack) ) {
+            write( no_attack );
+            enemy->StopHuntFor( ME, 1 );
+        }
+        else
+            enemy->StopHuntFor( ME, 0 );
+    }
+    else if ( environment()==environment(enemy) ) {
+      // Gegner anwesend, als Feind merken...
+      if ( interactive(enemy) || !query_once_interactive(enemy) )
+        here+=({enemy});
+      else
+        netdead+=({enemy});
+      // ... und Hunttime erneuern.
+      htime = hunttime;
+    }
+    // Feind nicht anwesend. Timer dekrementieren und Feind austragen, wenn
+    // Jagdzeit abgelaufen.
+    else if ( (--htime) <= 0 )
+      StopHuntFor(enemy);
+  }
+  // Netztote sind nur Feinde, wenn sonst keine da sind.
+  if ( !sizeof(here) )
+    here=netdead;
+
+  // schonmal abfragen, ob ein Spell vorbereitet wird.
+  mixed pspell=QueryProp(P_PREPARED_SPELL);
+
+  //Da hbs 0 und no_more_attacks durch einen manuellen Aufruf von Attack() im
+  //Spieler >0 sein kann, kann jetzt hbs <0 sein. (s.o.)
+  if (hbs > 0 && sizeof(here)) {
+    foreach(int i: hbs) {
+      // Feind in Nahkampfreichweite finden
+      if ( objectp(enemy=SelectNearEnemy(here)) ) {
+        // Flucht erwuenscht?
+        if ( QueryProp(P_WIMPY) > QueryProp(P_HP) ) {
+          Flee();
+          // Flucht gelungen?
+          if ( enemy && (environment(enemy)!=environment()) ) {
+            here = 0; // naechste Runde neue Feinde suchen
+            enemy = 0;
+            continue; // kein Kampf, Runde ueberspringen
+          }
+        }
+      }
+      else {
+        // keine Feinde gefunden... Abbrechen.
+        break;
+      }
+      // Paralyse gesetzt? -> Abbrechen. Dieses Pruefung muss hier passieren,
+      // damit ggf. die automatische Flucht des Lebewesens durchgefuehrt wird,
+      // auch wenn es paralysiert ist.
+      if (dis_attack > 0) break;
+
+      // Kampf durch Spell-Prepare blockiert?
+      // Keine genaue Abfrage, wird spaeter noch genau geprueft.
+      if ( pspell && QueryProp(P_GUILD_PREPAREBLOCK) )
+        break; // keine Angriffe in diesem HB.
+      // wenn Feind da: hit'em hard.
+      if ( objectp(enemy) )
+        Attack(enemy);
+      // ggf. hat Attack() no_more_attacks gesetzt. Zuruecksetzen
+      no_more_attacks = 0;
+      // naechste Kampfrunde neue Feinde suchen
+      here = 0;
+    }  // foreach
+  }
+
+  no_more_attacks=0;
+
+  // Ist ein Spell in Vorbereitung und evtl. jetzt fertig vorbereitet?
+  if ( pointerp(pspell)
+      && (sizeof(pspell)>=3) && intp(pspell[0]) && stringp(pspell[1]) ) {
+    if ( time()>=pspell[0] ) // Kann der Spruch jetzt ausgefuehrt werden?
+    {
+      UseSpell(pspell[2],pspell[1]); // Dann los
+      SetProp(P_PREPARED_SPELL,0);
+    }
+  }
+  else if ( pspell ) // Unbrauchbarer Wert, loeschen
+    SetProp(P_PREPARED_SPELL,0);
+
+} // Ende heart_beat
+
+// wird von NPC gerufen, die Heartbeats nachholen und dabei die Hunttimes um
+// die Anzahl verpasster Heartbeats reduzieren muessen.
+// Wird von Spielerobjekten gerufen, wenn sie nach 'schlafe ein' aufwachen, um
+// die notwendige Anzahl an HB hier abzuziehen und ggf. die Feinde zu expiren,
+// da Spieler ja netztot keinen Heartbeat haben.
+protected void update_hunt_times(int beats) {
+  if (!mappingp(enemies)) return;
+  foreach(object en, int htime: &enemies) { // Mapping-Keys in en
+    htime -= beats;
+    if ( htime <= 0 )
+      StopHuntFor(en);
+  }
+}
+
+public int IsEnemy(object wer)
+{
+  return (member(enemies,wer));
+}
+
+public void StopHuntText(object arg)
+{
+  tell_object(arg,
+    Name(WER,1)+" "+(QueryProp(P_PLURAL)?"jagen ":"jagt ")+
+               (arg->QueryProp(P_PLURAL)?"Euch":"Dich")+" nicht mehr.\n");
+  tell_object(ME,(QueryProp(P_PLURAL)?"Ihr jagt ":"Du jagst ")+
+                 arg->name(WEN,1)+" nicht mehr.\n");
+}
+
+public varargs int StopHuntFor(object arg, int silent)
+{
+  if ( !objectp(arg) || !IsEnemy(arg) )
+    return 0;
+
+  if (!silent)
+    StopHuntText(arg);
+
+  m_delete(enemies,arg);
+  last_attack_msg=0;
+
+  return 1;
+}
+
+// Begruessungsschlag nur einmal pro HB
+public void Attack2(object enemy)
+{
+  if ( att2_time > time() )
+    return;
+
+  att2_time=time() + __HEART_BEAT_INTERVAL__;
+  Attack(enemy);
+}
+
+// Fuer evtl. Attack-Aenderungen, ohne dass man gleich das ganze
+// Attack ueberlagern muss:
+protected void InternalModifyAttack(mapping ainfo)
+{
+  return; // Vorerst!
+/*
+  int   fac;
+  mixed res;
+
+  fac=100;
+
+  if (CannotSee(1))
+    fac -= 50;
+
+  // Weitere Mali?
+
+  if (fac<100)
+    ainfo[SI_SKILLDAMAGE] = ainfo[SI_SKILLDAMAGE]*fac/100;
+  // Fertig
+*/
+}
+
+// Ausfuehren Eines Angriffs auch wenn es bereits einen Angriff
+// in dieser Runde gegeben hat. Dieser Angriff wird auch nicht
+// gezaehlt. Die Nutzung dieser Funktion ist nur fuer Spezialfaelle
+// gedacht und immer BALANCEPFLICHTIG!
+varargs public void ExtraAttack(object enemy, int ignore_previous)
+{
+  int saved_no_more_attacks;
+
+  // Erstschlagsinfo speichern.
+  saved_no_more_attacks = no_more_attacks;
+
+  // Bei Bedarf bisher schon durchgefuehrte Erstschlaege ignorieren
+  if (ignore_previous) no_more_attacks=0;
+
+  // Normalen Angriff durchfuehren
+  Attack (enemy);
+
+  // Gespeicherten Wert zuruecksetzen
+  no_more_attacks = saved_no_more_attacks;
+
+  return;
+}
+
+public void Attack(object enemy)
+{
+  closure cl;
+  mixed   res;
+  mapping ainfo;
+  mapping edefendinfo; // erweiterte Defend-Infos
+  mixed hookData;
+  mixed hookRes;
+
+  if ( no_more_attacks || QueryProp(P_GHOST) || 
+      !objectp(enemy) || !objectp(this_object()) || 
+      (QueryProp(P_DISABLE_ATTACK) > 0) || enemy->QueryProp(P_NO_ATTACK) || 
+      (query_once_interactive(this_object()) && !interactive(this_object())) )
+    return;
+
+  edefendinfo=([]);
+
+  Set(P_LAST_COMBAT_TIME,time());
+
+  // inkrementieren. Diese Variable wird im HB nach den Angriffen genullt.
+  // Diese Variable wird im naechsten Heartbeat von der Anzahl an verfuegbaren
+  // Angriffen des Livings abgezogen. Dies beruecksichtigt also zwischen den
+  // HBs (z.B. extern) gerufene Attacks().
+  no_more_attacks++;
+
+  // Wird das Attack durch einen temporaeren Attack-Hook ersetzt?
+  if ( res=QueryProp(P_TMP_ATTACK_HOOK) )
+  {
+    if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0]) && (time()<res[0])
+        && objectp(res[1]) && stringp(res[2]) )
+    {
+      if ( !(res=call_other(res[1],res[2],enemy)) )
+        return;
+    }
+    else
+      SetProp(P_TMP_ATTACK_HOOK,0);
+  }
+
+  // trigger attack hook
+  hookData=({enemy});
+  hookRes=HookFlow(H_HOOK_ATTACK,hookData);
+  if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
+    if(hookRes[H_RETCODE]==H_CANCELLED){
+      return;
+    }
+  }
+
+  ainfo = ([ SI_ENEMY : enemy,
+             SI_SPELL : 0,
+           ]);
+
+  if ( objectp(ainfo[P_WEAPON]=QueryProp(P_WEAPON)) )
+  {
+    ainfo[P_WEAPON]->TakeFlaw(enemy);
+
+    // Abfrage fuer den Fall, dass Waffe durch das TakeFlaw() zerstoert
+    // wurde. Dann wird das Attack abgebrochen.
+    if ( !objectp(ainfo[P_WEAPON]) )
+      return;
+
+    cl=symbol_function("name",ainfo[P_WEAPON]);
+    ainfo[SI_SKILLDAMAGE_MSG] = (" mit "+(string)funcall(cl,WEM,0));
+    ainfo[SI_SKILLDAMAGE_MSG2] = (" mit "+(string)funcall(cl,WEM,1));
+
+    ainfo[SI_SKILLDAMAGE] = (int)ainfo[P_WEAPON]->QueryDamage(enemy);
+
+    cl=symbol_function("QueryProp",ainfo[P_WEAPON]);
+    ainfo[SI_SKILLDAMAGE_TYPE] = funcall(cl,P_DAM_TYPE);
+    ainfo[P_WEAPON_TYPE] = (string)funcall(cl,P_WEAPON_TYPE);
+    ainfo[P_NR_HANDS] = (int)funcall(cl,P_NR_HANDS);
+    ainfo[P_WC] = (int)funcall(cl,P_WC);
+
+    // Zweihaendige Waffe?
+    if ( ainfo[P_NR_HANDS]==2
+        && mappingp(res=UseSkill(SK_TWOHANDED,deep_copy(ainfo)))
+        && member(res,SI_SKILLDAMAGE) )
+    {
+      // Nur den neuen Schadenswert uebernehmen.
+      ainfo[SI_SKILLDAMAGE]=((int)(res[SI_SKILLDAMAGE]));
+    }
+  }
+  else // Keine Waffe gezueckt
+  {
+    /* Check if there is a magical attack */
+    if ( mappingp(res=UseSkill(SK_MAGIC_ATTACK,([SI_ENEMY:enemy]))) )
+    {
+      ainfo[SI_SKILLDAMAGE]=(int)res[SI_SKILLDAMAGE];
+      ainfo[SI_SKILLDAMAGE_TYPE]=res[SI_SKILLDAMAGE_TYPE];
+
+      if ( stringp(res[SI_SKILLDAMAGE_MSG]) )
+        ainfo[SI_SKILLDAMAGE_MSG] = " mit "+(string)res[SI_SKILLDAMAGE_MSG];
+      else
+        ainfo[SI_SKILLDAMAGE_MSG] = " mit magischen Faehigkeiten";
+
+      if ( stringp(res[SI_SKILLDAMAGE_MSG2]) )
+        ainfo[SI_SKILLDAMAGE_MSG2] = " mit "+(string)res[SI_SKILLDAMAGE_MSG2];
+      else
+        ainfo[SI_SKILLDAMAGE_MSG2] = (string)ainfo[SI_SKILLDAMAGE_MSG];
+
+      if ( !(ainfo[P_WEAPON_TYPE]=res[P_WEAPON_TYPE]) )
+        ainfo[P_WEAPON_TYPE]=WT_MAGIC;
+
+      if ( member(res,SI_SPELL) )
+        ainfo[SI_SPELL]=res[SI_SPELL];
+    }
+    else
+    {
+      /* Ohne (freie) Haende wird auch nicht angegriffen */
+      if ( interactive(this_object())
+          && (QueryProp(P_USED_HANDS) >= QueryProp(P_MAX_HANDS)) )
+        return ;
+
+      if ( !pointerp(res=QueryProp(P_HANDS)) || (sizeof(res)<2) )
+        return;
+
+      ainfo[SI_SKILLDAMAGE] = (( 2*random(((int)res[1])+1)
+                              + 10*(QueryAttribute(A_STR)) )/3);
+      ainfo[SI_SKILLDAMAGE_TYPE] = res[2];
+      ainfo[SI_SKILLDAMAGE_MSG] = ainfo[SI_SKILLDAMAGE_MSG2]
+                                = ((string)res[0]);
+      ainfo[P_WEAPON_TYPE] = WT_HANDS;
+      ainfo[P_WC] = (int)res[1];
+    }
+  }  // besondere Faehigkeiten mit diesem Waffentyp?
+  if ( mappingp(res=UseSkill(FIGHT(ainfo[P_WEAPON_TYPE]),
+                             deep_copy(ainfo))) )
+    SkillResTransfer(res,ainfo);
+
+  // besondere allgemeine Kampffaehigkeiten?
+  if ( mappingp(res=UseSkill(SK_FIGHT,deep_copy(ainfo))) )
+    SkillResTransfer(res,ainfo);
+
+  // Veraenderungen durch einen Attack-Modifier?
+  if ( (res=QueryProp(P_TMP_ATTACK_MOD)) )
+  {
+    if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0])
+        && (time()<res[0]) && objectp(res[1]) && stringp(res[2]) )
+    {
+      if ( !(res=call_other(res[1],res[2],deep_copy(ainfo)))
+          || !mappingp(res) )
+        return;
+      else
+        SkillResTransfer(res,ainfo);
+    }
+    else
+      SetProp(P_TMP_ATTACK_MOD,0);
+  }
+
+  // trigger attack mod hook
+  hookData=deep_copy(ainfo);
+  hookRes=HookFlow(H_HOOK_ATTACK_MOD,hookData);
+  if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
+    if(hookRes[H_RETCODE]==H_CANCELLED){
+      return;
+    }
+    else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA] &&
+             mappingp(hookRes[H_RETDATA])){
+        SkillResTransfer(hookRes[H_RETDATA],ainfo);
+    }
+  }
+
+  // Interne Modifikationen der Angriffswerte
+  InternalModifyAttack(ainfo);
+
+  if ( !objectp(enemy) )
+    return;
+
+  // hier mal bewusst nicht auf P_PLURAL zuerst testen. in 90% der Faelle
+  // wird eh keine Angriffsmeldung mehr ausgegeben, also pruefen wir das
+  // lieber zuerst. Plural testen zieht schon genug Rechenzeit :/
+  // Nicht meine Idee! Nicht meine Idee! Nachtwind 7.7.2001
+  if ( ainfo[SI_SKILLDAMAGE_MSG2]!=last_attack_msg )
+  {
+    last_attack_msg = ainfo[SI_SKILLDAMAGE_MSG2];
+    if (QueryProp(P_PLURAL))
+    {
+      tell_object( ME, "  Ihr greift " + enemy->name(WEN,1)
+                   + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
+      say("  "+(Name(WER,1))+" greifen "+(enemy->name(WEN,1))+
+          ainfo[SI_SKILLDAMAGE_MSG]+" an.\n", enemy );
+      tell_object( enemy, "  "+(Name(WER,1))+" greifen "+
+                  (enemy->QueryProp(P_PLURAL)?"Euch":"Dich")+
+                   ainfo[SI_SKILLDAMAGE_MSG2]+" an.\n" );
+    }
+    else
+    {
+      tell_object( ME, "  Du greifst " + enemy->name(WEN,1)
+                   + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
+      say("  "+(Name(WER,2))+" greift "+(enemy->name(WEN,1))+
+          ainfo[SI_SKILLDAMAGE_MSG]+" an.\n", enemy );
+      tell_object( enemy, "  "+(Name(WER,1))+" greift "+
+                  (enemy->QueryProp(P_PLURAL)?"Euch":"Dich")+
+                   ainfo[SI_SKILLDAMAGE_MSG2]+" an.\n" );    }
+  }
+  else if ( Query(P_SHOW_ATTACK_MSG) )
+    if (QueryProp(P_PLURAL))
+      tell_object( ME, "  Ihr greift " + enemy->name(WEN,1)
+                   + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
+    else
+      tell_object( ME, "  Du greifst " + enemy->name(WEN,1)
+                   + ainfo[SI_SKILLDAMAGE_MSG2] + " an.\n" );
+
+  // ainfo in defendinfo merken
+  edefendinfo[ ORIGINAL_AINFO]= deep_copy(ainfo);
+  edefendinfo[ ORIGINAL_DAM]= ainfo[SI_SKILLDAMAGE];
+  edefendinfo[ ORIGINAL_DAMTYPE]= ainfo[SI_SKILLDAMAGE_TYPE];
+  edefendinfo[ CURRENT_DAM]= ainfo[SI_SKILLDAMAGE];
+  edefendinfo[ CURRENT_DAMTYPE]= ainfo[SI_SKILLDAMAGE_TYPE];
+
+  // ainfo[SI_SPELL] auf ein mapping normieren
+  if ( intp(ainfo[SI_SPELL]) )
+  {
+    ainfo[SI_SPELL] = ([ SP_PHYSICAL_ATTACK : !ainfo[SI_SPELL],
+               SP_SHOW_DAMAGE     : !ainfo[SI_SPELL],
+               SP_REDUCE_ARMOUR   : ([ ])  ]);
+  }
+
+  // defendinfo anhaengen, falls ainfo[SI_SPELL] ein mapping ist
+  if( mappingp(ainfo[SI_SPELL]))
+  {
+    ainfo[SI_SPELL][EINFO_DEFEND]=edefendinfo;
+  }
+
+
+  enemy->Defend(ainfo[SI_SKILLDAMAGE], ainfo[SI_SKILLDAMAGE_TYPE],
+                ainfo[SI_SPELL],       this_object());
+
+
+  //edefendinfo=([]);
+
+  /* Done attacking */
+}
+
+public void AddDefender(object friend)
+{ object *defs;
+
+  if ( !objectp(friend) )
+    return;
+
+  if ( !pointerp(defs=QueryProp(P_DEFENDERS)) )
+    defs=({});
+
+  if ( member(defs,friend)>=0 )
+    return;
+
+  defs+=({friend});
+  SetProp(P_DEFENDERS,defs);
+}
+
+public void RemoveDefender(object friend)
+{ object *defs;
+
+  if ( !objectp(friend) )
+    return;
+
+  if ( !pointerp(defs=QueryProp(P_DEFENDERS)) )
+    defs=({});
+
+  if ( member(defs,friend)==-1 )
+    return;
+
+  defs -= ({friend});
+  SetProp(P_DEFENDERS,defs);
+}
+
+public void InformDefend(object enemy)
+{
+  UseSkill(SK_INFORM_DEFEND,([ SI_ENEMY  : enemy,
+                               SI_FRIEND : previous_object() ]));
+  // Oh, oh - ich hoffe mal, dass InformDefend wirklich NUR aus Defend
+  // eines befreundeten livings aufgerufen wird... (Silvana)
+  // This is only experimental... ;)
+}
+
+public <int|string*|mapping>* DefendOther(int dam, string|string* dam_type,
+                                          int|mapping spell, object enemy)
+{
+  <int|string*|mapping>* res;
+
+  if ( (res=UseSkill(SK_DEFEND_OTHER,([ SI_SKILLDAMAGE : dam,
+                                        SI_SKILLDAMAGE_TYPE : dam_type,
+                                        SI_SPELL : spell,
+                                        SI_FRIEND : previous_object(),
+                                        SI_ENEMY : enemy ])))
+      && pointerp(res) )
+    return res;
+
+  return 0;
+}
+
+public void CheckWimpyAndFlee()
+{
+  if ( (QueryProp(P_WIMPY)>QueryProp(P_HP)) && !TeamFlee()
+      && find_call_out("Flee")<0 )
+    call_out(#'Flee,0,environment());
+}
+
+protected string mess(string msg,object me,object enemy)
+{ closure mname, ename;
+  string  *parts,x;
+  int     i;
+
+  mname = symbol_function("name", me);
+  ename = symbol_function("name", enemy);
+
+  parts=regexplode(msg,"@WE[A-Z]*[12]");
+  for ( i=sizeof(parts)-2 ; i>=1 ; i-=2 )
+  {
+    switch(parts[i])
+    {
+      case "@WER1":    parts[i]=funcall(mname,WER,1);    break;
+      case "@WESSEN1": parts[i]=funcall(mname,WESSEN,1); break;
+      case "@WEM1":    parts[i]=funcall(mname,WEM,1);    break;
+      case "@WEN1":    parts[i]=funcall(mname,WEN,1);    break;
+      case "@WER2":    parts[i]=funcall(ename,WER,1);    break;
+      case "@WESSEN2": parts[i]=funcall(ename,WESSEN,1); break;
+      case "@WEM2":    parts[i]=funcall(ename,WEM,1);    break;
+      case "@WEN2":    parts[i]=funcall(ename,WEN,1);    break;
+      default: ;
+    }
+  }
+
+  return break_string(capitalize(implode(parts,"")),78,"  ",1);
+}
+
+// Fuer evtl. Defend-Aenderungen, ohne dass man gleich das ganze
+// Attack ueberlagern muss:
+protected void InternalModifyDefend(int dam, string* dt, mapping spell, object enemy)
+{
+  return;
+}
+
+public int Defend(int dam, string|string* dam_type, int|mapping spell, object enemy)
+{
+  int     i,k;
+  mixed   res,res2;
+  object  *armours,tmp;
+  mixed hookData;
+  mixed hookRes;
+  
+  //  string  what, how;
+  string enname, myname;
+
+  // this_player(), wenn kein enemy bekannt...
+  enemy ||= this_player();
+  // Testen, ob dieses Lebewesen ueberhaupt angegriffen werden darf
+  if ( !this_object() || !enemy || QueryProp(P_NO_ATTACK)
+      || ( query_once_interactive(enemy) && ! interactive(enemy) ) )
+    return 0;
+
+  if ( intp(spell) )
+    spell = ([ SP_PHYSICAL_ATTACK : !spell,
+               SP_SHOW_DAMAGE     : !spell,
+               SP_REDUCE_ARMOUR   : ([ ])  ]);
+  else if ( !mappingp(spell) ) // Illegaler spell-Parameter
+    return 0;
+
+  // testen ob eine erweiterte defendinfo vorhanden ist
+  if(!member(spell,EINFO_DEFEND))
+  {
+          //spell+=([EINFO_DEFEND:([])]); // ggf hinzufuegen
+        // use a temporary mapping to avoid recursive
+        // val[x][y] = deep_copy(val);
+        mapping tmpdefend = ([
+                ORIGINAL_AINFO:deep_copy(spell),
+                ORIGINAL_DAM:dam,
+                  ORIGINAL_DAMTYPE:dam_type,
+        ]);
+          spell[EINFO_DEFEND]=tmpdefend;
+  }
+
+  // Schadenstyp ueberpruefen
+  if ( !pointerp(dam_type) )
+    dam_type = ({ dam_type });
+
+  spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+  spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+
+  // Testen, ob der Angreifer schon als Feind registriert worden ist.
+  // Wenn nein, registrieren.
+  if ( !IsEnemy(enemy) && !spell[SP_NO_ENEMY] )
+  {
+    spell[EINFO_DEFEND][ENEMY_INSERTED]=1;
+    InsertEnemy(enemy);
+  }
+
+  // RFR-Taktik abfangen
+  if ( !QueryProp(P_ENABLE_IN_ATTACK_OUT) )
+  {
+    i=time()-(enemy->QueryProp(P_LAST_MOVE));
+    // Gegner hat sich bewegt, man selbst nicht
+    if ( (i<3) && (time()-QueryProp(P_LAST_MOVE)>=5) )
+    {
+      // Bei Erster Kampfrunde wenige Schaden
+      dam/=(4-i);
+      spell[EINFO_DEFEND][RFR_REDUCE]=dam;
+      spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+    }
+  }
+
+  // Man kann Verteidiger haben. Diese kommen als erste zum Zuge
+  if ( res=QueryProp(P_DEFENDERS) )
+  { object *defs,*defs_here;
+
+    defs=({});
+    defs_here=({});
+    if ( !pointerp(res) )
+      res=({res});
+    // erst alle anwesenden finden.
+    foreach(object defender: res) {
+      if ( objectp(defender) && (member(defs,defender)<0) )
+      {
+        defs+=({defender});
+        //  Verteidiger muessen im gleichen Raum oder im Living selber
+        //  enthalten sein.
+        if ( environment(defender) == environment()
+            || environment(defender) == ME)
+        {
+          call_other(defender,"InformDefend",enemy);
+          if (defender)
+            defs_here += ({ defender });
+        }
+      }
+    }
+    //Anwesende Verteidiger eintragen.
+    spell[EINFO_DEFEND][PRESENT_DEFENDERS]=defs_here;
+
+    // P_DEFENDERS auch gleich aktualisieren
+    if ( sizeof(defs)<1 )
+      defs=0;
+    SetProp(P_DEFENDERS,defs);
+
+    if ( spell[SP_PHYSICAL_ATTACK] ) {
+      // Bei physischen Angriffen nur Verteidiger aus Reihe 1
+      // nehmen (z.B. fuer Rueckendeckung)
+      foreach(object defender: defs_here) {
+        if ( (defender->PresentPosition())>1 ) {
+          defs_here-=({defender});
+        }
+      }
+    }
+ 
+    if ( (i=sizeof(defs_here)) )
+    {
+      mixed edefendtmp=({defs_here[random(i)],0,0,0}); 
+      res=call_other(edefendtmp[DEF_DEFENDER],"DefendOther",
+                     dam,dam_type,spell,enemy);
+      if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0])
+          && pointerp(res[1]))
+      {
+        // Helfer koennen den Schaden oder Schadenstyp aendern,
+        // z.B. Umwandlung von Feuer nach Eis oder so...
+        dam=res[0];
+        edefendtmp[DEF_DAM]=dam;
+        dam_type=res[1];
+        edefendtmp[DEF_DAMTYPE]=dam_type;
+
+        if ( mappingp(res[2]) )
+        {
+          spell=res[2];
+          // teuer, aber geht nicht anders (Rekursion vermeiden)
+          edefendtmp[DEF_SPELL]=deep_copy(res[2]);
+        }
+        spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+        spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+      }
+      spell[EINFO_DEFEND][DEFENDING_DEFENDER]=edefendtmp;
+    }
+  } // Ende Defender-Verarbeitung
+
+
+  // Ueber einen P_TMP_DEFEND_HOOK werden z.B. Schutzzauber gehandhabt
+  spell[EINFO_DEFEND][DEFEND_HOOK]=DI_NOHOOK;
+  if ( res=QueryProp(P_TMP_DEFEND_HOOK) )
+  {
+    if ( pointerp(res) && (sizeof(res)>=3) && intp(res[0]) && (time()<res[0])
+        && objectp(res[1]) && stringp(res[2]) )
+    {
+      if ( !(res=call_other(res[1],res[2],dam,dam_type,spell,enemy)) )
+      {
+        // Ein P_TMP_DEFEND_HOOK kann den Schaden vollstaendig abfangen,
+        // das Defend wird dann hier abgebrochen *SICK* und es wird nur
+        // noch getestet, ob man in die Flucht geschlagen wurde
+        spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOKINTERRUPT;
+        CheckWimpyAndFlee();
+        return 0;
+      }
+      else
+      {
+        spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOK;
+        if ( pointerp(res) && (sizeof(res)>=3)
+            && intp(res[0] && pointerp(res[1])) )
+        {
+          mixed edefendtmp=({0,0,0});
+          // Der P_TMP_DEFEND_HOOK kann ebenfalls Schadenshoehe und
+          // -art sowie die Spell-Infos veraendern
+          dam=res[0];
+          edefendtmp[HOOK_DAM]=dam;
+          dam_type=res[1];
+          edefendtmp[HOOK_DAMTYPE]=dam_type;
+
+          if ( mappingp(res[2]) )
+          {
+            spell=res[2];
+            // Waeh. Teuer. Aber geht nicht anders.
+            edefendtmp[HOOK_SPELL]=deep_copy(spell);
+          }
+          spell[EINFO_DEFEND][DEFEND_HOOK]=edefendtmp;
+          spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+          spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+        }
+      }
+    }
+    else
+      SetProp(P_TMP_DEFEND_HOOK,0);
+  } // P_TMP_DEFEND_HOOK
+
+  // trigger defend hook
+  hookData=({dam,dam_type,spell,enemy});
+  hookRes=HookFlow(H_HOOK_DEFEND,hookData);
+  if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
+    if(hookRes[H_RETCODE]==H_CANCELLED){
+        spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOKINTERRUPT;
+        CheckWimpyAndFlee();
+        return 0;
+    }
+    else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA]){
+        spell[EINFO_DEFEND][DEFEND_HOOK]=DI_HOOK;
+        if ( pointerp(hookRes[H_RETDATA]) && (sizeof(hookRes[H_RETDATA])>=3)
+            && intp(hookRes[H_RETDATA][0] && pointerp(hookRes[H_RETDATA][1])) )
+        {
+          mixed edefendtmp=({0,0,0});
+          // Der P_TMP_DEFEND_HOOK kann ebenfalls Schadenshoehe und
+          // -art sowie die Spell-Infos veraendern
+          dam=hookRes[H_RETDATA][0];
+          edefendtmp[HOOK_DAM]=dam;
+          dam_type=hookRes[H_RETDATA][1];
+          edefendtmp[HOOK_DAMTYPE]=dam_type;
+
+          if ( mappingp(hookRes[H_RETDATA][2]) )
+          {
+            spell=hookRes[H_RETDATA][2];
+            // Teuer, teuer... :-(
+            edefendtmp[HOOK_SPELL]=deep_copy(spell);
+          }
+          spell[EINFO_DEFEND][DEFEND_HOOK]=edefendtmp;
+          spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+          spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+        }
+    }
+  } // Ende Hook-Behandlung
+
+  if ( !member(spell,SP_REDUCE_ARMOUR) || !mappingp(spell[SP_REDUCE_ARMOUR]) )
+        spell[SP_REDUCE_ARMOUR] = ([]);
+
+  // Es gibt auch Parierwaffen,
+  if ( objectp(tmp=QueryProp(P_PARRY_WEAPON)) )
+  {
+    res2=tmp->QueryDefend(dam_type, spell, enemy);
+
+    // Reduzierbare Wirksamkeit der Parierwaffe?
+    if ( member(spell[SP_REDUCE_ARMOUR],P_PARRY_WEAPON)
+        && intp(res=spell[SP_REDUCE_ARMOUR][P_PARRY_WEAPON]) && (res>=0) )
+    {
+      res2=(res2*res)/100;
+    }
+
+    dam-=res2;
+    spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+  }
+
+  // Jetzt kommen die Ruestungen des Lebewesens ins Spiel (wenn es denn
+  // welche traegt)
+
+  armours=QueryProp(P_ARMOURS)-({0});
+  if ( (i=sizeof(armours))>0 ) { 
+    string aty;
+
+    tmp=armours[random(i)];
+
+    if ( objectp(tmp) )
+      //Uebergabe des Mappings eh als Pointer/Referenz, daher billig
+      tmp->TakeFlaw(dam_type,spell[EINFO_DEFEND]);
+
+    // pro Ruestung ein Key an Platz reservieren
+    spell[EINFO_DEFEND][DEFEND_ARMOURS]=m_allocate(i,2);
+    foreach(object armour : armours) {
+      if ( objectp(armour) ) {
+        aty=(string)armour->QueryProp(P_ARMOUR_TYPE);
+
+        if ( member(spell[SP_REDUCE_ARMOUR],aty)
+            && intp(res2=spell[SP_REDUCE_ARMOUR][aty]) && (res2>=0) )
+          dam -= (res2*armour->QueryDefend(dam_type, spell, enemy))/100;
+        else
+          dam -= (int)(armour->QueryDefend(dam_type, spell, enemy));
+        // Schaden NACH DefendFunc vermerken. 
+        // Schutzwirkung VOR DefendFunc (DEF_ARMOUR_PROT) vermerkt
+        // das QueryDefend() selber.
+        spell[EINFO_DEFEND][DEFEND_ARMOURS][armour,DEF_ARMOUR_DAM]=dam;
+        // akt. Schaden vermerken und Schutz der aktuellen Ruestung (vor
+        // DefendFunc) wieder auf 0 setzen fuer den naechsten Durchlauf.
+        spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+        spell[EINFO_DEFEND][DEFEND_CUR_ARMOUR_PROT]=0;
+      }
+    }
+  }
+
+  // Manche Gilden haben einen Verteidigungsskill. Der kommt jetzt zum
+  // tragen. Der Skill kann die Schadenshoehe und -art beeinflussen.
+  spell[EINFO_DEFEND]+=([DEFEND_GUILD:({})]);
+  if ( mappingp(res=UseSkill(SK_MAGIC_DEFENSE,
+                            ([ SI_ENEMY            : enemy,
+                               SI_SKILLDAMAGE      : dam,
+                               SI_SKILLDAMAGE_TYPE : dam_type,
+                               SI_SPELL            : spell     ]))) )
+  {
+    dam=(int)res[SI_SKILLDAMAGE];
+
+    if ( pointerp(res[SI_SKILLDAMAGE_TYPE]) )
+        dam_type=res[SI_SKILLDAMAGE_TYPE];
+
+    spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+    spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+    spell[EINFO_DEFEND][DEFEND_GUILD]=({dam,dam_type});
+  }
+
+  // Evtl. interne Modifikationen
+  InternalModifyDefend(&dam, &dam_type, &spell, &enemy);
+
+  spell[EINFO_DEFEND][CURRENT_DAMTYPE]=dam_type;
+  spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+
+  // Testen, ob irgendwas im Inventory des Lebewesen auf den Angriff
+  // "reagiert"
+  CheckSensitiveAttack(dam,dam_type,spell,enemy);
+
+  if ( !objectp(enemy) )
+    return 0;
+
+  // Angriffszeit im Gegner setzen
+  enemy->SetProp(P_LAST_COMBAT_TIME,time());
+
+  // Die Resistenzen des Lebewesens (natuerliche und durch Ruestungen
+  // gesetzte) beruecksichtigen
+  dam = to_int(CheckResistance(dam_type)*dam);
+  spell[EINFO_DEFEND][DEFEND_RESI]=dam;
+
+  spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+
+  // Bei physikalischen Angriffen wird die natuerliche Panzerung und die
+  // Geschicklichkeit des Lebewesens beruecksichtigt
+  object stat = find_object("/d/erzmagier/zesstra/pacstat"); // TODO: remove
+  if ( spell[SP_PHYSICAL_ATTACK] )
+  {
+    // Minimum ist auch hier 1.
+    int body = QueryProp(P_BODY)+QueryAttribute(A_DEX);
+    res2 = (body/4 + random(body*3/4 + 1)) || 1;
+    if (stat)
+      stat->bodystat(body, res2, random(body)+1);
+
+    // Reduzierbare Wirksamkeit des Bodies?
+    if ( member(spell[SP_REDUCE_ARMOUR], P_BODY)
+        && intp(res=spell[SP_REDUCE_ARMOUR][P_BODY]) && (res>=0) )
+      res2=(res2*res)/100;
+
+    dam-=res2;
+  }
+  spell[EINFO_DEFEND][DEFEND_BODY]=dam;
+  spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+
+  // Ist ueberhaupt noch etwas vom Schaden uebrig geblieben?
+  if ( dam<0 )
+    dam = 0;
+  spell[EINFO_DEFEND][CURRENT_DAM]=dam;
+
+  // fuer die Statistik
+  // TODO: entfernen nach Test-Uptime
+  if (stat)
+    stat->damagestat(spell[EINFO_DEFEND]);
+
+  // Die Anzahl der abzuziehenden Lebenspunkte ist der durch 10 geteilte
+  // Schadenswert
+  dam = dam / 10;
+  spell[EINFO_DEFEND][DEFEND_LOSTLP]=dam;
+
+  // evtl. hat entweder der Aufrufer oder das Lebewesen selber eigene
+  // Schadensmeldungen definiert. Die vom Aufrufer haben Prioritaet.
+  mixed dam_msg = spell[SP_SHOW_DAMAGE];
+  // Wenn != 0 (d.h. Treffermeldungen grundsaetzlich erwuenscht), aber kein
+  // Array, hier im Living fragen.
+  if (dam_msg && !pointerp(dam_msg))
+    dam_msg = QueryProp(P_DAMAGE_MSG);
+
+  // In den meisten Faellen soll auch eine Schadensmeldung ausgegeben
+  // werden, die die Hoehe des Schadens (grob) anzeigt
+  if (spell[SP_SHOW_DAMAGE] && !pointerp(dam_msg)) {
+    myname=name(WEN);
+    enname=enemy->Name(WER);
+    if (enemy->QueryProp(P_PLURAL)) {
+      switch (dam) {
+      case 0:
+        tell_object(enemy,sprintf("  Ihr verfehlt %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s verfehlen Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+           sprintf("  %s verfehlen %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 1:
+        tell_object(enemy,sprintf("  Ihr kitzelt %s am Bauch.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s kitzeln Dich am Bauch.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s kitzeln %s am Bauch.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 2..3:
+        tell_object(enemy,sprintf("  Ihr kratzt %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s kratzen Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s kratzen %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 4..5:
+        tell_object(enemy,sprintf("  Ihr trefft %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s treffen Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s treffen %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 6..10:
+        tell_object(enemy,sprintf("  Ihr trefft %s hart.\n",myname));
+        tell_object(this_object(),sprintf("  %s treffen Dich hart.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s treffen %s hart.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 11..20:
+        tell_object(enemy,sprintf("  Ihr trefft %s sehr hart.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s treffen Dich sehr hart.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s treffen %s sehr hart.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 21..30:
+        tell_object(enemy,
+          sprintf("  Ihr schlagt %s mit dem Krachen brechender Knochen.\n",
+                  myname));
+        tell_object(this_object(),
+          sprintf("  %s schlagen Dich mit dem Krachen brechender Knochen.\n",
+                  enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s schlagen %s mit dem Krachen brechender Knochen.\n",
+                  enname,myname), ({ enemy, this_object() }));
+        break;
+      case 31..50:
+        tell_object(enemy,
+          sprintf("  Ihr zerschmettert %s in kleine Stueckchen.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s zerschmettern Dich in kleine Stueckchen.\n",
+                                          enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s zerschmettern %s in kleine Stueckchen.\n",
+                  enname,myname), ({ enemy, this_object() }));
+        break;
+      case 51..75:
+        tell_object(enemy,sprintf("  Ihr schlagt %s zu Brei.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s schlagen Dich zu Brei.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s schlagen %s zu Brei.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 76..100:
+        tell_object(enemy,sprintf("  Ihr pulverisiert %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s pulverisieren Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s pulverisieren %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 101..150:
+        tell_object(enemy,sprintf("  Ihr zerstaeubt %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s zerstaeuben Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s zerstaeuben %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 151..200:
+        tell_object(enemy,sprintf("  Ihr atomisiert %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s atomisieren Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s atomisieren %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      default:
+        tell_object(enemy,sprintf("  Ihr vernichtet %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s vernichten Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s vernichten %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      }
+
+    }
+    else {
+      switch (dam) {
+      case 0:
+        tell_object(enemy,sprintf("  Du verfehlst %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s verfehlt Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s verfehlt %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 1:
+        tell_object(enemy,sprintf("  Du kitzelst %s am Bauch.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s kitzelt Dich am Bauch.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s kitzelt %s am Bauch.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 2..3:
+        tell_object(enemy,sprintf("  Du kratzt %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s kratzt Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s kratzt %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 4..5:
+        tell_object(enemy,sprintf("  Du triffst %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s trifft Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s trifft %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 6..10:
+        tell_object(enemy,sprintf("  Du triffst %s hart.\n",myname));
+        tell_object(this_object(),sprintf("  %s trifft Dich hart.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s trifft %s hart.\n",enname,myname),
+            ({ enemy, this_object() }));
+        break;
+      case 11..20:
+        tell_object(enemy,sprintf("  Du triffst %s sehr hart.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s trifft Dich sehr hart.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s trifft %s sehr hart.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 21..30:
+        tell_object(enemy,
+          sprintf("  Du schlaegst %s mit dem Krachen brechender Knochen.\n",
+                  myname));
+        tell_object(this_object(),
+          sprintf("  %s schlaegt Dich mit dem Krachen brechender Knochen.\n",
+                  enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s schlaegt %s mit dem Krachen brechender Knochen.\n",
+                  enname,myname), ({ enemy, this_object() }));
+        break;
+      case 31..50:
+        tell_object(enemy,
+          sprintf("  Du zerschmetterst %s in kleine Stueckchen.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s zerschmettert Dich in kleine Stueckchen.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s zerschmettert %s in kleine Stueckchen.\n",
+                  enname,myname), ({ enemy, this_object() }));
+        break;
+      case 51..75:
+        tell_object(enemy,sprintf("  Du schlaegst %s zu Brei.\n",myname));
+        tell_object(this_object(),
+          sprintf("  %s schlaegt Dich zu Brei.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s schlaegt %s zu Brei.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 76..100:
+        tell_object(enemy,sprintf("  Du pulverisierst %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s pulverisiert Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s pulverisiert %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 101..150:
+        tell_object(enemy,sprintf("  Du zerstaeubst %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s zerstaeubt Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s zerstaeubt %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      case 151..200:
+        tell_object(enemy,sprintf("  Du atomisierst %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s atomisiert Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s atomisiert %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      default:
+        tell_object(enemy,sprintf("  Du vernichtest %s.\n",myname));
+        tell_object(this_object(),sprintf("  %s vernichtet Dich.\n",enname));
+        tell_room(environment(enemy)||environment(this_object()),
+          sprintf("  %s vernichtet %s.\n",enname,myname),
+                  ({ enemy, this_object() }));
+        break;
+      }
+    }
+  }
+
+  // Man kann auch selbst-definierte Schadensmeldungen ausgeben lassen
+  else if( spell[SP_SHOW_DAMAGE] && pointerp(dam_msg) )
+  {
+    for( i=sizeof(dam_msg) ; --i >= 0 ; )
+    {
+      if ( dam>dam_msg[i][0] )
+      {
+        tell_object(ME,mess(dam_msg[i][1],ME,enemy));
+        tell_object(enemy,mess(dam_msg[i][2],ME,enemy));
+        say(mess(dam_msg[i][3],ME,enemy), enemy);
+        break;
+      }
+    }
+  }
+  // else (!spell[SP_SHOW_DAMAGE]) keine Schadensmeldung.
+
+  // Informationen ueber den letzten Angriff festhalten
+  Set(P_LAST_DAMTYPES, dam_type);
+  Set(P_LAST_DAMTIME,  time());
+  Set(P_LAST_DAMAGE,   dam);
+
+  // Bei Angriffen mit SP_NO_ENEMY-Flag kann man nicht sterben ...
+  if ( spell[SP_NO_ENEMY] )
+    reduce_hit_points(dam);
+  // ... bei allen anderen natuerlich schon
+  else
+    do_damage(dam,enemy);
+
+  // evtl. ist dies Objekt hier tot...
+  if (!objectp(ME)) return dam;
+
+  // Testen, ob man in die Fucht geschlagen wird
+  CheckWimpyAndFlee();
+
+  // Verursachten Schaden (in LP) zurueckgeben
+  return dam;
+}
+
+public float CheckResistance(string *dam_type) {
+    //funktion kriegt die schadensarten uebergeben, schaut sich dieses
+    //sowie P_RESISTENCE_STRENGTH und P_RESITENCE_MODFIFIER an und berechnet
+    //einen Faktor, mit dem die urspruengliche Schadensmenge multipliziert
+    //wird, um den Schaden nach Beruecksichtigung der Resis/Anfaelligkeiten zu
+    //erhalten. Rueckgabewert normalerweise (s.u.) >=0.
+  mapping rstren, mod;
+  float   faktor,n;
+  int     i;
+
+  mod = Query(P_RESISTANCE_MODIFIER);
+  if ( mappingp(mod) )
+    mod = mod["me"];
+
+  if ( !mappingp(rstren=Query(P_RESISTANCE_STRENGTHS)) )
+  {
+    if (!mappingp(mod))
+      return 1.0;
+    else
+      rstren = ([]);
+  }
+
+  if ( !mappingp(mod) )
+    mod = ([]);
+
+  if ( (i=sizeof(dam_type))<1 )
+    return 1.0;
+
+  n=to_float(i);
+  faktor=0.0;
+
+  //dies hier tut nicht mehr das gewuenschte, wenn in P_RESISTENCE_STRENGTHS
+  //Faktoren <-1.0 angegeben werden. Rueckgabewerte <0 sind dann moeglich und
+  //leider werden die Schadensarten ohne Resis nicht mehr richtig verwurstet. :-/
+  foreach(string dt: dam_type) {
+    faktor = faktor + (1.0 + to_float(rstren[dt]))
+           * (1.0 + to_float(mod[dt]))-1.0;
+  }
+  return 1.0+(faktor/n);
+}
+
+public varargs mapping StopHuntingMode(int silent)
+{ mapping save_enemy;
+  int     i;
+
+  save_enemy=enemies;
+  if ( !silent )
+    walk_mapping(enemies, #'StopHuntText); //#');
+
+  enemies=m_allocate(0,1);
+  last_attack_msg=0;
+
+  return save_enemy;
+}
+
+public <object*|int*>* QueryEnemies()
+{
+  return ({m_indices(enemies),m_values(enemies)});
+}
+
+public mapping GetEnemies()
+{
+  return enemies;
+}
+
+public mapping SetEnemies(<object*|int*>* myenemies)
+{
+  enemies=mkmapping(myenemies[0],myenemies[1]);
+  return enemies;
+}
+
+private string _kill_alias( string str )
+{
+    return "\\" + str;
+}
+
+public varargs void Flee( object oldenv, int force )
+{ mixed   *exits, exit, dex;
+  mapping tmp;
+  int     i;
+  object  env;
+
+  if ( !environment() )
+    return;
+
+  // mit 'force' kann man die Checks umgehen, damit der Spieler auf jeden
+  // Fall fluechtet ...
+  if ( !force && ( oldenv && (oldenv != environment()) ||
+                   query_once_interactive(ME) && (!EnemyPresent() ||
+                   (QueryProp(P_HP) >= QueryProp(P_WIMPY))) ) )
+    return;
+
+  // ... aber Magier fluechten zu lassen ist nicht ganz so einfach ;-)
+  if ( query_once_interactive(this_object()) && IS_LEARNING(this_object()) )
+    return;
+
+  // Geister brauchen nicht um ihr Leben zu fuerchten
+  if ( QueryProp(P_GHOST) )
+    return;
+
+  tell_object( ME, "Die Angst ist staerker als Du ... "+
+                   "Du willst nur noch weg hier.\n");
+
+  if ( TeamFlee() ) // Erfolgreiche Flucht in hintere Kampfreihe?
+    return;
+
+  env = environment();
+  tmp = environment()->QueryProp(P_EXITS);
+  exits = m_indices(tmp);
+  tmp = environment()->QueryProp(P_SPECIAL_EXITS);
+  exits += m_indices(tmp);
+
+  if ( query_once_interactive(ME) )
+        exits = map( exits, #'_kill_alias/*'*/ );
+
+  // die Fluchtrichtung von Magiern wird aus Sicherheitsgruenden
+  // nicht ausgewertet
+  if ( interactive(ME) && IS_SEER(ME) && !IS_LEARNER(ME)
+      && (dex=QueryProp(P_WIMPY_DIRECTION)) )
+  {
+    i = 60 + 4 * (QueryProp(P_LEVEL) - 30) / 3;
+    exits += ({dex}); // bevorzugte Fluchtrichtung mindestens einmal
+  }
+
+  if ( !sizeof(exits) )
+  {
+    tell_object( ME, "Du versuchst zu fliehen, schaffst es aber nicht.\n" );
+
+    return;
+  }
+
+  while ( sizeof(exits) && (environment()==env) )
+  {
+    if ( dex                         // Vorzugsweise Fluchtrichtung?
+        && (member(exits,dex) >= 0)  // moeglich?
+        && (random(100) <= i))       // und Wahrscheinlichkeit gross genug?
+      exit = dex;
+    else
+      exit = exits[random(sizeof(exits))];
+
+    catch(command(exit);publish);
+    exits -= ({exit});
+  }
+
+  if ( environment()==env )
+    tell_object( ME, "Dein Fluchtversuch ist gescheitert.\n" );
+}
+
+public object EnemyPresent()
+{
+  foreach(object en: enemies) {
+    if (environment()==environment(en))
+      return en;
+  }
+  return 0;
+}
+
+public object InFight()
+{
+  return EnemyPresent();
+}
+
+public varargs int StopHuntID(string str, int silent) { 
+
+  if ( !stringp(str) )
+    return 0;
+
+  int j;
+  foreach(object en: enemies) {
+    if (en->id(str)) {
+      StopHuntFor(en,silent);
+      j++;
+    }
+  }
+
+  return j;
+}
+
+public int SpellDefend(object caster, mapping sinfo)
+{ int    re;
+  mixed  res;
+  string *ind;
+
+  re = UseSkill(SK_SPELL_DEFEND,([ SI_SKILLARG : sinfo ,
+                                   SI_ENEMY    : caster ]) );
+
+  if ( (res=QueryProp(P_MAGIC_RESISTANCE_OFFSET)) && mappingp(res)
+      && pointerp(sinfo[SI_MAGIC_TYPE]))
+  {
+    ind = m_indices(res) & sinfo[SI_MAGIC_TYPE];
+
+    if (pointerp(ind) && sizeof(ind) ) {
+      foreach(string index : ind)
+        re+=res[index];
+    }
+  }
+  else if(res && intp(res))
+    re+=res;
+
+  if ( (re>3333) && query_once_interactive(this_object()) )
+    re=3333; /* Maximal 33% Abwehrchance bei Spielern */
+  return re;
+}
+
+// **** this is property-like
+
+static int _set_attack_busy(mixed val)
+{
+  if ( ((to_int(val))>5) && previous_object(1)
+      && query_once_interactive(previous_object(1)) )
+    log_file("ATTACKBUSY",sprintf("%s %d Taeter: %O Opfer: %O\n",
+             dtime(time()),to_int(val),previous_object(1),this_object()));
+
+  attack_busy-=to_int(val*100.0);
+
+  if ( attack_busy<-2000 )
+    attack_busy=-2000;
+
+  return attack_busy;
+}
+
+static int _query_attack_busy()
+{
+  if (IS_LEARNING(ME))
+    return 0;
+
+  return (attack_busy<100);
+}
+
+// **** local property methods
+static int _set_wimpy(int i)
+{
+  if ( !intp(i) || (i>QueryProp(P_MAX_HP)) || (i<0) )
+    return 0;
+
+  // ggf. Statusreport ausgeben
+  if (interactive(ME))
+    status_report(DO_REPORT_WIMPY, i);
+
+  return Set(P_WIMPY, i);
+}
+
+static string _set_wimpy_dir(string s) {
+  // ggf. Statusreport ausgeben
+  if (interactive(ME))
+    status_report(DO_REPORT_WIMPY_DIR, s);
+  return Set(P_WIMPY_DIRECTION, s, F_VALUE);
+}
+
+static mixed _set_hands(mixed h)
+{ 
+  if ( sizeof(h)==2 )
+    h += ({ ({DT_BLUDGEON}) });
+  if (!pointerp(h[2]))
+    h[2] = ({h[2]});
+  return Set(P_HANDS, h, F_VALUE);
+}
+
+//TODO normalisieren/korrigieren in updates_after_restore().
+static mixed _query_hands()
+{
+  mixed *hands = Query(P_HANDS);
+  if ( !hands )
+    return Set(P_HANDS, ({ " mit blossen Haenden", 30, ({DT_BLUDGEON})}));
+  else if ( sizeof(hands)<3 )
+    return Set(P_HANDS, ({hands[0], hands[1], ({DT_BLUDGEON})}));
+  else if ( !pointerp(hands[2]) )
+    return Set(P_HANDS, ({hands[0], hands[1], ({ hands[2] })}));
+
+  return Query(P_HANDS);
+}
+
+static int _query_total_wc()
+{ mixed res;
+  int   totwc;
+
+  if ( objectp(res=QueryProp(P_WEAPON)) )
+    totwc = ((int)(res->QueryProp(P_WC)));
+  else if ( pointerp(res=QueryProp(P_HANDS)) && sizeof(res)>1
+           && intp(res[1]) )
+    totwc=((int)res[1]);
+  else
+    totwc=30;
+
+  totwc = ((2*totwc)+(10*QueryAttribute(A_STR)))/3;
+
+  return Set(P_TOTAL_WC, totwc, F_VALUE);
+}
+
+static int _query_total_ac() {
+
+  int totac = 0;
+  object *armours = QueryProp(P_ARMOURS);
+  object parry = QueryProp(P_PARRY_WEAPON);
+
+  if ( member(armours,0)>=0 ) {
+    armours -= ({ 0 }); 
+  }
+
+  foreach(object armour: armours)
+    totac += ((int)armour->QueryProp(P_AC));
+
+  if ( objectp(parry) )
+    totac += parry->QueryProp(P_AC);
+
+  totac += (QueryProp(P_BODY)+QueryAttribute(A_DEX));
+
+  return Set(P_TOTAL_AC, totac, F_VALUE);
+}
+
+static mapping _query_resistance_strengths() {
+
+  UpdateResistanceStrengths();
+
+  mapping rstren = copy(Query(P_RESISTANCE_STRENGTHS, F_VALUE));
+  mapping mod = Query(P_RESISTANCE_MODIFIER, F_VALUE);
+
+  if ( !mappingp(rstren) )
+    rstren = ([]);
+
+  if ( !mappingp(mod) || !mappingp(mod=mod["me"]) || !sizeof(mod) )
+    return rstren;
+
+  foreach(string modkey, float modvalue : mod)
+    rstren[modkey] = ((1.0+rstren[modkey])*(1.0+modvalue))-1.0;
+
+  return rstren;
+}
+
+static int _set_disable_attack(int val)
+{
+  if (val<-100000)
+           {
+                  log_file("PARALYSE_TOO_LOW",
+                          sprintf("Wert zu klein: %s, Wert: %d, "
+                                  "Aufrufer: %O, Opfer: %O",
+                                  ctime(time()),
+                                          val,previous_object(1),
+                                          this_object()));
+           }
+  if ( val>30 )
+    val=30;
+  if ( (val>=20) && previous_object(1)!=ME && query_once_interactive(ME) )
+    log_file("PARALYSE",sprintf("%s %d Taeter: %O Opfer: %O\n",
+                                ctime(time())[4..15],
+                                val,previous_object(1),this_object()));
+ 
+  if (time()<QueryProp(P_NEXT_DISABLE_ATTACK))
+  {
+    // gueltige Zeitsperre existiert.
+    // Erhoehen von P_DISABLE_ATTACK geht dann nicht. (Ja, auch nicht erhoehen
+    // eines negativen P_DISABLE_ATTACK)
+    if (val >= QueryProp(P_DISABLE_ATTACK))
+      return DISABLE_TOO_EARLY;
+    // val ist kleiner als aktuelles P_DISABLE_ATTACK - das ist erlaubt, ABER es
+    // darf die bestehende Zeitsperre nicht verringern, daher wird sie nicht
+    // geaendert.
+    return Set(P_DISABLE_ATTACK,val,F_VALUE);
+  }
+  // es existiert keine gueltige Zeitsperre - dann wird sie nun gesetzt.
+  // (Sollte val < 0 sein, wird eine Zeitsperre in der Vergangenheit gesetzt,
+  // die schon abgelaufen ist. Das ist ueberfluessig, schadet aber auch
+  // nicht.)
+  SetProp(P_NEXT_DISABLE_ATTACK,time()+val*2*__HEART_BEAT_INTERVAL__);
+  return Set(P_DISABLE_ATTACK,val);
+}
+
+// Neue Verwaltung der Haende:
+
+// P_HANDS_USED_BY enhaelt ein Array mit allen Objekten, die Haende
+// belegen, jedes kommt so oft vor wie Haende belegt werden.
+
+static mixed *_query_hands_used_by()
+{
+  return ((Query(P_HANDS_USED_BY, F_VALUE) || ({}) ) - ({0}));
+}
+
+static int _query_used_hands()
+{
+  return sizeof(QueryProp(P_HANDS_USED_BY));
+}
+
+static int _query_free_hands()
+{
+  return (QueryProp(P_MAX_HANDS)-QueryProp(P_USED_HANDS));
+}
+
+public varargs int UseHands(object ob, int num)
+{ mixed *h;
+
+  if ( !ob && !(ob=previous_object(1)) )
+    return 0;
+
+  if ( (num<=0) && ((num=ob->QueryProp(P_HANDS))<=0) )
+    return 0;
+
+  h=QueryProp(P_HANDS_USED_BY)-({ob});
+
+  if ( (sizeof(h)+num)>QueryProp(P_MAX_HANDS) )
+    return 0;
+
+  foreach(int i: num)
+    h+=({ob});
+
+  SetProp(P_HANDS_USED_BY,h);
+
+  return 1;
+}
+
+public varargs int FreeHands(object ob)
+{
+  if ( !ob && !(ob=previous_object(1)) )
+    return 0;
+
+  SetProp(P_HANDS_USED_BY,QueryProp(P_HANDS_USED_BY)-({ob}));
+
+  return 1;
+}
+
+// Kompatiblitaetsfunktionen:
+
+static int _set_used_hands(int new_num)
+{ int    old_num, dif;
+  object ob;
+
+  old_num=QueryProp(P_USED_HANDS);
+
+  if ( !objectp(ob=previous_object(1)) )
+    return old_num;
+
+  // Meldung ins Debuglog schreiben. Aufrufer sollte gefixt werden.
+  debug_message(sprintf("P_USED_HANDS in %O wird gesetzt durch %O\n",
+        this_object(), ob), DMSG_LOGFILE|DMSG_STAMP);
+
+  if ( !(dif=new_num-old_num) )
+    return new_num;
+
+  if ( dif>0 )
+    UseHands(ob,dif);
+  else
+    FreeHands(ob);
+
+  return QueryProp(P_USED_HANDS);
+}
+
+// Funktionen fuer Begruessungsschlag / Nackenschlag:
+
+public int CheckEnemy(object ob)
+{
+  return (living(ob) && IsEnemy(ob));
+}
+
+public varargs void ExecuteMissingAttacks(object *remove_attackers)
+{
+  if ( !pointerp(missing_attacks) )
+    missing_attacks=({});
+
+  if ( pointerp(remove_attackers) )
+    missing_attacks-=remove_attackers;
+
+  foreach(object ob : missing_attacks) {
+    if ( objectp(ob) && (environment(ob)==environment()) )
+      ob->Attack2(ME);
+  }
+  missing_attacks=({});
+}
+
+public void InitAttack()
+{ object ob,next;
+  closure cb;
+
+  if ( !living(ME) )
+    return;
+
+  ExecuteMissingAttacks();
+  //EMA kann das Living zerstoeren oder toeten...
+  if (!living(ME) || QueryProp(P_GHOST)) return;
+
+  if ( objectp(ob=IsTeamMove()) )
+    cb=symbol_function("InitAttack_Callback",ob);
+  else
+    cb=0;
+
+  for ( ob=first_inventory(environment()) ; objectp(ob) ; ob=next)
+  {
+    next=next_inventory(ob);
+
+    if ( !living(ob) )
+      continue;
+
+    if (ob->IsEnemy(ME))
+    {
+      // Das ist nicht so sinnlos wie es aussieht. a) werden die Hunttimes
+      // aktualisiert und b) werden Teammitglieder von mir bei diesem
+      // InsertEnemy() ggf. erfasst.
+      ob->InsertEnemy(ME);
+
+      if ( closurep(cb) && funcall(cb,ob) ) // Wird ganzes Team gemoved?
+        missing_attacks += ({ ob }); // Dann erstmal warten bis alle da sind
+      else
+        ob->Attack2(ME);
+
+    }
+    else if ( IsEnemy(ob) )
+    {
+      // Das ist nicht so sinnlos wie es aussieht. a) werden die Hunttimes
+      // aktualisiert und b) werden Teammitglieder von ob bei diesem 
+      // InsertEnemy() ggf. erfasst.
+      InsertEnemy(ob);
+      Attack2(ob);
+    }
+    //Attack2 kann dieses Objekt zerstoeren oder toeten. Wenn ja: abbruch
+    if ( !living(ME) || QueryProp(P_GHOST)) break;
+  }
+}
+
+public void ExitAttack()
+{
+  if ( !living(ME) )
+    return;
+
+  // Noch nachzuholende Begruessungsschlaege:
+  ExecuteMissingAttacks();
+}
+
+public object|object*|mapping QueryArmourByType(string type)
+{
+  // Rueckgabewert:
+  // DIE Ruestung vom Typ <type>, falls <type> nicht AT_MISC,
+  // Array aller AT_MISC-Ruestungen falls <type> AT_MISC (auch leer),
+  // Mapping mit allen oben genannten Infos, falls <type> Null
+
+  object *armours;
+  string typ2;
+
+  // Wenn Cache vorhanden, dann Cache liefern.
+  if (mappingp(QABTCache)) {
+    if (type == AT_MISC)
+      return QABTCache[AT_MISC] - ({0});
+    else if (type)
+      return QABTCache[type];
+    else
+      return copy(QABTCache);
+  }
+
+  if ( !pointerp(armours=QueryProp(P_ARMOURS)) )
+    armours=({});
+
+  // Cache erzeugen
+  QABTCache = ([ AT_MISC: ({}) ]);
+  foreach(object ob: armours - ({0}) ) {
+    if ( !stringp(typ2=ob->QueryProp(P_ARMOUR_TYPE)) )
+      continue;
+    if ( typ2==AT_MISC )
+      QABTCache[AT_MISC] += ({ob});
+    else
+      QABTCache[typ2] = ob;
+  }
+  // Und gewuenschtes Datum liefern.
+  if (type)
+    return QABTCache[type];
+  else
+    return copy(QABTCache);
+}
+
+//TODO: langfristig waers besser, wenn hier nicht jeder per SetProp() drauf
+//los schreiben wuerde.
+static object *_set_armours(object *armours) {
+  if (pointerp(armours)) {
+    // Cache wegwerfen
+    QABTCache = 0;
+    // armours ggf. unifizieren. Ausserdem Kopie reinschreiben.
+    return Set(P_ARMOURS, m_indices(mkmapping(armours)), F_VALUE);
+  }
+  return QueryProp(P_ARMOURS);
+}
+
+/** Reduziert die Befriedezaehler pro Reset im Durchschnitt um 2.5.
+  Berechnet ganzzahlige durchschnittliche Resets seit dem letzten Expire und
+  erhoeht die letzte Expirezeit um Resets*__RESET_TIME__ (Standard Intervall
+  fuer Reset ist momentan 3600s, im Durchschnitt kommen dann 2700 zwischen 2
+  Resets bei raus). So wird der unganzzahlige Rest beim naechsten Aufruf
+  beruecksichtigt.
+  Diese Variante des Expires wurde gewaehlt, um zu vermeiden, combat.c einen
+  reset() zu geben und in jedem Reset in jedem Lebewesen ein Expire machen zu
+  muessen, auch wenn nur in sehr wenigen Lebewesen was gemacht werden muss.
+  @param[in,out] ph Mapping aus P_PEACE_HISTORY. Wird direkt aktualisiert.
+  @attention Muss ein gueltiges P_PEACE_HISTORY uebergeben bekommen, anderem
+  Datentyp inkl. 0 wird die Funktion buggen.
+  */
+private void _decay_peace_history(mixed ph) {
+  // Ganzzahlige resets seit letztem Expire ermitteln. (Durchschnitt)
+  int resets = (time() - ph[0]) / (__RESET_TIME__*75/100);
+  // auf Zeitstempel draufrechnen, damit der unganzzahlige Rest nicht
+  // verlorengeht, der wird beim naechsten Expire dann beruecksichtigt.
+  ph[0] += resets * (__RESET_TIME__ * 75 / 100);
+  // pro Reset werden im Durchschnitt 2.5 Versuche abgezogen. (Hier werden
+  // beim Expire mal 2 und mal 3 Versuche pro Reset gerechnet. Aber falls hier
+  // viele Resets vergangen sind, ist es vermutlich eh egal, weil die Counter
+  // auf 0 fallen.)
+  int expire = resets * (random(2) + 2);
+  // ueber alle Gilden
+  mapping tmp=ph[1];
+  foreach(string key, int count: &tmp ) {
+    count-=expire;
+    if (count < 0) count = 0;
+  }
+}
+
+/** Pacify() dient zur Bestimmung, ob sich ein Lebewesen gerade 
+ * befrieden lassen will.
+ * Berechnet eine Wahrscheinlichkeit nach unten stehender Formel, welche die
+ * Intelligenz dieses Lebenwesens, die Intelligenz des Casters und die
+ * bisherige Anzahl erfolgreicher Befriedungsversuche dieser Gilde eingeht.
+ * Anschliessend wird aus der Wahrscheinlichkeit und random() bestimmt, ob
+ * dieser Versuch erfolgreich ist.
+Formel: w = (INT_CASTER + 10 - ANZ*4) / (INT_ME + 10)
+INT_CASTER: Caster-Intelligenz, INT_ME: Intelligenz dieses Livings
+ANZ: Anzahl erfolgreicher Befriedungsversuche
+
+Annahme: INT_CASTER === 22, alle Wahrscheinlichkeiten * 100
+
+INT_ME   Erfolgswahrscheinlichkeiten je nach Anzahl erfolgreicher Versuche
+              1       2       3       4       5       6       7       8
+      0     280     240     200     160     120      80      40       0
+      2  233,33     200  166,67  133,33     100   66,67   33,33       0
+      4     200  171,43  142,86  114,29   85,71   57,14   28,57       0
+      6     175     150     125     100      75      50      25       0
+      8  155,56  133,33  111,11   88,89   66,67   44,44   22,22       0
+     10     140     120     100      80      60      40      20       0
+     12  127,27  109,09   90,91   72,73   54,55   36,36   18,18       0
+     14  116,67     100   83,33   66,67      50   33,33   16,67       0
+     16  107,69   92,31   76,92   61,54   46,15   30,77   15,38       0
+     18     100   85,71   71,43   57,14   42,86   28,57   14,29       0
+     20   93,33      80   66,67   53,33      40   26,67   13,33       0
+     22    87,5      75    62,5      50    37,5      25    12,5       0
+     24   82,35   70,59   58,82   47,06   35,29   23,53   11,76       0
+     26   77,78   66,67   55,56   44,44   33,33   22,22   11,11       0
+     28   73,68   63,16   52,63   42,11   31,58   21,05   10,53       0
+     30      70      60      50      40      30      20      10       0
+     32   66,67   57,14   47,62    38,1   28,57   19,05    9,52       0
+     34   63,64   54,55   45,45   36,36   27,27   18,18    9,09       0
+     35   62,22   53,33   44,44   35,56   26,67   17,78    8,89       0
+     36   60,87   52,17   43,48   34,78   26,09   17,39     8,7       0
+     38   58,33      50   41,67   33,33      25   16,67    8,33       0
+     40      56      48      40      32      24      16       8       0
+     42   53,85   46,15   38,46   30,77   23,08   15,38    7,69       0
+     44   51,85   44,44   37,04   29,63   22,22   14,81    7,41       0
+     46      50   42,86   35,71   28,57   21,43   14,29    7,14       0
+     48   48,28   41,38   34,48   27,59   20,69   13,79     6,9       0
+     50   46,67      40   33,33   26,67      20   13,33    6,67       0
+     52   45,16   38,71   32,26   25,81   19,35    12,9    6,45       0
+     54   43,75    37,5   31,25      25   18,75    12,5    6,25       0
+     56   42,42   36,36    30,3   24,24   18,18   12,12    6,06       0
+     58   41,18   35,29   29,41   23,53   17,65   11,76    5,88       0
+     60      40   34,29   28,57   22,86   17,14   11,43    5,71       0
+     62   38,89   33,33   27,78   22,22   16,67   11,11    5,56       0
+     64   37,84   32,43   27,03   21,62   16,22   10,81    5,41       0
+     66   36,84   31,58   26,32   21,05   15,79   10,53    5,26       0
+     68    35,9   30,77   25,64   20,51   15,38   10,26    5,13       0
+     70      35      30      25      20      15      10       5       0
+     72   34,15   29,27   24,39   19,51   14,63    9,76    4,88       0
+     74   33,33   28,57   23,81   19,05   14,29    9,52    4,76       0
+     76   32,56   27,91   23,26    18,6   13,95     9,3    4,65       0
+     78   31,82   27,27   22,73   18,18   13,64    9,09    4,55       0
+     80   31,11   26,67   22,22   17,78   13,33    8,89    4,44       0
+     82   30,43   26,09   21,74   17,39   13,04     8,7    4,35       0
+     84   29,79   25,53   21,28   17,02   12,77    8,51    4,26       0
+     86   29,17      25   20,83   16,67    12,5    8,33    4,17       0
+     88   28,57   24,49   20,41   16,33   12,24    8,16    4,08       0
+     90      28      24      20      16      12       8       4       0
+     92   27,45   23,53   19,61   15,69   11,76    7,84    3,92       0
+     94   26,92   23,08   19,23   15,38   11,54    7,69    3,85       0
+     96   26,42   22,64   18,87   15,09   11,32    7,55    3,77       0
+     98   25,93   22,22   18,52   14,81   11,11    7,41     3,7       0
+    100   25,45   21,82   18,18   14,55   10,91    7,27    3,64       0
+ * @return 1, falls Befrieden erlaubt ist, 0 sonst.
+ * @param[in] caster Derjenige, der den Spruch ausfuehrt.
+ * @attention Wenn acify() 1 zurueckliefert, zaehlt dies als
+ * erfolgreicher Befriedungsversuch und in diesem Lebewesen wurde
+ * StopHuntingMode(1) aufgerufen.
+*/
+public int Pacify(object caster) {
+  
+  // wenn das Viech keine Gegner hat, dann witzlos. ;-) Ohne Caster gehts auch
+  // direkt raus.
+  if (!mappingp(enemies) || !sizeof(enemies)
+      || !objectp(caster)) {
+    return 0;
+  }
+
+  // Wenn P_ACCEPT_PEACE gesetzt ist, altes Verhalten wiederspiegeln
+  // -> der NPC ist einfach immer befriedbar. Gleiches gilt fuer den Caster
+  // selber, der wird sich ja nicht gegen das eigene Befriede wehren. Und auch
+  // im team wehrt man sich nicht gegen das Befriede eines Teamkollegen
+  if (QueryProp(P_ACCEPT_PEACE)==1 || caster==ME
+      || member(TeamMembers(), caster) > -1) {
+    StopHuntingMode(1); // Caster/Gilde sollte eigene Meldung ausgeben
+    return 1;
+  }
+
+  string gilde = caster->QueryProp(P_GUILD) || "ANY";
+
+  // ggf. P_PEACE_HISTORY initialisieren
+  mixed ph = (mixed)QueryProp(P_PEACE_HISTORY);
+  if (!pointerp(ph))
+    SetProp(P_PEACE_HISTORY, ph=({time(), ([]) }) );
+  
+  // ggf. die Zaehler reduzieren.
+  if ( ph[0] + __RESET_TIME__ * 75/100 < time()) {
+    _decay_peace_history(&ph);
+  }
+  
+  float w = (caster->QueryAttribute(A_INT) + 10 - ph[1][gilde] * 4.0) / 
+             (QueryAttribute(A_INT) + 10);
+  // auf [0,1] begrenzen.
+  if (w<0) w=0.0;
+  else if (w>1) w=1.0;
+  // w * n ist eine Zahl zwischen 0 und n, wenn w * n > random(n)+1,
+  // darf befriedet werden. Da das Random fuer grosse Zahlen
+  // besser verteilt ist, nehm ich n = __INT_MAX__ und vernachlaessige
+  // ausserdem die +1 beim random().
+  if (ceil(w * __INT_MAX__) > random(__INT_MAX__) ) {
+    ph[1][gilde]++;
+    StopHuntingMode(1);
+    return 1;
+  }
+  // ein SetProp(P_PEACE_HISTORY) kann entfallen, da das Mapping direkt
+  // geaendert wird. Sollte die Prop allerdings mal ne Querymethode haben,
+  // welche eine Kopie davon liefert, muss das hier geaendert oder die
+  // Prop per Query() abgefragt werden.
+  return 0;
+}
+
diff --git a/std/living/comm.c b/std/living/comm.c
new file mode 100644
index 0000000..6001ffd
--- /dev/null
+++ b/std/living/comm.c
@@ -0,0 +1,133 @@
+// MorgenGrauen MUDlib
+//
+// living/comm.c -- communiction module for livings
+//
+// $Id$
+
+#pragma strong_types,save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+#include <defines.h>
+#include <living/comm.h>
+
+void create_super()
+{
+  set_next_reset(-1);
+}
+
+protected string comm_guess_action() {
+  string cmd;
+  string action = query_verb();
+  // Die Aktionen sind intern in der Regel nach den haeufigsten Kommandoverben
+  // dieser Aktion benannt. Bei einigen Aktionen sind mehrere Kommandoverben
+  // ueblich, die sollen hier noch abgehandelt werden.
+  switch(action) {
+    case "nehme":
+      // MA_TAKE == nimm
+      action = MA_TAKE;
+      break;
+
+    case "norden":
+    case "nordosten":
+    case "osten":
+    case "suedosten":
+    case "sueden":
+    case "suedwesten":
+    case "westen":
+    case "nordwesten":
+    case "oben":
+    case "unten":
+    case "betrete":
+    case "verlasse":
+    case "teleport":
+    case "teleportiere":
+      action = MA_MOVE;
+      break;
+
+    case "unt":
+      action = MA_LOOK;
+      break;
+
+    case "wirf":
+      if (strstr(query_command(), " weg") > -1)
+        action = MA_PUT;
+      break;
+
+    case "stecke":
+      cmd = query_command();
+      if (strstr(cmd, " weg") > -1)
+        action = MA_UNWIELD;
+      else if (strstr(cmd," in ") > -1)
+        action = MA_PUT;
+      break;
+
+    case "ziehe":
+      cmd = query_command();
+      if (strstr(cmd, " an") > -1)
+        action = MA_WEAR;
+      else if (strstr(cmd, " aus") > -1)
+        action = MA_UNWEAR;
+      break;
+
+    case "esse":
+    case "friss":
+      action = MA_EAT;
+      break;
+
+    case "saufe":
+      action = MA_DRINK;
+      break;
+
+    case "hoere":
+      //MA_LISTEN == lausche
+      action = MA_LISTEN;
+      break;
+    case "lese":
+      action = MA_READ;
+      break;
+
+    case ":":
+    case ";":
+      action = MA_EMOTE;
+      break;
+
+    case "zerbreche":
+    case "zerstoere":
+    case "verbrenne":
+    case "entsorge":
+      action = MA_REMOVE;
+      break;
+  }
+  return action;
+}
+
+protected int comm_guess_message_type(string action, mixed origin) {
+  // everything not mentioned in the switch becomes MT_LOOK.
+  switch(action) {
+    case MA_FIGHT:
+      // Kampf kann man meisten sowohl sehen als auch hoeren.
+      return MT_LOOK | MT_LISTEN;
+    case MA_LISTEN:
+    case MA_SAY:
+      return MT_LISTEN;
+    case MA_FEEL:
+      return MT_FEEL;
+    case MA_SMELL:
+      return MT_SMELL;
+    case MA_CHANNEL:
+      return MT_COMM | MT_FAR;
+    case MA_EMOTE:
+      if (objectp(origin)
+          && environment(origin) == environment())
+        return MT_COMM;
+      else
+        return MT_COMM | MT_FAR;
+    case MA_SHOUT:
+      return MT_LISTEN | MT_FAR;
+  }
+  // die meisten Aktionen sind zumindest sichtbar...
+  return MT_LOOK;
+}
+
diff --git a/std/living/description.c b/std/living/description.c
new file mode 100644
index 0000000..6353ca9
--- /dev/null
+++ b/std/living/description.c
@@ -0,0 +1,332 @@
+// MorgenGrauen MUDlib
+//
+// living/description.c -- description for living objects
+//
+// $Id: description.c 9395 2015-12-08 23:04:38Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/container/description";
+
+#define NEED_PROTOTYPES
+
+#include <living/skills.h>
+#include <living/clothing.h>
+#include <thing/properties.h>
+#include <wizlevels.h>
+#include <new_skills.h>
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+#include <class.h>
+#include <sys_debug.h>
+
+public string _query_internal_extralook() {
+  mixed xl;
+  int zeit;
+  string res, look="";
+
+  xl=Query(P_INTERNAL_EXTRA_LOOK,F_VALUE);
+  if (!mappingp(xl))
+    return(0);
+
+  foreach(string key, mapping xld: xl) {
+    if (intp(zeit=xld["xlduration"])) {
+      //hat offenbar nen Ablaufdatum
+      if ( (zeit > 0 && zeit < time()) ||
+           (zeit < 0 && abs(zeit) < object_time(ME)) ) {
+        // Zeit abgelaufen oder
+        // negative "Ablaufzeit" und das Objekt ist neuer als die
+        // Eintragzeit, also loeschen und weiter (ja, das geht. ;-) und xld
+        // hat das Eintragsmapping ja noch, weitere Benutzung also moeglich.)
+        m_delete(xl,key);
+        // ggf. Meldung ausgeben
+        if (interactive(ME)) {
+          if (sizeof(xld["xlende"])) {
+            tell_object(ME,xld["xlende"]);
+          }
+          //kein einfacher String, aber Objekt+Funktion gegeben?
+          else if (sizeof(xld["xlendefun"]) && sizeof(xld["xlobjectname"]) &&
+            (!catch(res=call_other(xld["xlobjectname"],xld["xlendefun"],ME)
+                    ;publish))) {
+              if (stringp(res) && sizeof(res))
+                tell_object(ME,res);
+            }
+        }
+        continue;
+      }
+    }
+    // Der Eintrag ist offenbar noch gueltig, Meldung anhaengen, bzw. via
+    // Funktionsaufruf beschaffen.
+    if (sizeof(xld["xllook"]))
+      look+=xld["xllook"];
+    else if (sizeof(xld["xlfun"]) && sizeof(xld["xlobjectname"])) {
+      closure cl;
+      if (catch(cl=symbol_function(xld["xlfun"],xld["xlobjectname"]);publish)
+          || !cl) {
+          // wenn Fehler beim Laden/Closure erstellen, dann Eintrag loeschen
+          // -> Annahme, dass dieser Fehler permanent sein wird, z.B. Eintrag
+          // von Clonen
+          m_delete(xl,key);
+          continue;
+      }
+      else if (!catch(res=funcall(cl, ME); publish)) {
+        if (!stringp(res) || !sizeof(res)) {
+          // keinen String oder leeren String gekriegt -> ueberspringen.
+          continue;
+        }
+        else
+          look+=res;
+      }
+    }
+  }
+  // fertig. Wenn look nicht leer ist, zurueckgeben, sonst 0.
+  if (sizeof(look))
+    return(look);
+  else
+    return(0);
+}
+
+public varargs int AddExtraLook(string look, int duration, string key, 
+                                string lookende, object ob) {
+  mapping xl;
+  string oname;
+  if (!stringp(key) || !sizeof(key)) {
+    // Automatisch erzeugen, wenn moeglich
+    if (!objectp(previous_object()) || 
+        !stringp(key=object_name(previous_object())) || !sizeof(key))
+      return(-1);
+  }
+
+  if (!stringp(look) || !sizeof(look))
+    return(-2);
+  if (!intp(duration))
+    return(-3);
+
+  xl=Query(P_INTERNAL_EXTRA_LOOK,F_VALUE); // dran denken: liefert referenz zurueck
+  if (!mappingp(xl)) {
+    Set(P_INTERNAL_EXTRA_LOOK, xl=([]) );
+  }
+
+  // kein Automatisches Ueberschreiben.
+  if (member(xl,key))
+    return(-4);
+
+  // neg. Werte: "bis Ende/reboot", abs(duration) == Eintragzeit
+  // 0: "fuer ewig", >0: Zeitdauer in Sekunden
+  if (duration > 0)
+    duration+=time();  // hoffentlich gibt es reichtzeitig 64bit-Ints
+  else if (duration < 0)
+    duration=negate(time());
+  // 0 bleibt, wie es ist.
+
+  if (objectp(ob)) {
+    // Funktionsname und Objektname (als Name, damit es auch noch geht, wenn
+    // das Objekt entladen wurde, Crash/reboot war etc.) speichern
+    // Clone werden auch gespeichert, aber es wird direkt ein harter Fehler
+    // ausgeloest, wenn ein permanenter Xlook fuer einen Clone registriert
+    // werden soll: das kann nicht gehen.
+    if (!duration && clonep(ob))
+        raise_error(sprintf(
+           "AddExtraLook(): Fehlerhaftes Argument <duration>: %d, "
+           "permanente Extralooks durch Clone (%s) nicht registrierbar.\n",
+           duration, object_name(ob)));
+
+    xl[key]=(["xlobjectname":object_name(ob),
+              "xlfun": look,
+             ]);
+    // ggf. Name der Funktion speichern, die bei Ablauf aufgerufen wird.
+    if (stringp(lookende) && sizeof(lookende))
+        xl[key]["xlendefun"]=lookende;
+  }
+  else {
+    // Einfacher Eintrag, nur den bearbeiteten String merken. ;-)
+    xl[key]=(["xllook": break_string(replace_personal(look,({ME}),1),78,
+                                     "",BS_LEAVE_MY_LFS),
+             ]);
+    // ggf. Meldung speichern, die bei Ablauf ausgegeben werden soll.
+    if (stringp(lookende) && sizeof(lookende)) {
+      xl[key]["xlende"]=break_string(replace_personal(lookende,({ME}),1),78,
+                                     "",BS_LEAVE_MY_LFS);
+    }
+  }
+  // Endezeit vermerken.
+  if (duration != 0)
+    xl[key]["xlduration"]=duration;
+
+  // Kein Set noetig, weil Query das Mapping ja als Referenz lieferte.
+  return(1);
+}
+
+public int RemoveExtraLook(string key) {
+  mapping xl;
+  if (!stringp(key) || !sizeof(key)) {
+    // Automatisch erzeugen, wenn moeglich
+    if (!objectp(previous_object()) ||
+        !stringp(key=object_name(previous_object())) || !sizeof(key))
+      return(-1);
+  }
+  xl=Query(P_INTERNAL_EXTRA_LOOK,F_VALUE); // dran denken: liefert referenz zurueck
+  if (!mappingp(xl))
+    return (-2);
+  if (!member(xl,key))
+    return(-2);
+
+  m_delete(xl,key);
+  // Kein Set noetig, weil Query das Mapping ja als Referenz lieferte.
+  return(1);
+}
+
+void create()
+{ 
+  ::create();
+  Set(P_GENDER, SAVE, F_MODE);
+  // Extralook-Property speichern und vor manueller Aenderung schuetzen
+  // EMs duerfen, die wissen hoffentlich, was sie tun.
+  Set(P_INTERNAL_EXTRA_LOOK, SAVE|PROTECTED, F_MODE_AS);
+  SetProp(P_CLOTHING,({}));
+  AddId("Living");
+}
+
+string condition()
+{
+  int hpnt, max_hpnt, perc;
+
+  hpnt        = QueryProp( P_HP );
+  max_hpnt    = QueryProp( P_MAX_HP );
+
+  if(max_hpnt>0 && hpnt>0)
+    perc=100*hpnt/max_hpnt;
+ 
+  switch(perc) {
+    case 0..9:
+        return capitalize(QueryPronoun(WER))+" steht auf der Schwelle des Todes.\n";
+    case 10..19:
+        return capitalize(QueryPronoun(WER))+" braucht dringend einen Arzt.\n";
+    case 20..29:
+        return capitalize(QueryPronoun(WER))+" ist in keiner guten Verfassung.\n";
+    case 30..39:
+        return capitalize(QueryPronoun(WER))+" wankt bereits bedenklich.\n";
+    case 40..49:
+        return capitalize(QueryPronoun(WER))+" macht einen mitgenommenen Eindruck.\n";
+    case 50..59:
+        return capitalize(QueryPronoun(WER))+" sieht nicht mehr taufrisch aus.\n";
+    case 60..69:
+        return capitalize(QueryPronoun(WER))+" ist leicht angeschlagen.\n";
+    case 70..79:
+        return capitalize(QueryPronoun(WER))+" fuehlte sich heute schon besser.\n";
+    case 80..89:
+        return capitalize(QueryPronoun(WER))+" ist schon etwas geschwaecht.\n";
+  }
+  //fall-through
+  return capitalize(QueryPronoun(WER))+" ist absolut fit.\n";
+}
+
+varargs string long() {
+  string str, cap_pronoun;
+  string descr, invl,tmp,exl;
+  int hpnt, max_hpnt;
+  mixed filter_ldfied;
+  object ob;
+
+  str = process_string( QueryProp(P_LONG) );
+  if(!stringp(str)) str = "";
+
+  str += condition();
+
+  // Extralook
+  if(stringp(tmp = QueryProp(P_EXTRA_LOOK)))
+    str += tmp;
+  if (stringp(tmp = QueryProp(P_INTERNAL_EXTRA_LOOK)))
+    str += tmp;
+  for(ob = first_inventory(ME); ob; ob = next_inventory(ob))
+    if(exl = ob->QueryProp(P_EXTRA_LOOK)) 
+      str += exl;
+    else if(exl = ob->extra_look()) 
+      str += exl; // TO BE REMOVED
+
+  
+  if(filter_ldfied = QueryProp(P_TRANSPARENT))
+  {
+    invl = make_invlist(PL, all_inventory(ME));
+    if(invl != "")
+      str += capitalize(QueryPronoun(WER))+" traegt bei sich:\n" + invl;
+  }
+  return str;
+}
+
+varargs string name(int casus, int demonst)
+{ 
+  if( QueryProp( P_INVIS ) )
+  {
+    if( casus == RAW ) return "Jemand";
+    return ({"Jemand","Jemands","Jemandem","Jemanden"})[casus];
+  }
+  if (QueryProp(P_FROG) && casus != RAW )
+  {
+    string s=QueryArticle(casus, 0, 1)+"Frosch";
+    if (casus==WESSEN) s += "es";
+    return s;
+  }
+  return ::name( casus, demonst );
+}
+
+static int _query_gender()
+{
+  if (QueryProp(P_FROG)) return 1;
+  return Query(P_GENDER);
+}
+
+// NPC sollen aus Kompatibilitaetsgruenden auch eine "echte" Rasse haben.
+// Default ist hier die Rasse, man kann aber hiermit auch einen NPC faken,
+// der sich tarnt, indem man P_REAL_RACE per Hand setzt.
+static string _query_real_race()
+{
+  return Query(P_REAL_RACE,F_VALUE)||QueryProp(P_RACE);
+}
+
+static mixed _set_name(mixed nm )
+{
+  string lvnam;
+  lvnam = nm;
+  if(pointerp(nm)) lvnam = nm[0];
+  set_living_name(lower_case(lvnam));
+  return Set(P_NAME, nm);
+}
+
+int _query_container()
+{
+  return 0;
+}
+
+int is_class_member(mixed str) {
+  // Keine Klasse, keine Mitgliedschaft ...
+  if (!str || (!stringp(str) && !pointerp(str)) || str=="") 
+      return 0;
+
+  if (::is_class_member(str))
+    return 1;
+
+  if (stringp(str))
+    str = ({str});
+
+  // Rassen werden als implizite Klassen aufgefasst.
+  // TODO: Pruefen, ob das unbedingt hart-kodiert sein muss.
+  string race = QueryProp(P_RACE);
+  if ( stringp(race) && member( str, lower_case(race) ) > -1 )
+    return 1;
+  else
+    return 0;
+}
+
+mapping _query_material() {
+  mixed res;
+
+  if (mappingp(res=Query(P_MATERIAL)))
+    return res;
+  return ([MAT_MISC_LIVING:100]);
+}
+
diff --git a/std/living/helpers.c b/std/living/helpers.c
new file mode 100644
index 0000000..3d292c0
--- /dev/null
+++ b/std/living/helpers.c
@@ -0,0 +1,149 @@
+// MorgenGrauen MUDlib
+//
+// living/helpers.c -- (Query)Methoden fuer Hilfsobjekte, z.B. zum Tauchen
+//
+// $Id: moneyhandler.h,v 3.1 1997/02/12 13:29:09 Wargon Exp %
+
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <living/helpers.h>
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+public int RegisterHelperObject(object helper, int type, 
+                                string|closure callback) 
+{
+  // cb: closure auf die Callback-Funktion in previous_object()
+  closure cb;
+  // helpers: Mapping aller eingetragenen Helfer-Objekte
+  mapping helpers;
+
+  // Kein positiver Integerwert als Helfertyp uebergeben?
+  if ( !intp(type) || type < 1 )
+     raise_error(sprintf( "Wrong argument 1 to RegisterHelperObject(). "
+        "Expected positive <int>, got %O.\n", type));
+  // Kein Objekt vorhanden, an dem die Callback-Funktion gerufen werden soll?
+  if ( !objectp(helper) )
+    return HELPER_NO_CALLBACK_OBJECT;
+
+  // Funktionsname zum Zweck des Callbacks uebergeben?
+  if ( stringp(callback) ) {
+    // Dann Closure davon erstellen.
+    cb = symbol_function(callback, helper);
+    // Wenn das nicht klappt (zB weil die Funktion private ist), dann
+    // Fehler werfen und abbrechen.
+    if ( !closurep(cb) )
+      raise_error(sprintf("Error in RegisterHelperObject(): Unable to call "
+        "function %s in object %O.\n", callback, helper));
+  }
+  // Wenn schon eine Closure uebergeben wurde, dann diese direkt speichern.
+  else if ( closurep(callback) ) {
+    cb = callback;
+  }
+  // Weder Funktionsname, noch Closure, dann Fehler werfen und abbrechen.
+  else
+    raise_error(sprintf("Wrong argument 2 to RegisterHelperObject(). "
+      "Expected <string/closure>, got %O.\n",callback));
+
+  // Property auslesen und zwischenspeichern
+  helpers = QueryProp(P_HELPER_OBJECTS);
+  // Wenn die Prop leer ist, hier initialisieren
+  if ( !helpers ) {
+    helpers = ([type:({})]);
+  }
+  // Wenn der Typ noch nicht existiert, hier nachtragen.
+  else if ( !pointerp(helpers[type]) ) {
+    helpers[type] = ({});
+  }
+
+  // Closure eintragen, wenn noch nicht vorhanden
+  if ( member(helpers[type], cb)==-1 ) {
+    helpers[type] = helpers[type]+({cb});
+    SetProp(P_HELPER_OBJECTS, helpers);
+    return HELPER_SUCCESS;
+  }
+  else
+    return HELPER_ALREADY_LISTED;
+}
+
+public int UnregisterHelperObject(object helper, int type) {
+  if ( !intp(type) || type < 1 )
+     raise_error(sprintf( "Wrong argument 2 to UnregisterHelperObject(). "
+        "Expected positive <int>, got %O.\n", type));
+  if ( !objectp(helper) )
+    return HELPER_NO_CALLBACK_OBJECT;
+
+  mapping helpers = Query(P_HELPER_OBJECTS, F_VALUE);
+
+  if ( mappingp(helpers) ) {
+    foreach(closure cl: helpers[type]) {
+      if ( get_type_info(cl,2) == helper ) {
+        helpers[type] = helpers[type]-({cl});
+        return HELPER_SUCCESS;
+      }
+    }
+  }
+  return HELPER_NOTHING_TO_UNREGISTER;
+}
+
+// Querymethode fuer P_AQUATIC_HELPERS
+public mapping _query_lib_p_aquatic_helpers() {
+  mapping ret = ([]);
+  // eingetragene Callback-Closures auslesen
+  closure *helpers = 
+    ( Query(P_HELPER_OBJECTS, F_VALUE) || ([]) )[HELPER_TYPE_AQUATIC];
+  // Es sind gar keine Werte eingetragen? Dann gleich rausspringen.
+  if ( !pointerp(helpers) )
+    return ret;
+
+  // Nullelement substrahieren
+  helpers -= ({0});
+
+  if ( sizeof(helpers) ) {
+    // Mapping erstellen: Keys sind die Objekte, deren Closures eingetragen
+    // sind. Values sind die Rueckgabewerte der Closures,
+    // die dabei das Spielerobjekt und das abfragende Objekt uebergeben
+    // bekommen.
+    object *keys = map(helpers, #'get_type_info, 2);
+    int *vals = map(helpers, #'funcall, this_object(), previous_object(1));
+    ret = mkmapping(keys,vals);
+  }
+  return ret;
+}
+
+// Querymethode fuer P_AERIAL_HELPERS
+public mapping _query_lib_p_aerial_helpers() {
+  mapping ret = ([]);
+  // eingetragene Callback-Closures auslesen
+  closure *helpers = 
+    ( Query(P_HELPER_OBJECTS, F_VALUE) || ([]) )[HELPER_TYPE_AERIAL];
+
+  // Es sind gar keine Werte eingetragen? Dann gleich rausspringen.
+  if ( !pointerp(helpers) )
+    return ret;
+  
+  // Nullelement substrahieren
+  helpers -= ({0});
+
+  if ( sizeof(helpers) ) {
+    // Mapping erstellen: Keys sind die Objekte, deren Closures eingetragen
+    // sind. Values sind die Rueckgabewerte der Closures,
+    // die dabei das Spielerobjekt und das abfragende Objekt uebergeben
+    // bekommen.
+    object *keys = map(helpers,#'get_type_info, 2);
+    int *vals = map(helpers, #'funcall, this_object(), previous_object(1));
+    ret = mkmapping(keys,vals);
+  }
+  return ret;
+}
+
+// Querymethode fuer P_HELPER_OBJECTS
+public mapping _query_lib_p_helper_objects() {
+  return deep_copy(Query(P_HELPER_OBJECTS,F_VALUE));
+}
+
diff --git a/std/living/inventory.c b/std/living/inventory.c
new file mode 100644
index 0000000..e3027e4
--- /dev/null
+++ b/std/living/inventory.c
@@ -0,0 +1,66 @@
+// MorgenGrauen MUDlib
+//
+// container/inventory.c -- Umgang mit besonderen Objekten im Inventory
+//
+// $Id: inventory.c 6554 2007-10-17 22:45:53Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/container/inventory";
+
+#define NEED_PROTOTYPES
+
+#include <properties.h>
+#include <sensitive.h>
+#include <attributes.h>
+
+#define ME this_object()
+
+public void RemoveSensitiveObject(object ob) {
+  ::RemoveSensitiveObject(ob);
+  RemoveSensitiveObjectFromList(ob,SENSITIVE_ATTACK);
+  if (objectp(ob) && (ob->QueryProp(P_X_ATTR_MOD)   ||
+                      ob->QueryProp(P_X_HEALTH_MOD) ))
+  {
+    deregister_modifier(ob);
+    // Erst wenn das Objekt den Spieler verlassen konnte, die Attribute
+    // neu berechnen.
+    if (find_call_out("UpdateAttributes")==-1)
+      call_out("UpdateAttributes",0); 
+  }
+}
+
+public void InsertSensitiveObject(object ob, mixed arg) {
+  ::InsertSensitiveObject(ob,arg);
+  if (objectp(ob) && (ob->QueryProp(P_X_ATTR_MOD)   ||
+                      ob->QueryProp(P_X_HEALTH_MOD) ))
+  {
+    register_modifier(ob);
+    UpdateAttributes();
+  }
+}
+
+public void CheckSensitiveAttack(int dam, mixed dam_type, mixed spell, 
+                                 object enemy) {
+  mixed a,x;
+  int i;
+  
+  if (!pointerp(a=QueryProp(P_SENSITIVE_ATTACK)))
+    return;
+  if (!pointerp(dam_type))
+    dam_type=({dam_type});
+  for (i=sizeof(a)-1;i>=0;i--)
+    if (pointerp(x=a[i]) &&
+	dam>x[SENS_THRESHOLD] &&
+	member(dam_type,x[SENS_KEY])>=0 &&
+	objectp(x[SENS_OBJECT]) &&
+	environment(x[SENS_OBJECT])==ME &&
+	closurep(x[SENS_CLOSURE]))
+      funcall(x[SENS_CLOSURE],
+	      enemy,x[SENS_KEY],dam,
+	      spell,x[SENS_OPT]);
+}
+
diff --git a/std/living/life.c b/std/living/life.c
new file mode 100644
index 0000000..f3a72f5
--- /dev/null
+++ b/std/living/life.c
@@ -0,0 +1,1572 @@
+// MorgenGrauen MUDlib
+//
+// living/life.c -- life variables
+//
+// $Id: life.c 9426 2016-01-03 10:02:57Z Zesstra $
+
+// living object life variables
+//
+//  P_ALIGN         -- alignment value
+//  P_NPC           -- if living is an NPC
+//  P_HP            -- HitPoints
+//  P_SP            -- SpellPoints
+//  P_ALCOHOL       -- value of intoxication
+//  P_DRINK         -- value of soakness
+//  P_FOOD          -- value of stuffness
+//  P_XP            -- experience
+//  P_POISON        -- level of poison
+//  P_CORPSE        -- corpse-object
+//  P_DEAF          -- if living is deaf
+#pragma strong_types,save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <hook.h>
+#include <living/skills.h>
+#include <thing/properties.h>
+#include <living/life.h>
+#include <living/moving.h>
+#include <living/combat.h>
+#include <living/attributes.h>
+#include <thing/description.h>
+#include <thing/language.h>
+#undef NEED_PROTOTYPES
+#include <health.h>
+#include <defines.h>
+#include <new_skills.h>
+#include <scoremaster.h>
+#include <defuel.h>
+#include <properties.h>
+#include <events.h>
+#include <wizlevels.h>
+
+#define ALCOHOL_VALUE(n) n
+
+#include <debug_info.h> //voruebergehend
+
+
+// 'private'-Prototypen
+private void DistributeExp(object enemy, int exp_to_give);
+
+// Variablen
+nosave int delay_alcohol; /* time until next alcohol effect */
+nosave int delay_drink;   /* time until next drink effect */
+nosave int delay_food;    /* time until next food effect */
+nosave int delay_heal;    /* time until next heal effect */
+nosave int delay_sp;      /* time until next sp regeneration */
+nosave int delay_poison;  /* time until next poison effect */
+nosave int drop_poison;
+nosave mapping enemy_damage;
+nosave mapping hp_buffer;
+nosave mapping sp_buffer;
+nosave int remove_me;
+int nextdefueltimefood;
+int nextdefueltimedrink;
+
+
+protected void create()
+{
+  Set(P_GHOST, SAVE, F_MODE);
+  Set(P_FROG, SAVE, F_MODE);
+  Set(P_ALIGN, SAVE, F_MODE);
+  Set(P_HP, SAVE, F_MODE);
+  Set(P_SP, SAVE, F_MODE);
+  Set(P_XP, SAVE, F_MODE);
+  Set( P_LAST_XP, ({ "", 0 }) );
+  Set( P_LAST_XP, PROTECTED, F_MODE_AS );
+
+  Set(P_ALCOHOL, SAVE, F_MODE);
+  Set(P_DRINK, SAVE, F_MODE);
+  Set(P_FOOD, SAVE, F_MODE);
+  Set(P_POISON, SAVE, F_MODE);
+  Set(P_DEAF, SAVE, F_MODE);
+
+  SetProp(P_FOOD_DELAY, FOOD_DELAY);
+  SetProp(P_DRINK_DELAY, DRINK_DELAY);
+  SetProp(P_ALCOHOL_DELAY, ALCOHOL_DELAY);
+  SetProp(P_HP_DELAY,HEAL_DELAY);
+  SetProp(P_SP_DELAY,HEAL_DELAY);
+  SetProp(P_POISON_DELAY,POISON_DELAY);
+  // default fuer alle Lebewesen (NPC + Spieler):
+  SetProp(P_MAX_POISON, 10);
+  SetProp(P_CORPSE, "/std/corpse");
+
+  nextdefueltimefood=time()+QueryProp(P_DEFUEL_TIME_FOOD);
+  nextdefueltimedrink=time()+QueryProp(P_DEFUEL_TIME_DRINK);
+
+  enemy_damage=([:2 ]);
+  hp_buffer=([]);
+  sp_buffer=([]);
+
+  SetProp(P_DEFUEL_LIMIT_FOOD,1);
+  SetProp(P_DEFUEL_LIMIT_DRINK,1);
+  SetProp(P_DEFUEL_TIME_FOOD,1);
+  SetProp(P_DEFUEL_TIME_DRINK,1);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,1);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,1);
+
+  offerHook(H_HOOK_DIE,1);
+
+  offerHook(H_HOOK_FOOD,1);
+  offerHook(H_HOOK_DRINK,1);
+  offerHook(H_HOOK_ALCOHOL,1);
+  offerHook(H_HOOK_POISON,1);
+  offerHook(H_HOOK_CONSUME,1);
+}
+
+// Wenn der letzte Kampf lang her ist und das Lebewesen wieder vollgeheilt
+// ist, wird P_ENEMY_DAMAGE zurueckgesetzt.
+protected void ResetEnemyDamage() {
+  if (time() > QueryProp(P_LAST_COMBAT_TIME) + __RESET_TIME__ * 4
+      && QueryProp(P_HP) == QueryProp(P_MAX_HP))
+    enemy_damage=([:2 ]);
+}
+
+private void DistributeExp(object enemy, int exp_to_give) {
+  int total_damage, tmp, ex;
+  mapping present_enemies;
+
+  if ( exp_to_give<=0 )
+    return;
+
+  mapping endmg=deep_copy(enemy_damage);
+
+  // Mitglieder im Team des Killers bekommen:
+  //
+  //                  Gesamtanteil des Teams
+  // Eigenen Anteil + ----------------------
+  //                  Anzahl  Teammitglieder
+  // ---------------------------------------
+  //                2
+  //
+  object *inv = enemy->TeamMembers();
+  if ( pointerp(inv) )
+  {
+    present_enemies=m_allocate(sizeof(inv), 1);
+    foreach(object ob: inv)
+    {
+      if ( objectp(ob) && (environment(ob)==environment()) )
+      {
+        tmp=endmg[object_name(ob)];
+        total_damage+=tmp;
+        present_enemies[ob] = tmp/2;
+        m_delete(endmg,object_name(ob)); //s.u.
+      }
+    }
+    int mitglieder = sizeof(present_enemies);
+    if ( mitglieder )
+    {
+      tmp=total_damage/(2*mitglieder);
+      foreach(object ob, int punkte: &present_enemies)
+        punkte += tmp;
+    }
+  }
+  else {
+    // ohne Team wird trotzdem ein Mapping gebraucht. Da Groessenveraenderung
+    // rel. teuer sind, kann einfach mal fuer 3 Eintraege Platz reservieren.
+    present_enemies=m_allocate(3, 1);
+  }
+  // Und noch die Lebewesen im Raum ohne Team.
+  foreach(object ob: all_inventory(environment()))
+  {
+    if ( tmp=endmg[object_name(ob)] )
+    {
+      total_damage += tmp;
+      present_enemies[ob] = tmp;
+      m_delete(endmg,object_name(ob)); // Nur einmal pro Leben Punkte :)
+    }
+  }
+  if ( !total_damage )
+  {
+    enemy->AddExp(exp_to_give);
+  }
+  else
+  {
+    foreach(object ob, int damage: present_enemies)
+    {
+      if ( !objectp(ob) )
+        continue;
+      if ( query_once_interactive(ob) && ( !interactive(ob)
+              || (query_idle(ob)>600) ) )
+        continue;
+      //exp_to_give*present_enemies[i][1]/total_damage gibt bei viel Schaden
+      //einen numerical overflow. Daher muessen wir hier wohl doch
+      //zwischenzeitlich mit floats rechnen, auch wenn das 0-1 XP Verlust
+      //durch float->int-Konversion gibt. (ceil() lohnt sich IMHO nicht.)
+      ex = (int)(exp_to_give*((float)damage/(float)total_damage));
+      ob->AddExp(ex);
+    }
+  }
+}
+
+/*
+ * This function is called from other players when they want to make
+ * damage to us. But it will only be called indirectly.
+ * We return how much damage we received, which will
+ * change the attackers score. This routine is probably called from
+ * heart_beat() from another player.
+ * Compare this function to reduce_hit_points(dam).
+ */
+public int do_damage(int dam, object enemy)
+{ int hit_point,al,al2;
+
+  if ( extern_call()
+      && objectp(enemy)
+      && living(enemy)
+      && !QueryProp(P_ENABLE_IN_ATTACK_OUT))
+  {
+    al=time()-enemy->QueryProp(P_LAST_MOVE);
+    if (al<3)      // Erste Kampfrunde nach Betreten des Raumes?
+      dam/=(4-al); // Gegen Rein-Feuerball-Raus-Taktik
+  }
+
+  if ( QueryProp(P_GHOST) || QueryProp(P_NO_ATTACK) || (dam<=0)
+      || ( objectp(enemy)
+          && ( enemy->QueryProp(P_GHOST)
+              || enemy->QueryProp(P_NO_ATTACK) ) ) )
+    return 0;
+
+  hit_point = QueryProp(P_HP)-dam;
+
+  if ( QueryProp(P_XP) && objectp(enemy) )
+  {
+    if ( !QueryProp(P_NO_XP) )
+      enemy->AddExp(dam*(int)QueryProp(P_TOTAL_WC)/10);
+  }
+
+  if (living(enemy)) {
+      string enname = object_name(enemy);
+      // Hmpf. Blueprints sind doof. Die Chance ist zwar gering, aber koennte
+      // sein, dass ein Unique-NPC mit zwei verschiedenen Spielern am gleichen
+      // NPC metzelt.
+      // TODO: MHmm. wie gross ist das Risiko wirklich?
+      //if (!clonep(enemy))
+      //    enname = enname + "_" + to_string(object_time(enemy));
+      // nur wenn gegner NPC ist und noch nicht drinsteht: Daten aus
+      // P_HELPER_NPC auswerten
+      if (!member(enemy_damage,enemy) && !query_once_interactive(enemy)) {
+          mixed helper = enemy->QueryProp(P_HELPER_NPC);
+          if (pointerp(helper) && objectp(helper[0]))
+              enemy_damage[enname,1] = helper[0];
+      }
+      enemy_damage[enname,0]+=dam;
+  }
+
+  SetProp(P_HP, hit_point);
+
+  if ( hit_point<0 )
+  {
+    //TODO: Warum nicht das ganze Zeug ins die() verlegen?
+    if ( enemy )
+    {
+      enemy->StopHuntFor(ME,1);
+      if ( !QueryProp(P_NO_XP) )
+        DistributeExp(enemy,QueryProp(P_XP)/100);
+      if ( !query_once_interactive(ME) )
+        log_file ("NPC_XP", sprintf(
+	    "[%s] %s, XP: %d, HP*WC: %d, Killer: %s\n",
+	    dtime(time()), object_name(ME), (QueryProp(P_XP)/100),
+                  QueryProp(P_TOTAL_WC)*QueryProp(P_MAX_HP)/10,
+                  enemy->name()||"NoName" ));
+      al = QueryProp(P_ALIGN)/50 + enemy->QueryProp(P_ALIGN)/200;
+      if (al>20)
+        al=20;
+      else if(al<-20)
+        al=-20;
+      enemy->SetProp(P_ALIGN,enemy->QueryProp(P_ALIGN)-al);
+    }
+    SetProp(P_KILLER, enemy);
+    
+    die();
+  }
+  return dam;
+}
+
+
+private void _transfer( object *obs, string|object dest, int flag )
+{   int i;
+
+    i = sizeof(obs);
+
+    // Eine Schleife ist zwar langsamer als filter() o.ae., aber
+    // selbst mit einer noch so schnellen Loesung kann leider nicht
+    // ausgeschlossen werden, dass irgendwo ein too-long-eval-Bug dazwischen
+    // kommt. Dazu sind die Kaempfe mit Gilden-NPCs etc. einfach zu teuer ...
+    // Pruefung auf zerstoerte Objekte, da einige sich evtl. im NotifyPlayerDeath() 
+    // zerstoeren.
+   while ( i && get_eval_cost() > 300000 )
+        if ( objectp(obs[--i]) && !obs[i]->QueryProp(P_NEVERDROP) )
+        // Jetzt wird's noch etwas teurer mit catch() - aber manche Sachen
+        // duerfen einfach nicht buggen
+            catch( obs[i]->move( dest, flag );publish );
+
+    if ( i > 0 )
+        // Zuviel Rechenzeit verbraten, es muessen noch Objekte bewegt werden
+        call_out( #'_transfer, 0, obs[0..i-1], dest, flag );
+    else {
+        if ( remove_me )
+            remove();
+    }
+}
+
+
+public varargs void transfer_all_to( string|object dest, int isnpc ) {
+    int flags;
+    object *obs;
+
+    if ( !objectp(ME) )
+        return;
+
+    // Das Flag "isnpc" ist fuer NPCs gedacht. Deren Ausruestung darf nicht
+    // mit M_NOCHECK bewegt werden, da Spieler das bei Nicht-Standard-Leichen
+    // sonst u.U. ausnutzen koennten.
+    if ( isnpc )
+        flags = M_SILENT;
+    else
+        flags = M_SILENT|M_NOCHECK;
+
+    obs = all_inventory(ME) || ({});
+
+    // unnoetig, weil _transfer() auch auf P_NEVERDROP prueft. Zesstra
+    //obs -= filter_objects( obs, "QueryProp", P_NEVERDROP );
+
+    _transfer( obs, dest, flags );
+}
+
+
+protected varargs void create_kill_log_entry(string killer, object enemy) {
+  int level,lost_exp;
+
+  if ( (level=QueryProp(P_LEVEL))<20 || !IS_SEER(ME) )
+    lost_exp = QueryProp(P_XP)/3;
+  else
+    lost_exp = QueryProp(P_XP)/(level-17);
+  
+  log_file("KILLS",sprintf("%s %s (%d,%d) %s\n", strftime("%e %b %H:%M"),
+               capitalize(REAL_UID(ME)), level, lost_exp/1000, killer)); 
+}
+
+// Liefert im Tod (nach dem toetenden do_damage()) das Spielerobjekt, was den
+// Tod wohl zu verantworten hat, falls es ermittelt werden kann. Es werden vor
+// allem registrierte Helfer-NPC und einige Sonderobjekte beruecksichtigt.
+protected object get_killing_player()
+{
+  object killer=QueryProp(P_KILLER);
+  // koennte sein, wenn ausserhalb des Todes gerufen oder eine Vergiftung uns
+  // umgebracht hat.
+  if (!objectp(killer))
+    return 0;
+
+  while (killer && !query_once_interactive(killer))
+    killer = killer->QueryUser();
+
+  return killer;
+}
+
+protected object GiveKillScore(object pl, int npcnum)
+{
+  // Stufenpunkt fuer den Kill vergeben.
+  // Falls der Killer den Punkt schon hat, wird
+  // zufaellig ein Mitglied seines Teams ausgewaehlt
+  // und diesem der Punkt gegeben.
+  object *obs,ob;
+  mixed *fr;
+  int i,j,sz;
+
+  if ( pointerp(obs=pl->TeamMembers()) && (member(obs,pl)>=0) )
+  {
+    if ( !pointerp(fr=pl->PresentTeamRows())
+        || !sizeof(fr)
+        || !pointerp(fr=fr[0])) // Erste Reihe des Teams
+      fr=({});
+    fr-=({pl,0});
+    obs-=({pl,0});
+    obs-=fr;
+    i=sz=sizeof(obs); // restliche Teammitglieder in zufaelliger Reihenfolge:
+    for ( --i ; i>=0 ; i-- )
+    {
+      j=random(sz);
+      ob=obs[j];
+      obs[j]=obs[0];
+      obs[0]=ob;
+    }
+    i=sz=sizeof(fr);  // Erste Reihe in zufaelliger Reihenfolge:
+    for ( --i ; i>=0 ; i-- )
+    {
+      j=random(sz);
+      ob=fr[j];
+      fr[j]=fr[0];
+      fr[0]=ob;
+    }
+
+    obs+=fr;     // Erste Reihe wird vor Rest getestet
+    obs+=({pl}); // Killer wird als erstes getestet
+  }
+  else
+  {
+    obs=({pl});
+  }
+  for ( i=sizeof(obs)-1 ; i>=0 ; i-- )
+    if ( objectp(ob=obs[i] )
+        && interactive(ob)     // Nur netztot dabei stehen gilt nicht :)
+        && query_idle(ob)<600  // gegen Leute die sich nur mitschleppen lassen
+        && environment(ob)==environment(pl) // Nur anwesende Teammitglieder
+        && !IS_LEARNER(ob)
+//        && !ob->QueryProp(P_TESTPLAYER)
+        && !(SCOREMASTER->HasKill(ob,ME)) )
+      return SCOREMASTER->GiveKill(ob,npcnum),ob;
+
+  return SCOREMASTER->GiveKill(pl,npcnum),pl;
+}
+
+// zum ueberschreiben in Spielern
+public int death_suffering() {
+  return 0; // NPC haben keine Todesfolgen
+}
+
+// kein 2. Leben fuer Nicht-Spieler. ;-)
+varargs protected int second_life( object corpse ) {
+    return 0;
+}
+
+public varargs void die( int poisondeath, int extern )
+{   object corpse;
+    string die_msg, tmp;
+    mixed res;
+    mixed hookData;
+    mixed hookRes;
+
+    if ( !objectp(this_object()) || QueryProp(P_GHOST) )
+        return; // Ghosts dont die ...
+
+    // direkt von extern aufgerufen und nicht ueber heart_beat() oder
+    // do_damage() hierher gelangt?
+    if (extern_call() && previous_object() != this_object()) {
+      extern=1;
+      SetProp(P_KILLER, previous_object());
+    }
+
+    if ( res = QueryProp(P_TMP_DIE_HOOK) ){
+        if ( pointerp(res) && sizeof(res)>=3
+            && intp(res[0]) && time()<res[0]
+            && objectp(res[1]) && stringp(res[2]) )
+        {
+            if ( res = call_other( res[1], res[2], poisondeath ) ) {
+              SetProp(P_KILLER,0);
+              return;
+            }
+        }
+        else
+            SetProp(P_TMP_DIE_HOOK,0);
+    }
+
+    // trigger die hook
+    hookData=poisondeath;
+    hookRes=HookFlow(H_HOOK_DIE,hookData);
+    if (pointerp(hookRes) && sizeof(hookRes)>H_RETDATA){
+      if(hookRes[H_RETCODE]==H_CANCELLED) {
+        SetProp(P_KILLER,0);
+        return;
+      }
+      else if (hookRes[H_RETCODE]==H_ALTERED)
+          poisondeath = hookRes[H_RETDATA];
+    }
+
+    if ( IS_LEARNING(ME) && query_once_interactive(ME) ){
+       tell_object( ME, "Sei froh dass Du unsterblich bist, sonst waere es "
+                        "eben Dein Ende gewesen.\n");
+       SetProp(P_KILLER,0);
+       return;
+    }
+
+    // Gegner befrieden.
+    map_objects( QueryEnemies()[0], "StopHuntFor", ME, 1 );
+    StopHuntingMode(1);
+
+    // Falls die() direkt aufgerufen wurde und dies ein Spieler ist, muss das
+    // die() noch Eintraege in /log/KILLS via create_kill_log_entry bzw. in
+    // /log/KILLER erstellen.
+    if ( query_once_interactive(ME) && extern )
+    {
+      object killer = QueryProp(P_KILLER) 
+                     || previous_object() || this_interactive() || this_player();
+      if ( killer && !query_once_interactive(killer) )
+      {
+          tmp = explode( object_name(killer), "#")[0] + " (direkt !)";
+
+          create_kill_log_entry( tmp + " (" + REAL_UID(killer) + ")", killer );
+      }
+      else if ( killer && !QueryProp(P_TESTPLAYER) && !IS_LEARNER(ME) )
+      {
+          log_file( "KILLER", sprintf( "%s %s (%d/%d) toetete %s (%d/%d)\n",
+                                       ctime(time()),
+                                       capitalize(getuid(killer)),
+                                       query_wiz_level(killer),
+                                       killer->QueryProp(P_LEVEL),
+                                       capitalize(getuid(ME)),
+                                       query_wiz_level(ME),
+                                       QueryProp(P_LEVEL) ) );
+
+          killer->SetProp( P_KILLS, -1 );
+      }
+    }
+
+  // Bei NPC EKs vergeben und ggf. in der Gilde des Killers und im Raum
+  // NPC_Killed_By() rufen.
+  if ( !query_once_interactive(ME) )
+  {
+    object killer = ((object) QueryProp(P_KILLER)) || previous_object() ||
+      this_interactive() || this_player();
+
+    if ( killer && query_once_interactive(killer) )
+    {
+      if (stringp(res=killer->QueryProp(P_GUILD))
+          && objectp(res=find_object("/gilden/"+res)))
+        res->NPC_Killed_By(killer);
+
+      if (environment())
+          environment()->NPC_Killed_By(killer);
+
+      res = QueryProp(P_XP);
+      res = (res < SCORE_LOW_MARK) ? 0 : ((res > SCORE_HIGH_MARK) ? 2 : 1);
+      if ( !QueryProp(P_NO_SCORE) && !IS_LEARNER(killer) &&
+           // !killer->QueryProp(P_TESTPLAYER) &&
+           pointerp( res = SCOREMASTER->QueryNPC(res)) )
+        GiveKillScore( killer, res[0] );
+    }
+  }
+
+  if( !(die_msg = QueryProp(P_DIE_MSG)) )
+    if (QueryProp(P_PLURAL))
+      die_msg = " fallen tot zu Boden.\n";
+    else
+      die_msg = " faellt tot zu Boden.\n";
+
+  if ( poisondeath )
+  {
+      Set( P_LAST_DAMTYPES, ({ DT_POISON }) );
+      Set( P_LAST_DAMTIME, time() );
+      Set( P_LAST_DAMAGE, 1 );
+      die_msg = " wird von Gift hinweggerafft und kippt um.\n";
+  }
+
+  say( capitalize(name(WER,1)) + die_msg );
+
+  // Wenn keine Leiche, dann Kram ins Env legen.
+  if ( QueryProp(P_NOCORPSE) || !(tmp = QueryProp(P_CORPSE))
+      || catch(corpse = clone_object(tmp);publish) 
+      || !objectp(corpse) )
+  {
+      // Magier oder Testspieler behalten ihre Ausruestung.
+      // Sonst kaemen u.U. Spieler an Magiertools etc. heran
+      if ( !(IS_LEARNER(ME) || (tmp = Query(P_TESTPLAYER)) &&
+             (!stringp(tmp) || IS_LEARNER( lower_case(tmp) ))) )
+          transfer_all_to( environment(), 0 );
+      else
+          // Aber sie ziehen sich aus.
+          filter_objects(QueryProp(P_ARMOURS),"DoUnwear",M_NOCHECK,0);
+  }
+  else
+  // sonst in die Leiche legen.
+  {
+      corpse->Identify(ME);
+      corpse->move( environment(), M_NOCHECK|M_SILENT );
+      // Magier oder Testspieler behalten ihre Ausruestung.
+      // Sonst kaemen u.U. Spieler an Magiertools etc. heran
+      if ( !(IS_LEARNER(ME) || (tmp = Query(P_TESTPLAYER)) &&
+             (!stringp(tmp) || IS_LEARNER( lower_case(tmp) ))) )
+          transfer_all_to( corpse, !query_once_interactive(ME) );
+      else
+          // Aber sie ziehen sich aus.
+          filter_objects(QueryProp(P_ARMOURS),"DoUnwear",M_NOCHECK,0);
+  }
+
+  if ( query_once_interactive(ME) ) {
+      Set( P_DEADS, Query(P_DEADS) + 1 );
+      // Spieler-Tod-event ausloesen
+      EVENTD->TriggerEvent(EVT_LIB_PLAYER_DEATH, ([
+      E_OBJECT: ME, E_PLNAME: getuid(ME),
+      E_ENVIRONMENT: environment(), E_TIME: time(),
+      P_KILLER: QueryProp(P_KILLER),
+      P_LAST_DAMAGE: QueryProp(P_LAST_DAMAGE),
+      P_LAST_DAMTYPES: copy(QueryProp(P_LAST_DAMTYPES)),
+      E_EXTERNAL_DEATH: extern,
+      E_POISON_DEATH: poisondeath, 
+      E_CORPSE: (objectp(corpse)?corpse:0) ]) );
+  }
+  else {
+      // NPC-Todes-Event ausloesen. Div. Mappings/Arrays werden nicht kopiert,
+      // weil der NPC ja jetzt eh zerstoert wird.
+      mapping data = ([
+            E_OBNAME: object_name(ME),
+            E_ENVIRONMENT: environment(), E_TIME: time(),
+            P_NAME: QueryProp(P_NAME),
+            P_KILLER: QueryProp(P_KILLER),
+            P_ENEMY_DAMAGE: QueryProp(P_ENEMY_DAMAGE),
+            P_LAST_DAMAGE: QueryProp(P_LAST_DAMAGE),
+            P_LAST_DAMTYPES: QueryProp(P_LAST_DAMTYPES),
+            E_EXTERNAL_DEATH: extern,
+            E_POISON_DEATH: poisondeath,
+            E_CORPSE: (objectp(corpse)?corpse:0),
+            P_XP: QueryProp(P_XP),
+            P_ATTRIBUTES: QueryProp(P_ATTRIBUTES),
+            P_MAX_HP: QueryProp(P_MAX_HP),
+            P_HANDS: QueryProp(P_HANDS),
+            P_ALIGN: QueryProp(P_ALIGN),
+            P_RACE: QueryProp(P_RACE),
+            P_CLASS: QueryProp(P_CLASS),
+            ]);
+      EVENTD->TriggerEvent(EVT_LIB_NPC_DEATH(""), data);
+      EVENTD->TriggerEvent(
+          EVT_LIB_NPC_DEATH(load_name(ME)), data);
+  }
+
+  // transfer_all_to() ist evtl. (wenn zuviele Objekte bewegt werden mussten)
+  // noch nicht ganz fertig und wird per call_out() den Rest erledigen.
+  // Sollte die Leiche dann nicht mehr existieren, verbleiben die restlichen
+  // Objekte im Spieler.
+  // Es bleiben aber auf jeden Fall noch rund 300k Eval-Ticks ueber, damit
+  // kein Spieler dank "evalcost too high" ungeschoren davon kommt.
+  if ( !(second_life(corpse)) )
+  {
+      Set( P_GHOST, 1 ); // Fuer korrekte Ausgabe auf Teamkanal.
+
+      if ( find_call_out(#'_transfer) == -1 )
+          // Falls kein call_out() mehr laeuft, sofort destructen ...
+          remove();
+      else
+          // ... ansonsten vormerken
+          remove_me = 1;
+  }
+}
+
+public void heal_self(int h)
+{
+  if ( h<=0 )
+    return;
+  SetProp(P_HP, QueryProp(P_HP)+h);
+  SetProp(P_SP, QueryProp(P_SP)+h);
+}
+
+
+//--------------------------------------------------------------------------
+//
+//   int defuel_food( /* void */ )
+//
+//   Enttankt den Spieler um einen gewissen Essens-Wert.
+//   Sollte nur von Toiletten aufgerufen werden.
+//
+//--------------------------------------------------------------------------
+public int defuel_food()
+{
+  int food;
+
+  food=QueryProp(P_FOOD);
+
+// wenn spieler kein food hat: return 0
+  if ( !food )
+   return NO_DEFUEL;
+
+// wenn spieler unter enttank-grenze: return -1
+  if ( food < QueryProp(P_DEFUEL_LIMIT_FOOD) )
+   return DEFUEL_TOO_LOW;
+
+// wenn letztes enttanken nicht lange genug zurueckliegt: return -2
+  if ( time() < nextdefueltimefood )
+   return DEFUEL_TOO_SOON;
+
+  food=to_int(((food*QueryProp(P_DEFUEL_AMOUNT_FOOD))/2));
+  food+=random(food);
+
+// sicherheitshalber
+  if ( food > QueryProp(P_FOOD) )
+   food=QueryProp(P_FOOD);
+
+  SetProp(P_FOOD,(QueryProp(P_FOOD)-food));
+
+  nextdefueltimefood=time()+QueryProp(P_DEFUEL_TIME_FOOD);
+
+  return food;
+}
+
+
+//--------------------------------------------------------------------------
+//
+//   int defuel_drink( /* void */ )
+//
+//   Enttankt den Spieler um einen gewissen Fluessigkeits-Wert.
+//   Gleichzeitig wird eine gewisse Menge Alkohol reduziert.
+//   Sollte nur von Toiletten aufgerufen werden.
+//
+//--------------------------------------------------------------------------
+public int defuel_drink()
+{
+  int alc, drink;
+
+  drink=QueryProp(P_DRINK);
+
+// wenn spieler kein drink hat: return 0
+  if ( !drink )
+   return NO_DEFUEL;
+
+// wenn spieler unter enttank-grenze: return -1
+  if ( drink < QueryProp(P_DEFUEL_LIMIT_DRINK) )
+   return DEFUEL_TOO_LOW;
+
+// wenn letztes enttanken nicht lange genug zurueckliegt: return -2
+  if ( time() < nextdefueltimedrink )
+   return DEFUEL_TOO_SOON;
+    
+  drink=to_int(((drink*QueryProp(P_DEFUEL_AMOUNT_DRINK))/2));
+  drink+=random(drink);
+
+// sicherheitshalber
+  if ( drink > QueryProp(P_DRINK) )
+   drink=QueryProp(P_DRINK);
+
+  SetProp(P_DRINK,(QueryProp(P_DRINK)-drink));
+
+// jedes fluessige Enttanken macht auch etwas nuechterner :^)
+// bei sehr kleinen Mengen enttankt man keinen Alkohol
+// ansonsten in Abhaengigkeit von enttankter Menge, P_ALCOHOL und P_WEIGHT
+
+  if ( drink > 9 && QueryProp(P_ALCOHOL) > 0 )
+   {
+    alc=(to_int(exp(log(1.1)*(drink)))*
+         to_int(exp(log(0.67)*(QueryProp(P_ALCOHOL)))))/
+         (QueryProp(P_MAX_DRINK)*QueryProp(P_MAX_ALCOHOL))*
+         (to_int(QueryProp(P_WEIGHT)/1000));
+
+     SetProp(P_ALCOHOL,QueryProp(P_ALCOHOL)-(alc+random(alc)));
+   }
+
+  nextdefueltimedrink=time()+QueryProp(P_DEFUEL_TIME_DRINK);
+ 
+  return drink;
+}
+
+
+public void reduce_spell_points(int h)
+{
+  SetProp(P_SP, QueryProp(P_SP)-h);
+}
+
+public void restore_spell_points(int h)
+{
+  SetProp(P_SP, QueryProp(P_SP)+h);
+}
+
+/* Reduce hitpoints. Log who is doing it. */
+public int reduce_hit_points(int dam)
+{ object o;
+  int i;
+
+#ifdef LOG_REDUCE_HP
+  if (this_player()!=ME)
+  {
+    log_file("REDUCE_HP", name()+" by ");
+    if(!this_player()) log_file("REDUCE_HP","?\n");
+    else {
+      log_file("REDUCE_HP",this_player()->name());
+      o=previous_object();
+      if (o)
+        log_file("REDUCE_HP", " " + object_name(o) + ", " +
+                 o->name(WER,0) + " (" + creator(o) + ")\n");
+      else
+        log_file("REDUCE_HP", " ??\n");
+    }
+  }
+#endif
+  if ((i=QueryProp(P_HP)) <= dam)
+    return SetProp(P_HP,1);
+  return SetProp(P_HP, i - dam);
+}
+
+public int restore_hit_points(int heal)
+{
+  return reduce_hit_points(-heal);
+}
+
+public varargs int drink_alcohol(int strength,int testonly, string mytext)
+{ int alc,add,res;
+
+  add=ALCOHOL_VALUE(strength);
+  res=UseSkill(SK_BOOZE,([
+      SI_SKILLARG : add,
+      SI_TESTFLAG : 1])); // Kann der Spieler gut saufen?
+  if (intp(res) && res>0) add=res;
+  alc=QueryProp(P_ALCOHOL)+add;
+  if ((alc >= QueryProp(P_MAX_ALCOHOL)) && !IS_LEARNING(this_object())){
+    if(!testonly)
+      tell_object(ME,mytext||"So ein Pech, Du hast alles verschuettet.\n");
+    return 0;
+  }
+  if(testonly)return 1;
+  UseSkill(SK_BOOZE,([ SI_SKILLARG : ALCOHOL_VALUE(strength) ]));
+  if(alc < 0) alc = 0;
+  if(!alc) tell_object(ME, "Du bist stocknuechtern.\n");
+  SetProp(P_ALCOHOL, alc);
+  return 1;
+}
+
+public varargs int drink_soft(int strength, int testonly, string mytext)
+{ int soaked;
+
+  soaked = QueryProp(P_DRINK);
+  if((soaked + strength > QueryProp(P_MAX_DRINK)) &&
+     !IS_LEARNING(this_object())){
+    if(!testonly)
+     tell_object(ME, mytext||
+       "Nee, so viel kannst Du momentan echt nicht trinken.\n" );
+    return 0;
+  }
+  if(testonly)return 1;
+  if((soaked += DRINK_VALUE(strength)) < 0) soaked = 0;
+  if(!soaked) tell_object(ME, "Dir klebt die Zunge am Gaumen.\n");
+  SetProp(P_DRINK, soaked);
+  return 1;
+}
+
+public varargs int eat_food(int strength, int testonly, string mytext)
+{ int stuffed;
+
+  stuffed = QueryProp(P_FOOD);
+  if ((stuffed + strength > QueryProp(P_MAX_FOOD)) &&
+      !IS_LEARNING(this_object()))
+  {
+    if(!testonly)
+        tell_object(ME,
+            mytext || "Das ist viel zu viel fuer Dich! Wie waers mit etwas "
+            "leichterem?\n");
+    return 0;
+  }
+  if(testonly)return 1;
+  stuffed += FOOD_VALUE(strength);
+  if(stuffed < 0) stuffed = 0;
+  if(!stuffed) tell_object(ME, "Was rumpelt denn da in Deinem Bauch?\n");
+  SetProp(P_FOOD, stuffed);
+  return 1;
+}
+
+public int buffer_hp(int val,int rate)
+{
+  int dif;
+
+  if(val<=0 || rate<=0)return 0;
+  if(val < rate)rate = val;
+  if(rate>20) rate=20;
+
+  /* Check for BufferOverflow */
+  if((dif=(hp_buffer[0]+val)-QueryProp(P_MAX_HP)) > 0)val-=dif;
+  if(val<=0)return 0;
+
+  hp_buffer[0] += val;
+  hp_buffer[1+rate] += val;
+  if(rate > hp_buffer[1])hp_buffer[1] = rate;
+
+  return hp_buffer[0];
+}
+
+public int buffer_sp(int val,int rate)
+{
+  int dif;
+
+  if(val<=0 || rate<=0)return 0;
+  if(val < rate)rate = val;
+  if(rate>20) rate=20;
+
+  /* Check for BufferOverflow */
+  if((dif=(sp_buffer[0]+val)-QueryProp(P_MAX_SP)) > 0)val-=dif;
+  if(val<=0)return 0;
+
+  sp_buffer[0] += val;
+  sp_buffer[1+rate] += val;
+  if(rate > sp_buffer[1])sp_buffer[1] = rate;
+
+  return sp_buffer[0];
+}
+
+protected void update_buffers()
+{ int i, rate, max;
+
+  rate=0;
+  max=0;
+  for(i=1;i<=20;i++){
+    if(member(hp_buffer, i+1))
+      if(hp_buffer[i+1]<=0)
+        hp_buffer = m_delete(hp_buffer,i+1);
+      else{
+        max+=hp_buffer[i+1];
+        rate=i;
+      }
+  }
+
+  hp_buffer[0]=max;
+  hp_buffer[1]=rate;
+  rate=0;
+  max=0;
+  for(i=1;i<=20;i++){
+    if(member(sp_buffer, i+1))
+      if(sp_buffer[i+1]<=0)
+        sp_buffer = m_delete(sp_buffer,i+1);
+      else{
+        max+=sp_buffer[i+1];
+        rate=i;
+      }
+  }
+  sp_buffer[0]=max;
+  sp_buffer[1]=rate;
+}
+
+public int check_timed_key(string key) {
+
+  // keine 0 als key (Typ wird per RTTC geprueft)
+  if (!key)
+    return 0;
+  mapping tmap=Query(P_TIMING_MAP, F_VALUE);
+  if (!mappingp(tmap)) {
+    tmap=([]);
+    Set(P_TIMING_MAP, tmap, F_VALUE);
+  }
+  // Wenn key noch nicht abgelaufen, Ablaufzeitpunkt zurueckgeben.
+  // Sonst 0 (key frei)
+  return (time() < tmap[key]) && tmap[key];
+}
+
+public int check_and_update_timed_key(int duration,string key) {
+
+  // wenn key noch gesperrt, die zeit der naechsten Verfuegbarkeit
+  // zurueckgeben.
+  int res = check_timed_key(key);
+  if (res) {
+    return res;
+  }
+
+  // duration <= 0 ist unsinnig. Aber key ist nicht mehr gesperrt, d.h. time()
+  // ist ein sinnvoller Rueckgabewert.
+  if (duration <= 0)
+    return time();
+ 
+  mapping tmap = Query(P_TIMING_MAP,F_VALUE);
+  tmap[key]=time()+duration;
+  
+  // speichern per SetProp() unnoetig, da man das Mapping direkt aendert,
+  // keine Kopie.
+  //SetProp(P_TIMING_MAP, tmap);
+  
+  return -1; // Erfolg.
+}
+
+protected void expire_timing_map() {
+  
+  mapping tmap = Query(P_TIMING_MAP, F_VALUE);
+  if (!mappingp(tmap) || !sizeof(tmap))
+    return;
+  foreach(string key, int endtime: tmap) {
+    if (endtime < time())
+      m_delete(tmap, key);
+  }
+  // speichern per SetProp() unnoetig, da man das Mapping direkt aendert,
+  // keine Kopie.
+}
+
+protected void heart_beat()
+{
+    if ( !this_object() )
+        return;
+
+    attribute_hb();
+
+    // Als Geist leidet man nicht unter so weltlichen Dingen wie
+    // Alkohol, Gift&Co ...
+    if ( QueryProp(P_GHOST) )
+        return;
+
+    int hpoison = QueryProp(P_POISON);
+    int rlock = QueryProp(P_NO_REGENERATION);
+    int hp = QueryProp(P_HP);
+    int sp = QueryProp(P_SP);
+    int alc;
+
+    // Wenn Alkohol getrunken: Alkoholauswirkungen?
+    if ( (alc = QueryProp(P_ALCOHOL)) && !random(40) ){
+        int n;
+        string gilde;
+        object ob;
+
+        n = random( 5 * (alc - 1)/QueryProp(P_MAX_ALCOHOL) );
+
+        switch (n){
+        case ALC_EFFECT_HICK:
+            say( capitalize(name( WER, 1 )) + " sagt: <Hick>!\n" );
+            write( "<Hick>! Oh, Tschuldigung.\n" );
+            break;
+            
+        case ALC_EFFECT_STUMBLE:
+            say( capitalize(name( WER, 1 )) + " stolpert ueber " +
+                 QueryPossPronoun( FEMALE, WEN ) + " Fuesse.\n" );
+            write( "Du stolperst.\n" );
+            break;
+            
+        case ALC_EFFECT_LOOKDRUNK:
+            say( capitalize(name( WER, 1 )) + " sieht betrunken aus.\n" );
+            write( "Du fuehlst Dich benommen.\n" );
+            break;
+            
+        case ALC_EFFECT_RUELPS:
+            say( capitalize(name( WER, 1 )) + " ruelpst.\n" );
+            write( "Du ruelpst.\n" );
+            break;
+        }
+       
+        // Gilde und Environment informieren ueber Alkoholauswirkung.
+        if ( stringp(gilde = QueryProp(P_GUILD))
+             && objectp(ob = find_object( "/gilden/" + gilde )) )
+            ob->InformAlcoholEffect( ME, n, ALC_EFFECT_AREA_GUILD );
+        
+        if ( environment() )
+            environment()->InformAlcoholEffect( ME, n, ALC_EFFECT_AREA_ENV );
+    }
+    
+    // Alkohol abbauen und etwas extra heilen, falls erlaubt.
+    if ( alc && (--delay_alcohol < 0)
+         && !(rlock & NO_REG_ALCOHOL) ){
+
+        SetProp( P_ALCOHOL, alc - 1 );
+        
+        if ( !hpoison ){
+            hp++;
+            sp++;
+        }
+        
+        delay_alcohol = QueryProp(P_ALCOHOL_DELAY);
+    }
+
+    // P_DRINK reduzieren, falls erlaubt.
+    if ( (--delay_drink < 0) && !(rlock & NO_REG_DRINK) ){
+        delay_drink = QueryProp(P_DRINK_DELAY);
+        SetProp( P_DRINK, QueryProp(P_DRINK) - 1 );
+    }
+
+    // P_FOOD reduzieren, falls erlaubt.
+    if ( (--delay_food < 0) && !(rlock & NO_REG_FOOD) ){
+        delay_food = QueryProp(P_FOOD_DELAY);
+        SetProp( P_FOOD, QueryProp(P_FOOD) - 1 );
+    }
+
+    // Regeneration aus dem HP-Puffer
+    // Hierbei wird zwar nur geheilt, wenn das erlaubt ist, aber der Puffer
+    // muss trotzdem abgearbeitet/reduziert werden, da Delfen sonst eine
+    // mobile Tanke kriegen. (Keine Heilung im Hellen, Puffer bleibt sonst
+    // konstant und kann bei Bedarf ueber dunkelmachende Items abgerufen
+    // werden.)
+    int val;
+    if (hp_buffer[0]) {
+        int rate = hp_buffer[1];
+        val = hp_buffer[rate + 1];
+    
+        if ( val > rate )
+            val = rate;
+        hp_buffer[0] -= val;
+        hp_buffer[rate + 1] -= val;
+        if ( hp_buffer[rate + 1] <= 0 )
+            update_buffers();
+    }
+    // Jetzt Regeneration aus dem Puffer durchfuehren, aber nur wenn erlaubt.
+    if ( val && !(rlock & NO_REG_BUFFER_HP) )
+        hp += val; 
+    // normales Heilen, falls keine Regeneration aus dem Puffer erfolgte und
+    // es erlaubt ist.
+    else if ( (--delay_heal < 0) && !(rlock & NO_REG_HP) ){
+        delay_heal = QueryProp(P_HP_DELAY);
+        if ( !hpoison )
+            hp++;
+    }
+
+    // Gleiches Spiel jetzt fuer den SP-Puffer (s.o.)
+    val=0;
+    if ( sp_buffer[0] ) {
+        int rate = sp_buffer[1];
+        val = sp_buffer[rate + 1];
+        
+        if ( val > rate )
+            val = rate;
+        
+        sp_buffer[0] -= val;
+        sp_buffer[rate + 1] -= val;
+ 
+        if ( sp_buffer[rate + 1] <= 0 )
+            update_buffers();
+    }
+    // Regeneration erlaubt?
+    if ( val && !(rlock & NO_REG_BUFFER_SP) )
+         sp += val;
+    // Wenn nicht, normales Hochideln versuchen.
+    else if ( (--delay_sp < 0) && !(rlock & NO_REG_SP) ){
+        delay_sp = QueryProp(P_SP_DELAY);
+        if ( !hpoison )
+            sp++;
+    }
+
+    if ( hpoison && (interactive(ME) || !query_once_interactive(ME)) ){
+        // Vanion, 26.10.03
+        // Wenn _set_poison() per SET_METHOD ueberschrieben wird, kann
+        // nicht sichergestellt werden, dass poison immer groesser 0 ist
+        // Daher muss hier ein Test rein, so teuer das auch ist :(
+        if (--hpoison < 0)
+          hpoison=0;
+        
+        if ( --delay_poison < 0 ){
+            delay_poison = QueryProp(P_POISON_DELAY)
+                + random(POISON_MERCY_DELAY);
+            hp -= hpoison;
+
+            if ( hp < 0 ){
+                tell_object( ME, "Oh weh - das Gift war zuviel fuer Dich!\n"
+                             + "Du stirbst.\n" );
+                
+                if ( query_once_interactive(ME) ){
+                    create_kill_log_entry( "Vergiftung", 0 );
+                    
+                    // Beim Gifttod gibt es keinen Killer. Aber auf diese Art
+                    // erkennt der Todesraum die Ursache korrekt und gibt die
+                    // richtige Meldung aus.
+                    SetProp( P_KILLER, "gift" );
+                }
+                
+                die(1);
+                return;
+            }
+            
+            if ( (hpoison < 3 || !query_once_interactive(ME) )
+                 && --drop_poison < 0)
+            {
+                // Giftlevel eins reduzieren. hpoison wurde oben schon
+                // reduziert, d.h. einfach hpoison in P_POISON schreiben.
+                // dabei wird dann auch ggf. drop_poison richtig gesetzt.
+                SetProp( P_POISON, hpoison );
+                if ( !hpoison )
+                    tell_object( ME, "Du scheinst die Vergiftung "
+                                     "ueberwunden zu haben.\n" );  
+            }
+        }
+        
+        if ( hpoison && !random(15) )
+            switch ( hp*100/QueryProp(P_MAX_HP) ){
+            case 71..100 :
+                write( "Du fuehlst Dich nicht gut.\n" );
+                say( capitalize(name(WER)) +
+                     " sieht etwas benommen aus.\n" );
+                break;
+                
+            case 46..70 :
+                write( "Dir ist schwindlig und Dein Magen revoltiert.\n" );
+                say( capitalize(name(WER)) + " taumelt ein wenig.\n" );
+                break;
+                
+            case 26..45 :
+                write( "Dir ist heiss. Du fuehlst Dich schwach. Kopfweh "
+                       "hast Du auch.\n" );
+                say( capitalize(name(WER)) + " glueht direkt und scheint "
+                     "grosse Schwierigkeiten zu haben.\n" );
+                break;
+                
+            case 11..25 :
+                write( "Du fuehlst Dich beschissen. Alles tut weh, und Du "
+                       "siehst nur noch unscharf.\n" );
+                say( capitalize(name(WER)) + " taumelt und stoehnt und "
+                     "kann gerade noch vermeiden, hinzufallen.\n" );
+                break;
+                
+            case 0..10 :
+                write( break_string( "Du siehst fast nichts mehr und kannst "
+                                     "Dich nur noch unter groessten Schmerzen "
+                                     "bewegen. Aber bald tut nichts mehr weh"
+                                     "...", 78 ) );
+                say( break_string( capitalize(name(WER)) + " glueht wie "
+                                   "im Fieber, kann sich kaum noch ruehren "
+                                   "und hat ein schmerzverzerrtes Gesicht.\n",
+                                   78 ) );
+                break;
+            }
+    }
+
+    SetProp( P_HP, hp );
+    SetProp( P_SP, sp );
+}
+
+public int AddExp( int e )
+{
+  int experience;
+  string fn;
+  mixed last;
+
+  experience = QueryProp(P_XP);
+
+  if ( QueryProp(P_KILLS) > 1 && e > 0 )
+      return experience;
+
+  fn = implode( explode( object_name( environment() || this_object() ),
+                               "/" )[0..<2], "/" );
+
+  if ( pointerp(last = Query(P_LAST_XP)) && sizeof(last) == 2 && last[0] == fn )
+      Set( P_LAST_XP, ({ fn, last[1]+e }) );
+  else
+      Set( P_LAST_XP, ({ fn, e }) );
+
+  if ( (experience += e) < 0 )
+      experience = 0;
+
+  return SetProp( P_XP, experience );
+}
+
+static <string|int>* _set_last_xp( <int|string>* last )
+{
+    if ( !pointerp(last) || sizeof(last) != 2 || !stringp(last[0]) ||
+         !intp(last[1]) )
+        return Query(P_LAST_XP);
+    else
+        return Set( P_LAST_XP, last );
+}
+
+
+static int _set_align(int a)
+{
+  if (a<-1000) a = -1000;
+  if (a>1000) a = 1000;
+  return Set(P_ALIGN, a);
+}
+
+
+static int _set_hp( int hp )
+{
+    if ( QueryProp(P_GHOST) )
+        return QueryProp(P_HP);
+
+    if ( hp < 0 )
+        return Set( P_HP, 0 );
+
+    if ( hp > QueryProp(P_MAX_HP) )
+        return Set( P_HP, QueryProp(P_MAX_HP), F_VALUE );
+
+    return Set( P_HP, hp, F_VALUE );
+}
+
+static int _set_sp( int sp )
+{
+    //einige Leute schreiben floats in die P_HP. :-(
+    if (!intp(sp)) {
+	sp=to_int(sp);
+	//ja, es ist teuer. Aber ich will wissen, wers ist. Kann vor
+	//naechstem Reboot wieder raus.
+	log_file("ILLEGAL_TYPE.log",sprintf(
+	      "Versuch, einen nicht-int in P_SP in %O zu schreiben: \n%O\n",
+	      this_object(),
+	      debug_info(DINFO_TRACE,DIT_STR_CURRENT)));
+    }
+
+    if ( QueryProp(P_GHOST) )
+        QueryProp(P_SP);
+
+    if ( sp < 0 )
+        return Set( P_SP, 0 );
+
+    if ( sp > QueryProp(P_MAX_SP) )
+        return Set( P_SP, QueryProp(P_MAX_SP), F_VALUE );
+
+    return Set( P_SP, sp, F_VALUE );
+}
+
+static int _set_alcohol(int n)
+{
+  if(!intp(n))
+      raise_error(sprintf(
+        "_set_alcohol(): expected <int>, got %.50O\n", n));
+
+  if (QueryProp(P_GHOST))
+    return Query(P_ALCOHOL, F_VALUE);
+
+  // nur Änderungen und Werte >=0 werden gesetzt...
+  n = n < 0 ? 0 : n;
+  int old = Query(P_ALCOHOL, F_VALUE);
+  if ( old == n)
+    return old;
+
+  // Hooks aufrufen
+  int *ret = HookFlow(H_HOOK_ALCOHOL, n);
+  // Bei Abbruch alten Wert zurueckgeben
+  switch (ret[H_RETCODE]) {
+    case H_CANCELLED:
+      return old;
+    case H_ALTERED:
+      // sonst neuen Wert setzen
+      if(!intp(ret[H_RETDATA]))
+          raise_error(sprintf(
+            "_set_alcohol(): data from HookFlow() != <int>: %.50O\n",
+            ret[H_RETDATA]));
+      n = ret[H_RETDATA];
+      n = n < 0 ? 0 : n;
+
+    // H_NO_MOD is fallthrough
+  }
+
+  return Set(P_ALCOHOL, n, F_VALUE);
+}
+
+static int _set_drink(int n)
+{
+  if(!intp(n))
+      raise_error(sprintf(
+        "_set_drink(): expected <int>, got %.50O\n", n));
+
+  if (QueryProp(P_GHOST))
+    return Query(P_DRINK, F_VALUE);
+
+  // nur Änderungen und Werte >=0 werden gesetzt...
+  n = n < 0 ? 0 : n;
+  int old = Query(P_DRINK, F_VALUE);
+  if ( old == n)
+    return old;
+
+  // Hooks aufrufen
+  int *ret = HookFlow(H_HOOK_DRINK, n);
+  // Bei Abbruch alten Wert zurueckgeben
+  switch (ret[H_RETCODE]) {
+    case H_CANCELLED:
+      return old;
+    case H_ALTERED:
+      // sonst neuen Wert setzen
+      if(!intp(ret[H_RETDATA]))
+          raise_error(sprintf(
+            "_set_drink(): data from HookFlow() != <int>: %.50O\n",
+            ret[H_RETDATA]));
+      n = ret[H_RETDATA];
+      n = n < 0 ? 0 : n;
+
+    // H_NO_MOD is fallthrough
+  }
+
+  return Set(P_DRINK, n, F_VALUE);
+}
+
+static int _set_food(int n)
+{
+  if(!intp(n))
+      raise_error(sprintf(
+        "_set_food(): expected <int>, got %.50O\n", n));
+
+  if (QueryProp(P_GHOST))
+    return Query(P_FOOD, F_VALUE);
+
+  // nur Änderungen und Werte >=0 werden gesetzt...
+  n = n < 0 ? 0 : n;
+  int old = Query(P_FOOD, F_VALUE);
+  if ( old == n)
+    return old;
+
+  // Hooks aufrufen
+  int *ret = HookFlow(H_HOOK_FOOD, n);
+  // Bei Abbruch alten Wert zurueckgeben
+  switch (ret[H_RETCODE]) {
+    case H_CANCELLED:
+      return old;
+    case H_ALTERED:
+      // sonst neuen Wert setzen
+      if(!intp(ret[H_RETDATA]))
+          raise_error(sprintf(
+            "_set_food(): data from HookFlow() != <int>: %.50O\n",
+            ret[H_RETDATA]));
+      n = ret[H_RETDATA];
+      n = n < 0 ? 0 : n;
+
+    // H_NO_MOD is fallthrough
+  }
+
+  return Set(P_FOOD, n, F_VALUE);
+}
+
+static int _set_poison(int n)
+{
+    if(!intp(n))
+      raise_error(sprintf(
+        "_set_poison(): expected <int>, got %.50O\n", n));
+
+  if (QueryProp(P_GHOST))
+    return Query(P_POISON, F_VALUE);
+
+  int mp = QueryProp(P_MAX_POISON);
+  n = (n<0 ? 0 : (n>mp ? mp : n));
+
+  // nur >=0 zulassen.
+  n = n < 0 ? 0 : n;
+
+  int old = Query(P_POISON, F_VALUE);
+  if ( old == 0 && n == 0)
+    return old;
+
+  // Hooks aufrufen
+  int *ret = HookFlow(H_HOOK_POISON, n);
+  // Bei Abbruch alten Wert zurueckgeben
+  switch (ret[H_RETCODE]) {
+    case H_CANCELLED:
+      return old;
+    case H_ALTERED:
+      // sonst neuen Wert setzen
+      if(!intp(ret[H_RETDATA]))
+          raise_error(sprintf(
+            "_set_poison(): data from HookFlow() != <int>: %.50O\n",
+            ret[H_RETDATA]));
+      n = ret[H_RETDATA];
+      n = n < 0 ? 0 : n;
+
+    // H_NO_MOD is fallthrough
+  }
+
+  // Fuer die Selbstheilung.
+  switch(n) {
+  case 1:
+    drop_poison = 40+random(16);
+    break;
+  case 2:
+    drop_poison = 25+random(8);
+    break;
+  case 3:
+    drop_poison = 18+random(4);
+    break;
+  default:
+    // nur relevant fuer NPC, da Spieler bei >3 Gift nicht mehr regegenieren.
+    drop_poison = 22 - 2*n + random(43 - 3*n);
+    break;
+  }
+
+  // fuer Setzen der Prop von aussen ein Log schreiben
+  if (previous_object(1) != ME)
+    log_file("POISON", sprintf("%s - %s: %d von %O (%s)\n",
+           dtime(time())[5..],
+           (query_once_interactive(this_object()) ?
+             capitalize(geteuid(this_object())) :
+             capitalize(name(WER))),
+           n,
+           (previous_object(2) ? previous_object(2) : previous_object(1)),
+           (this_player() ? capitalize(geteuid(this_player())) : "???")));
+
+  return Set(P_POISON, n, F_VALUE);
+}
+
+static int _set_xp(int xp) { return Set(P_XP, xp < 0 ? 0 : xp, F_VALUE); }
+
+static mixed _set_die_hook(mixed hook)
+{
+  if(hook && query_once_interactive(this_object()))
+    log_file("DIE_HOOK",
+             sprintf("%s : DIE_HOOK gesetzt von %O in %O (%s)\n",
+                     dtime(time())[5..],
+                     (previous_object(2) ? previous_object(2):previous_object(1)),
+                     this_object(),getuid(this_object())));
+  return Set(P_TMP_DIE_HOOK,hook, F_VALUE);
+}
+
+static mapping _query_enemy_damage()
+{
+  return copy(enemy_damage);
+}
+
+// nur ne Kopie liefern, sonst kann das jeder von aussen aendern.
+static mapping _query_timing_map() {
+  return copy(Query(P_TIMING_MAP));
+}
+
+/****************************************************************************
+ * Consume-Funktion, um zentral durch konsumierbare Dinge ausgeloeste
+ * Aenderungen des Gesundheitszustandes herbeizufuehren.
+ ***************************************************************************/
+
+/* Konsumiert etwas
+ *
+ * Rueckgabewert
+ *      1       erfolgreich konsumiert
+ *      0       fehlende oder falsche Parameter
+ *     <0       Bedingung fuer konsumieren nicht erfuellt, Bitset aus:
+ *      1       Kann nichts mehr essen
+ *      2       Kann nichts mehr trinken
+ *      4       Kann nichts mehr saufen
+ *      8       Abgebrochen durch Hook H_HOOK_CONSUME
+ */
+public varargs int consume(mapping cinfo, int testonly)
+{
+  int retval = 0;
+  // nur was tun, wenn auch Infos reinkommen
+  if (mappingp(cinfo) && sizeof(cinfo)) {
+    // Hooks aufrufen, sie aendern ggf. noch was in cinfo.
+    mixed *hret = HookFlow(H_HOOK_CONSUME, ({cinfo, testonly}) );
+    switch(hret[H_RETCODE])
+    {
+        case H_CANCELLED:
+          return -HC_HOOK_CANCELLATION;
+        case H_ALTERED:
+          // testonly kann nicht geaendert werden.
+          cinfo = hret[H_RETDATA][0];
+    }
+    // Legacy-Mappings (flache) neben strukturierten Mappings zulassen
+    // flache Kopien erzeugen (TODO?: und fuer Teilmappings nicht relevante
+    // Eintraege loeschen)
+    mapping conditions;
+    if (mappingp(cinfo[H_CONDITIONS])) {
+      conditions = copy(cinfo[H_CONDITIONS]);
+    } else {
+      conditions = copy(cinfo);
+    }
+    mapping effects;
+    if (mappingp(cinfo[H_EFFECTS])) {
+      effects = filter(cinfo[H_EFFECTS], (: member(H_ALLOWED_EFFECTS, $1) > -1 :));
+    } else {
+      effects = filter(cinfo, (: member(H_ALLOWED_EFFECTS, $1) > -1 :));
+    }
+
+    // Bedingungen pruefen
+    if (mappingp(conditions) && sizeof(conditions)) {
+      // Bedingungen fuer Konsum auswerten
+      if (conditions[P_FOOD] && !eat_food(conditions[P_FOOD], 1))
+        retval |= HC_MAX_FOOD_REACHED;
+      else if (conditions[P_DRINK] && !drink_soft(conditions[P_DRINK], 1))
+        retval |= HC_MAX_DRINK_REACHED;
+      else if (conditions[P_ALCOHOL] && !drink_alcohol(conditions[P_ALCOHOL], 1))
+        retval |= HC_MAX_ALCOHOL_REACHED;
+      // retval negativ machen, damit Fehler leicht erkennbar ist
+      retval = -retval;
+    }
+    // Bedingungen wurden abgearbeitet, jetzt die Heilung durchfuehren
+    if (!retval) {
+      if (!testonly) {
+        // Bedingungen erfuellen, wenn alles passt und kein Test
+        if (conditions[P_ALCOHOL])
+          drink_alcohol(conditions[P_ALCOHOL]);
+        if (conditions[P_DRINK])
+          drink_soft(conditions[P_DRINK]);
+        if (conditions[P_FOOD])
+          eat_food(conditions[P_FOOD]);
+        // Und jetzt die Wirkungen
+        if (effects[P_POISON])
+          SetProp(P_POISON, QueryProp(P_POISON) + effects[P_POISON]);
+        // Und nun wirklich heilen
+        switch (cinfo[H_DISTRIBUTION]) {
+        case HD_INSTANT:
+          map(effects, (: SetProp($1, QueryProp($1) + $2) :));
+          break;
+        case 1..50:
+          buffer_hp(effects[P_HP], cinfo[H_DISTRIBUTION]);
+          buffer_sp(effects[P_SP], cinfo[H_DISTRIBUTION]);
+          break;
+        default:
+          buffer_hp(effects[P_HP], HD_STANDARD);
+          buffer_sp(effects[P_SP], HD_STANDARD);
+          break;
+        }
+      }
+      retval = 1;
+    }
+  }
+  return retval;
+}
diff --git a/std/living/light.c b/std/living/light.c
new file mode 100644
index 0000000..5b12e1f
--- /dev/null
+++ b/std/living/light.c
@@ -0,0 +1,55 @@
+// MorgenGrauen MUDlib
+//
+// living/description.c -- description for living objects
+//
+// $Id: description.c 7340 2009-11-19 21:44:51Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <living/description.h>
+#include <living/skills.h>
+#undef NEED_PROTOTYPES
+
+#include <player/viewcmd.h>
+#include <new_skills.h>
+#include <container.h>
+#include <player/base.h>
+#include <wizlevels.h>
+
+inherit "/std/container/light";
+
+protected void create() {
+  ::create();
+  SetProp(P_LIGHT_TRANSPARENCY, 0);
+}
+
+static int _query_player_light()
+{
+  if (environment())
+    return environment()->QueryProp(P_INT_LIGHT) + QueryProp(P_LIGHT_MODIFIER);
+}
+
+varargs int CannotSee(int silent)
+{
+   string is_blind;
+   if (is_blind = QueryProp(P_BLIND)) {
+      if (!silent) {
+         if (stringp(is_blind))
+            tell_object(this_object(), is_blind);
+         else tell_object(this_object(), "Du bist blind!\n");
+      }
+      return 2;
+   }
+   if (UseSkill(SK_NIGHTVISION)<=0 &&
+       environment() && QueryProp(P_PLAYER_LIGHT)<=0 &&
+       (!IS_LEARNER(this_object()) || !Query(P_WANTS_TO_LEARN)))
+   {
+       if (!silent) tell_object(this_object(), "Es ist zu dunkel!\n");
+       return 1;
+   }
+   return 0;
+}
diff --git a/std/living/moneyhandler.c b/std/living/moneyhandler.c
new file mode 100644
index 0000000..7a22226
--- /dev/null
+++ b/std/living/moneyhandler.c
@@ -0,0 +1,26 @@
+// MorgenGrauen MUDlib
+//
+// living/moneyhandler.c -- money handler for livings
+//
+// $Id: moneyhandler.c 6738 2008-02-19 18:46:14Z Humni $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/container/moneyhandler";
+
+// Funktionen sollen nur das Programm ersetzen, natuerlich nur in der
+// Blueprint _dieses_ Objektes, nicht in anderen. ;-) BTW: Normalerweise
+// sollte niemand hierdrin create() rufen, der das Ding hier erbt.
+protected void create_super() {
+  if (object_name(this_object()) == __FILE__[..<3])
+    replace_program();
+}
+
+// wird nicht von erbenden Objekten gerufen. (Wozu auch.)
+protected void create() {
+  create_super();
+}
+
diff --git a/std/living/moving.c b/std/living/moving.c
new file mode 100644
index 0000000..c6149ff
--- /dev/null
+++ b/std/living/moving.c
@@ -0,0 +1,457 @@
+// MorgenGrauen MUDlib
+//
+// living/moving.c -- moving of living objects
+//
+// $Id: moving.c 9448 2016-01-22 17:52:28Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/thing/moving";
+
+#define NEED_PROTOTYPES
+#include <hook.h>
+#include <living/moving.h>
+#include <living/skills.h>
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <moving.h>
+#include <new_skills.h>
+#include <living.h>
+
+#undef NEED_PROTOTYPES
+
+#include <config.h>
+#include <properties.h>
+#include <language.h>
+#include <wizlevels.h>
+#include <defines.h>
+
+
+protected void create()
+{
+    if (object_name(this_object()) == __FILE__[0..<3])
+    {
+      return;
+    }
+    offerHook(H_HOOK_MOVE,1);
+}
+
+public void AddPursuer(object ob)
+{
+  mixed *pur;
+
+  if (!objectp(ob))
+    return;
+
+  if (!pointerp(pur=Query(P_PURSUERS)))
+    pur=({0,({})});
+  else if (member(pur[1],ob)!=-1)
+    return;
+  
+  SetProp(P_PURSUERS,({ pur[0], pur[1]+({ob})-({0}) }));
+  ob->_SetPursued(ME);
+}
+
+public void RemovePursuer(object ob)
+{
+  mixed *pur;
+
+  if (pointerp(pur=Query(P_PURSUERS,F_VALUE)) 
+      && member(pur[1],ob)!=-1)
+  {
+    pur[1]-=({ob,0});
+    if (ob)
+      ob->_RemovePursued(ME);
+    if (!pur[0]&&!sizeof(pur[1]))
+      pur=0;
+    SetProp(P_PURSUERS,pur);
+  }
+}
+
+public void _SetPursued(object ob)
+{
+  mixed *pur;
+
+  if (!pointerp(pur=Query(P_PURSUERS)))
+    pur=({0,({})});
+  else
+    if (objectp(pur[0]))
+      pur[0]->RemovePursuer(ME);
+  pur[0]=ob;
+  pur[1]-=({0});
+  Set(P_PURSUERS,pur);
+}
+
+public void _RemovePursued(object ob)
+{
+  mixed *pur;
+
+  if (!pointerp(pur=Query(P_PURSUERS)) || pur[0]!=ob)
+    return;
+  pur[0]=0;
+  pur[1]-=({0});
+  if (!sizeof(pur[1]))
+    pur=0;
+  Set(P_PURSUERS,pur);
+}
+
+
+private void kampfende( object en ) {
+  if (!objectp(en)) return;
+  tell_object( ME, capitalize(en->name()) +
+      " ist jetzt hinter Dir her.\n" );
+  tell_object( en, "Du verfolgst jetzt " + name(WEN) + ".\n" );      
+  en->InsertSingleEnemy(ME);
+}
+
+private int _is_learner(object pl) {
+  return IS_LEARNER(pl); 
+}
+
+
+// a) Pruefungen, ob das move erlaubt ist.
+// b) zum Ueberschreiben
+protected int PreventMove(object dest, object oldenv, int method) {
+
+  // M_NOCHECK? -> Bewegung eh erlaubt (und Rueckgabewert wuerde ignoriert),
+  // aber PreventInsert/PreventLeave() rufen und ignorieren.
+  if ((method&M_NOCHECK)) {
+      // erst PreventLeaveLiving() rufen...
+      if(environment())        
+          environment()->PreventLeaveLiving(this_object(), dest);
+      // dann PreventInsertLiving() im Ziel-Env.
+      dest->PreventInsertLiving(this_object());
+      // und raus...
+      return(0);
+  }
+
+  // bei Lebewesen muss die Bewegungsmethode M_GO und M_TPORT sein. Dies ist
+  // gleichzeigt die Restriktion gegen das Nehmen von Lebewesen, da dort
+  // M_GET/M_GIVE/M_PUT etc. verwendet wuerde. Bei M_GO und M_TPORT findet
+  // keine Pruefung statt, ob das Objekt ins Ziel 'reinpasst' (Gewicht, Anzahl
+  // Objekte usw.).
+  // Ich finde es etwas merkwuerdig gebaut (Zesstra).
+  if ( !(method & (M_GO | M_TPORT)) )
+      return ME_PLAYER;
+  
+  // alte und neue Umgebung auf NO_TPORT pruefen.
+  if ( (method & M_TPORT) ) {
+    if ( environment() &&
+        (environment()->QueryProp(P_NO_TPORT) & (NO_TPORT_OUT|NO_TPORT)) )
+          return ME_CANT_TPORT_OUT;
+    else if ( dest->QueryProp(P_NO_TPORT) & (NO_TPORT_IN|NO_TPORT) )
+          return ME_CANT_TPORT_IN;
+  }
+
+  // erst PreventLeaveLiving() testen...
+  if( environment() && environment()->PreventLeaveLiving(this_object(), dest))
+      return ME_CANT_LEAVE_ENV;
+  // dann PreventInsertLiving() im Ziel-Env
+  if (dest->PreventInsertLiving(this_object())) 
+      return ME_CANT_BE_INSERTED;
+
+  return 0;
+}
+
+// Krams nach dem Move machen und nebenbei zum Ueberschreiben.
+protected void NotifyMove(object dest, object oldenv, int method) {
+  mixed res;
+  object enem;
+
+  // Begruessungsschlag fuer die Gegener
+  if ( !(method & M_NO_ATTACK) )
+      InitAttack();
+
+  if (!objectp(ME)) return;
+
+  // Verfolger nachholen.
+  if ( pointerp(res = Query(P_PURSUERS)) && sizeof(res[1]) ) {
+      while ( remove_call_out( "TakeFollowers" ) >= 0 );
+
+      call_out( "TakeFollowers", 0 );
+  }
+
+  // und noch das Team nachholen.
+  if ( oldenv != dest
+      && objectp(ME)
+      && QueryProp(P_TEAM_AUTOFOLLOW)
+      && objectp( enem = IsTeamLeader() ) )
+      enem->StartFollow(oldenv); // Teamverfolgung
+
+}
+
+varargs public int move( object|string dest, int method, string direction,
+                         string textout, string textin )
+{
+    int para, nightvis, invis, tmp;
+    object oldenv, *inv;
+    string fn,vc;
+    mixed res;
+    mixed hookData, hookRes;
+
+    if (!objectp(dest) && !stringp(dest))
+      raise_error(sprintf("Wrong argument 1 to move(). 'dest' must be a "
+            "string or object! Argument was: %.100O\n",
+            dest));
+
+    // altes Env erstmal merken.
+    oldenv = environment();
+    
+    //erstmal den richtigen Zielraum suchen, bevor irgendwelche Checks gemacht
+    //werden...
+    // Ist der Spieler in einer Parallelwelt?
+    if ( (para = QueryProp(P_PARA)) && intp(para) ) {
+        fn = objectp(dest) ? object_name(dest) : dest;
+
+        // Falls der Zielraum nicht schon explizit in der Parallelwelt ist,
+        // neuen Zielraum suchen. Aber nur, wenn fn kein # enthaelt (also kein
+        // Clone ist), sonst wuerde eine Bewegung nach raum#42^para versucht,
+        // was dann buggt. ;-) Problem wird offenbar, wenn ein Para-Lebewesen
+        // im create() eines VC-Raums in Para in den Raum bewegt wird, da
+        // dieser dann noch nicht vom Driver umbenannt wurde und raum#42
+        // heisst.
+        if ( !sizeof(regexp( ({ fn }), "\\^[1-9][0-9]*$" )) &&
+            strrstr(fn,"#")==-1 )
+        {
+            fn += "^" + para;
+
+          // Der Parallelwelt-Raum muss existieren und fuer Spieler
+          // freigegeben sein, damit er zum neuen Ziel wird. Ansonsten
+          // duerfen nur NPCs, Testspieler und Magier herein.
+          if ( (find_object(fn) 
+                || ((file_size(fn+".c")>0 ||
+                    (file_size(vc=implode(explode(fn,"/")[0..<2],"/")+
+                          "/virtual_compiler.c")>0 &&
+                    !catch(tmp=(int)call_other(vc,"QueryValidObject",fn);
+                           publish) && tmp>0)) &&
+                    !catch(load_object(fn);publish) )) &&
+                  (!interactive(ME) || !fn->QueryProp(P_NO_PLAYERS) || 
+                  (method & M_NOCHECK) || IS_LEARNER(ME) ||
+                  (stringp(res = QueryProp(P_TESTPLAYER)) &&
+                   IS_LEARNER( lower_case(res) ))) )
+          {
+              dest = fn;
+          }
+          else
+          {
+              // Wir bleiben in der Normalwelt.
+              para = 0;
+          }
+        }
+    }
+
+    // jetzt erstmal Hooks abpruefen, da sie ggf. die Daten aendern.
+    // alten P_TMP_MOVE_HOOK pruefen.
+    if ( res = QueryProp(P_TMP_MOVE_HOOK) ){
+        if ( pointerp(res) && sizeof(res) >= 3
+             && intp(res[0]) && time()<res[0]
+             && objectp(res[1]) && stringp(res[2]) ){
+            if ( res = call_other( res[1], res[2], dest, method, direction,
+                                   textout, textin ) ){
+                if ( pointerp(res) && sizeof(res) == 5 ){
+                    dest = res[0];
+                    method = res[1];
+                    direction = res[2];
+                    textout = res[3];
+                    textin = res[4];
+                }
+                else if ( intp(res) && res == -1 )
+                    return ME_CANT_LEAVE_ENV;
+            }
+        } else
+            SetProp( P_TMP_MOVE_HOOK, 0 );
+    }
+    // move hook nach neuem Hooksystem triggern.
+    hookData=({dest,method,direction,textout,textin});
+    hookRes=HookFlow(H_HOOK_MOVE,hookData);
+    if(hookRes && pointerp(hookRes) && sizeof(hookRes)>H_RETDATA) {
+      if(hookRes[H_RETCODE]==H_CANCELLED) {
+        return ME_CANT_LEAVE_ENV;
+      }
+    else if(hookRes[H_RETCODE]==H_ALTERED && hookRes[H_RETDATA] &&
+          pointerp(hookRes[H_RETDATA]) && sizeof(hookRes[H_RETDATA])>=5 ){
+      dest = hookRes[H_RETDATA][0];
+      method = hookRes[H_RETDATA][1];
+      direction = hookRes[H_RETDATA][2];
+      textout = hookRes[H_RETDATA][3];
+      textin = hookRes[H_RETDATA][4];
+      }
+    }
+
+    // dest auf Object normieren
+    if (stringp(dest)) dest=load_object(dest);
+
+    // jetzt Checks durchfuehren, ob das Move durchgefuehrt werden darf.
+    if (tmp=PreventMove(dest, oldenv, method)) {
+      // auf gueltigen Fehler pruefen, wer weiss, was Magier da evtl.
+      // versehentlich zurueckgeben.
+      if (VALID_MOVE_ERROR(tmp))
+        return(tmp);
+      else
+        return(ME_DONT_WANT_TO_BE_MOVED);
+    }
+  
+    if ( invis = QueryProp(P_INVIS) )
+        method |= M_SILENT;
+
+    if ( objectp(oldenv) ) {
+        if ( !(method & M_SILENT) ) {
+            string *mout;
+            if ( !textout ){
+                if ( method & M_TPORT )
+                    textout = (string) QueryProp(P_MMSGOUT) ||
+                        (string) QueryProp(P_MSGOUT);
+                else 
+                    textout = (mout = explode( (string)
+                                                QueryProp(P_MSGOUT) || "",
+                                                      "#" ))[0]
+                         || (string)QueryProp(P_MMSGOUT);
+            }
+
+            if ( !sizeof(direction) )
+                direction = 0;
+
+            inv = all_inventory(environment()) - ({ this_object() });
+            inv = filter( inv, #'living/*'*/ );
+            inv -= filter_objects( inv, "CannotSee", 1 );
+
+            filter( inv, #'tell_object/*'*/,
+                          Name( WER, 2 ) + " " + textout +
+                          (direction ? " " + direction : "") +
+                          (sizeof(mout) > 1 ? mout[1] : "") + ".\n" );
+        }
+        // Magier sehen auch Bewegungen, die M_SILENT sind
+        else if ( interactive(ME) ){
+            inv = (all_inventory(environment()) & users())
+                - ({ this_object() });
+            inv = filter( inv, #'_is_learner/*'*/ );
+
+            if ( invis )
+                fn = "(" + capitalize(getuid(ME)) + ") verschwindet "
+                    "unsichtbar.\n";
+            else
+                fn = capitalize(getuid(ME)) + " verschwindet ganz leise.\n";
+            
+            filter( inv, #'tell_object/*'*/, fn );
+        }
+        
+        // Nackenschlag beim Fluechten:
+        if ( !(method & M_NO_ATTACK) && objectp(ME) )
+            ExitAttack();
+        //falls nach ExitAttack() das Living nicht mehr existiert, muss das
+        //move() auch nicht mehr fortgesetzt werden. Weiter unten gibt es auch
+        //min. eine Stelle, die nicht prueft und ggf. buggt. Daher erfolgt
+        //hier ggf. ein Abbruch. 15.11.06 Zesstra
+        if (!objectp(ME)) return(ME_WAS_DESTRUCTED);
+
+        // Nackenschlag kann ME in den Todesraum bewegt haben...
+        if ( oldenv == environment() ) {
+            // Fuer alle anwesenden gegner kampfende() aufrufen
+            filter((QueryEnemies()[0] & all_inventory(oldenv))-({0}),
+                #'kampfende);
+            // Bugs im exit() sind ohne catch() einfach mist.
+            catch(environment()->exit(ME, dest);publish);
+        }
+    }
+
+    // irgendwas kann das Objekt zerstoert haben, z.B. env->exit().
+    if (!objectp(ME)) return(ME_WAS_DESTRUCTED);
+
+    if ( oldenv != environment() )
+        // Der Nackenschlag oder exit() koennen einen schon bewegt haben.
+        // Und wenn es in den Todesraum ist. ;^)
+        return MOVE_OK;
+    
+    SetProp( P_PREPARED_SPELL, 0 ); // Spruchvorbereitung abgebrochen
+    SetProp( P_LAST_MOVE, time() ); // Zeitpunkt der letzten Bewegung
+    
+    move_object(ME, dest);
+    if (!objectp(ME))
+      return(ME_WAS_DESTRUCTED);
+
+    dest = environment();
+    
+    nightvis = UseSkill(SK_NIGHTVISION);
+    // Meldungen an nicht-Blinde ausgeben, falls keine 'stille' Bewegung
+    if ( !(method & M_SILENT) ) {
+      if ( !textin ) {        
+        if ( method & M_TPORT )
+              textin = (string) QueryProp(P_MMSGIN);
+        else
+              textin = (string) QueryProp(P_MSGIN);
+      }
+            
+      inv = all_inventory(environment()) - ({ this_object() });
+      inv = filter( inv, #'living/*'*/ );
+            inv -= filter_objects( inv, "CannotSee", 1 );
+            filter( inv, #'tell_object/*'*/,
+                          capitalize(name( WER, 0 )) + " " + textin + ".\n" );
+    }
+    // sonst: Magier sehen auch M_SILENT-Bewegungen, hier also nur an Magier
+    // ausgeben, alle anderen sehen eh nix.
+    else if ( interactive(ME) ) {  
+      inv = (all_inventory(environment()) & users()) - ({this_object()});        
+      inv = filter( inv, #'_is_learner/*'*/ );        
+      if ( invis )
+        fn = "(" + capitalize(getuid(ME)) + ") taucht unsichtbar auf.\n";
+      else
+        fn = capitalize(getuid(ME)) + " schleicht leise herein.\n";        
+      filter( inv, #'tell_object, fn );    
+    }
+
+    // "Objekt" ueber das Move informieren.
+    NotifyMove(dest, oldenv, method);
+
+    // InitAttack() in NotifyMove() kann das Objekt zerstoert haben.
+    if (!objectp(ME))
+      return(ME_WAS_DESTRUCTED);
+
+    //scheint wohl geklappt zu haben.
+    return MOVE_OK;
+}
+
+public void TakeFollowers()
+{
+  mixed *f,env;
+  int meth,i,r;
+
+  f=Query(P_PURSUERS);
+  if (!pointerp(f))
+    return;
+  env=environment();
+  if(object_name(env) == "/room/netztot") return;
+  foreach(object follower: f[1]-({0}) ) {
+    // die pruefung auf objectp ist nicht verrueckt, es kann theo. sein, dass
+    // Verfolger im PreventFollow() oder in ihrem move/init andere Verfolger
+    // zerstoeren.
+    if (objectp(follower) && environment(follower)!=env) {
+      //meth=M_NOCHECK;
+      meth=M_GO;
+      if (follower->Query(P_FOLLOW_SILENT))
+          meth|=M_SILENT|M_NO_SHOW;
+      catch(r=follower->PreventFollow(env);publish);
+      if (!r)
+          follower->move(env,meth);
+      else if (r==2)
+          RemovePursuer(follower);
+    }
+  }
+}
+
+varargs public int remove()
+{ object team;
+
+  if (environment())
+  {
+    if ( objectp(team=Query(P_TEAM)) )
+      catch(team->RemoveMember(ME);publish);
+
+    environment()->NotifyRemove(ME);
+  }
+  destruct(ME);
+  return 1;
+}
+
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;
+}
diff --git a/std/living/skill_attributes.c b/std/living/skill_attributes.c
new file mode 100644
index 0000000..ad90aa0
--- /dev/null
+++ b/std/living/skill_attributes.c
@@ -0,0 +1,379 @@
+// MorgenGrauen MUDlib
+//
+// living/skills_attributes.c - Verwaltung der Skillattribute von Lebewesen
+//
+// $Id: skills.c 6673 2008-01-05 20:57:43Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/util/executer";
+
+#define NEED_PROTOTYPES
+#include <living/skill_attributes.h>
+#include <player/life.h>
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <properties.h>
+#include <defines.h>
+
+//#define ZDEBUG(x) if (find_player("zesstra")) tell_object(find_player("zesstra"),x)
+#define ZDEBUG(x)
+//#define SASETLOG(x) log_file("SASET.LOG", x, 250000)
+
+//#define __DEBUG__
+
+// Variable fuer die Skill-Attribute, Datenstruktur:
+/*  ([ SA_ATTR: ({Summe_Stat_Modifier, Zeitpunkt, AnzahlModifier, });
+              ([ ob1:value;duration,
+                 ob2:value;duration, ...]);  // stat. Modifier
+              ([ ob1:closure;duration,
+                 ob2:closure;duration, ...])     // dyn. Modifier
+             ,
+     SA_ATTR2: ({...}); ([]); ([]),
+   ]) */
+private nosave mapping skillattrs;
+
+protected void create() {
+  Set(P_SKILL_ATTRIBUTES, SECURED, F_MODE_AS);
+}
+
+// von aussen Prop setzen ist nicht...
+mapping _set_skill_attr(mapping sa) {
+  return deep_copy(skillattrs);
+}
+// und auch beim Abfragen nur kopien liefern. ;-)
+mapping _query_skill_attr() {
+  return deep_copy(skillattrs);
+
+//TODO: Evtl. ext. Setzen von P_SKILL_ATTRIBUTE_OFFSETS mitloggen?
+}
+
+private void UpdateSACache(string *attrs) {
+#ifdef __DEBUG__
+    if (getuid(this_object()) == "zesstra")
+    ZDEBUG(sprintf("%O\n",skillattrs));
+#endif
+  if (!mappingp(skillattrs)) return;
+  if (!pointerp(attrs) || sizeof(attrs))
+    attrs = m_indices(skillattrs); // alle
+  // sonst schnittmenge aus existierenden und den uebergebenen.
+  attrs = m_indices(skillattrs) & attrs;
+
+  // und jetzt ueber alle gewuenschten SAs drueber
+  foreach(string attr : attrs) {
+    int *cache = skillattrs[attr, SAM_CACHE];
+    mapping stat = skillattrs[attr, SAM_STATIC];
+    mapping dyn = skillattrs[attr, SAM_DYNAMIC];
+    int sum = 0;
+    int timeout = __INT_MAX__;
+ 
+    // ueber stat. Mods iterieren und Aufsummieren, kleinsten Timeout
+    // ermitteln.
+    foreach(object ob, int value, int duration: stat) {
+      // gueltige Mods aufaddieren, abgelaufene rauswerfen
+      if (duration >= time()) {
+        sum += value;
+        if (duration < timeout)
+          timeout = duration;
+      }
+      else
+        m_delete(stat, ob); // ja, geht im foreach ;-)
+    }
+    // Ablaufzeiten der dyn. Mods pruefen, waere hier zwar nicht unbedingt
+    // noetig sondern koennte man ausschliesslich im QuerySkillAttribute()
+    // machen, aber dann waere die Ermittlung der Summe der Mods schwieriger.
+    foreach(object ob, closure value, int duration: dyn) {
+      if (duration < time())
+        m_delete(dyn, ob); // ungueltig, weg damit.
+    }
+    // gesamtzahl Mods?
+    cache[SAM_COUNT] = sizeof(stat) + sizeof(dyn);
+    if (!cache[SAM_COUNT]) {
+      // keine mods da, Submapping fuer dieses SA komplett loeschen.
+      m_delete(skillattrs, attr);
+      continue;
+    }
+    // sonst die anderen Cache-Werte setzen.
+    cache[SAM_SUM] = sum;
+    cache[SAM_CACHE_TIMEOUT] = timeout;
+  }
+  // wenn alle Mods geloescht wurden.
+  if (!sizeof(skillattrs)) skillattrs=0;
+#ifdef __DEBUG__
+  if (getuid(this_object()) == "zesstra")
+    ZDEBUG(sprintf("%O\n",skillattrs));
+#endif
+}
+
+private int InternalModifySkillAttribute(object caster, string atrname,
+                                mixed value, int duration) {
+  int zeit = utime()[1];
+  int ticks = get_eval_cost();
+
+  // nur existierende SAs...
+  if (!stringp(atrname)
+      || member(VALID_SKILL_ATTRIBUTES, atrname) == -1)
+    return SA_MOD_INVALID_ATTR;
+
+  if (!objectp(caster)) return SA_MOD_INVALID_OBJECT;
+
+  if (!mappingp(skillattrs)) skillattrs=m_allocate(1,3);
+  
+  if (!member(skillattrs, atrname)) {
+    skillattrs[atrname,SAM_CACHE] = ({0, 0, 0});
+    // die meisten Mods sind statisch, daher auf Verdacht hier fuer einen
+    // Eintrag Platz reservieren, aber nicht fuer den dyn. Teil.
+    skillattrs[atrname,SAM_STATIC] = m_allocate(1,2);
+    skillattrs[atrname,SAM_DYNAMIC] = m_allocate(0,2);
+  }
+  // pruefen, ob Maximalzahl an Eintraegen drin ist
+  else if (skillattrs[atrname,SAM_CACHE][SAM_COUNT] >= SAM_MAX_MODS) {
+    // letzte Chance: destructete Objekte drin?
+    skillattrs[atrname,SAM_CACHE][SAM_COUNT] = 
+      sizeof(skillattrs[atrname,SAM_STATIC])
+    +sizeof(skillattrs[atrname,SAM_DYNAMIC]);
+    // es kann sein, dass noch abgelaufene Objekte drinstehen,
+    // aber die Pruefung ist mir gerade zu teuer. TODO
+    // nochmal gucken (rest vom cache wird ggf. unten geprueft.)
+    if (skillattrs[atrname,SAM_CACHE][SAM_COUNT] >= SAM_MAX_MODS)
+      return SA_TOO_MANY_MODS; // dann nicht.
+  }
+
+  // Dauer darf nur ein int sein.
+  if (!intp(duration))
+    raise_error(sprintf("Wrong argument 3 to ModifySkillAttribute: "
+    "expected 'int', got %.10O\n", duration));
+  // Zeitstempel ermitteln
+  duration += time();
+
+  // statischer oder dyn. Modifier?
+  if (intp(value)) {
+    // Mod darf nicht zu gross oder zu klein sein. TODO: Grenzen?
+    if (value < -1000)
+      return SA_MOD_TOO_SMALL;
+    else if (value > 1000)
+      return SA_MOD_TOO_BIG;
+    else if (!value)
+      return SA_MOD_INVALID_VALUE; 
+    // jedes Objekt darf nur einen mod haben. Wenn dieses schon einen dyn.
+    // hat, muss der geloescht werden (stat. werden ja eh ersetzt).
+    if (member(skillattrs[atrname,SAM_DYNAMIC], caster))
+      m_delete(skillattrs[atrname,SAM_DYNAMIC], caster);
+    // sonst eintragen
+    skillattrs[atrname, SAM_STATIC] += ([caster: value; duration]);
+  }
+  else if (closurep(value)) {
+    // nur ein Mod pro Objekt, s.o.
+    if (member(skillattrs[atrname,SAM_STATIC], caster))
+      m_delete(skillattrs[atrname,SAM_STATIC], caster);
+    // direkt ohne weitere Pruefung eintragen
+    skillattrs[atrname, SAM_DYNAMIC] += ([caster: value; duration]);
+  }
+  else
+    raise_error(sprintf("Wrong argument 2 to ModifySkillAttribute(): "
+    "expected 'int' or 'closure', got %.10O\n",value));
+
+#ifdef SASETLOG
+  if (query_once_interactive(this_object()))
+    SASETLOG(sprintf("%s: %O, %s, %O, %O\n", 
+        strftime("%y%m%d-%H%M%S"), this_object(), atrname, caster, value));
+#endif
+#ifdef SASTATD
+  object daemon;
+  if (query_once_interactive(ME)
+      && objectp(daemon=find_object(SASTATD)))
+    daemon->LogModifier(caster, atrname, value, duration);
+#endif
+  // noch den Cache fuer dieses SA neu berechnen
+  // TODO: Cache nur invalidieren, damit er erst bei der naechsten Abfrage
+  // aktualisiert wird. Spart Zeit, wenn bis dahin mehrere Mods
+  // entfernt/addiert werden. Dafuer ist die gecache Anzahl an Mods
+  // inkonsistent.
+  UpdateSACache( ({atrname}) );
+
+  ZDEBUG(sprintf("MSA: %O, Zeit: %d, Ticks: %d\n",
+	this_object(),
+	utime()[1]-zeit, ticks-get_eval_cost()));
+
+  return SA_MOD_OK;
+}
+
+public int ModifySkillAttribute(string atrname, mixed value, 
+                                    int duration) {
+  return InternalModifySkillAttribute(
+      (extern_call()?previous_object():ME), atrname, value, duration);
+}
+
+public int RemoveSkillAttributeModifier(object caster, string attrname) {
+  if (!stringp(attrname) || !mappingp(skillattrs) || !objectp(caster)
+      || !member(skillattrs, attrname))
+    return SA_MOD_NOT_FOUND;
+  // TODO: Berechtigung pruefen. ;-)
+
+  if (member(skillattrs[attrname, SAM_STATIC], caster)) {
+    m_delete(skillattrs[attrname, SAM_STATIC], caster);
+  }
+  else if (member(skillattrs[attrname, SAM_DYNAMIC], caster)) {
+    m_delete(skillattrs[attrname, SAM_DYNAMIC], caster);
+  }
+  else
+    return SA_MOD_NOT_FOUND;
+  
+  // TODO: Cache nur invalidieren, damit er erst bei der naechsten Abfrage
+  // aktualisiert wird. Spart Zeit, wenn bis dahin mehrere Mods
+  // entfernt/addiert werden. Dafuer ist die gecache Anzahl an Mods
+  // inkonsistent.
+  UpdateSACache( ({attrname}) );
+
+  return SA_MOD_REMOVED;
+}
+
+public int QuerySkillAttribute(string atrname)
+{
+  mixed offsets, attr;
+  int modsumme, qual, ret, cval;
+
+  if (!stringp(atrname)) // VALID_SKILL_ATTRIBUTES beruecksichtigen?
+    return 100;
+
+  // wenn nicht SA_QUALITY gefragt ist, erstmal jenes ermitteln, weil es den
+  // ersten Modifier auf alle anderen SAs darstellt. Sonst den Startwert fuer
+  // SA_QUALITY (100) modifiziert durch evtl. Todesfolgen ermitteln.
+  if ( atrname != SA_QUALITY )
+    qual = QuerySkillAttribute(SA_QUALITY);
+  else
+    // bei SA_QUALITY gehen die Todesfolgen ein
+    qual = to_int(100 * pow(0.9, death_suffering()/10.0));
+
+  // Die Offsets sind sozusagen der Basiswert der SAs. Als erstes verwursten,
+  // sofern vorhanden, nen int drinsteht und der offset != 0 ist.
+  if ( mappingp(offsets = Query(P_SKILL_ATTRIBUTE_OFFSETS))
+       && intp(attr=offsets[atrname]) 
+       && attr)
+    ret = attr;
+  else
+    ret = 100;
+
+  // wenn keine Mods gesetzt sind, wars das jetzt. ;-)
+  if ( !mappingp(skillattrs)
+       || !member(skillattrs, atrname) )
+    return (ret*qual)/100;
+
+  // wenn Cache der stat. Mods abgelaufen oder offenbar Objekte zerstoert
+  // wurden, muss der Cache neu berechnet werden.
+  if ( skillattrs[atrname,SAM_CACHE][SAM_CACHE_TIMEOUT] < time()
+      || sizeof(skillattrs[atrname,SAM_STATIC])
+         +sizeof(skillattrs[atrname,SAM_DYNAMIC]) !=
+           skillattrs[atrname,SAM_CACHE][SAM_COUNT] )
+  {
+    UpdateSACache( ({atrname}) );
+    // UpdateSACache() loescht uU das SA-Mapping oder Eintraege daraus.
+    if ( !mappingp(skillattrs)
+         || !member(skillattrs, atrname) )
+    return (ret*qual)/100;
+  }
+  // Summe der statischen Mods.
+  modsumme = skillattrs[atrname,SAM_CACHE][SAM_SUM];
+
+  // TODO! Evtl. andere Daten als ME an die Funktion uebergeben
+  // TODO! bereits nach Addition des Funktionsrueckgabewertes pruefen, ob der
+  //       Wertebereich des SA-Modifiers ueberschritten ist, oder freien
+  //       Wertebereich erlauben und erst am Ende deckeln (aktuelle Variante)
+  // Dynamische Modifier auswerten
+  foreach( object ob, closure cl, int duration:
+           skillattrs[atrname,SAM_DYNAMIC] )
+  {
+    if ( duration > time()               // Noch nicht abgelaufen und
+         && intp(cval=funcall(cl, ME)) ) // Funktion liefert int zurueck
+      modsumme += cval;
+    else {
+      m_delete(skillattrs[atrname,SAM_DYNAMIC], ob);
+      skillattrs[atrname,SAM_CACHE][SAM_COUNT]--;
+    }
+  }
+  ret = ((ret+modsumme)*qual)/100;
+  if ( ret < 10 )
+    ret = 10;
+  else if ( ret > 1000 )
+    ret = 1000;
+
+  return ret;
+}
+
+public varargs mapping QuerySkillAttributeModifier(object caster, 
+                           string *attrnames) {
+  
+  // auf abgelaufene Modifikatoren pruefen
+  if (!pointerp(attrnames))
+    UpdateSACache( ({}) );
+  else
+    UpdateSACache(attrnames);
+
+  if (!mappingp(skillattrs))
+    return ([]);
+  if (!pointerp(attrnames) || !sizeof(attrnames))
+    attrnames = m_indices(skillattrs); // alle durchsuchen
+  else // schnittmenge der gew. und vorhandenen bilden
+    attrnames = m_indices(skillattrs) & attrnames;
+  
+  mapping res=m_allocate(sizeof(attrnames), 1);
+
+  foreach(string atr: attrnames) {
+    res[atr] = m_allocate(5, 2); // mal fuer 5 Werte Platz reservieren
+    if (!objectp(caster)) {
+      // wenn kein bestimmter caster angefragt ist, alle mods liefern
+      foreach(object c, int value, int dur: skillattrs[atr, SAM_STATIC]) {
+	res[atr] += ([c: value; dur]);
+      }
+      foreach(object c, closure value, int dur: skillattrs[atr, SAM_DYNAMIC]) {
+	res[atr] += ([c: value; dur]);
+      }
+    }
+    else {
+      // sonst nur den Mod von caster
+      if (member(skillattrs[atr, SAM_STATIC], caster)) {
+	res[atr] += ([caster: 
+			 skillattrs[atr, SAM_STATIC][caster, SAM_VALUE];
+	                 skillattrs[atr, SAM_STATIC][caster, SAM_DURATION]
+		    ]);
+      }
+      else if (member(skillattrs[atr, SAM_DYNAMIC], caster)) {
+	res[atr] += ([caster:
+			 skillattrs[atr, SAM_DYNAMIC][caster, SAM_VALUE];
+	                 skillattrs[atr, SAM_DYNAMIC][caster, SAM_DURATION]
+		    ]);
+      }
+    }
+  }
+  return res;
+}
+
+// Kompatibilitaetsfunktion mit altem Interface. Ist nur ein Wrapper, der
+// value umrechnet und 'alte' Rueckgabewerte liefert.
+
+public varargs int ModifySkillAttributeOld(object caster, string atrname,
+                          int value, int duration, mixed fun) {
+  int res;
+  // Caller ermitteln
+  if (extern_call()) caster=previous_object();
+  else caster=ME;
+
+  // Closures koennen via ModifySkillAttributeOld() nicht mehr gesetzt werden,
+  // da deren Rueckgabewert nicht sinnvoll umgerechnet werden koennen. (Man
+  // weiss nicht, ob es eine neue oder alte Closure ist.)
+  if (pointerp(fun) || closurep(fun))
+      raise_error(sprintf("Closures for SA modifiers can't be set by "
+      "ModifySkillAttributeOld()! Use ModifySkillAttribute()!\n"));
+  
+  res = InternalModifySkillAttribute(caster, atrname, value-100, duration);
+  // die alte funktion hatte nur 0 fuer ungueltigen Wert und < 0 fuer zu
+  // kleines Level als Rueckgabewert. Zu kleines Level gibt nicht mehr, also
+  // bleibt nur 0 als Sammel-Fehlercode uebrig. *seufz*
+  if (res < 0) return 0;
+  return res;
+}
+
diff --git a/std/living/skill_utils.c b/std/living/skill_utils.c
new file mode 100644
index 0000000..24caf75
--- /dev/null
+++ b/std/living/skill_utils.c
@@ -0,0 +1,35 @@
+// MorgenGrauen MUDlib
+//
+// living/skill_utils -- some helper functions for manipulating skill data 
+//                       needed in more than one program.
+//
+// $Id: skill_utils.c 6738 2008-02-19 18:46:14Z Humni $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <new_skills.h>
+
+protected void SkillResTransfer(mapping from_M, mapping to_M)
+{
+  if ( !mappingp(from_M) || !mappingp(to_M) )
+    return;
+
+  if ( member(from_M,SI_SKILLDAMAGE) )
+    to_M[SI_SKILLDAMAGE] = to_int(from_M[SI_SKILLDAMAGE]);
+
+  if ( member(from_M,SI_SKILLDAMAGE_MSG) )
+    to_M[SI_SKILLDAMAGE_MSG] = to_string(from_M[SI_SKILLDAMAGE_MSG]);
+
+  if ( member(from_M,SI_SKILLDAMAGE_MSG2) )
+    to_M[SI_SKILLDAMAGE_MSG2] = to_string(from_M[SI_SKILLDAMAGE_MSG2]);
+
+  if ( member(from_M,SI_SKILLDAMAGE_TYPE) )
+    to_M[SI_SKILLDAMAGE_TYPE] = from_M[SI_SKILLDAMAGE_TYPE];
+
+  if ( member(from_M,SI_SPELL) )
+    to_M[SI_SPELL] = from_M[SI_SPELL];
+}
+
diff --git a/std/living/skills.c b/std/living/skills.c
new file mode 100644
index 0000000..93c9dcc
--- /dev/null
+++ b/std/living/skills.c
@@ -0,0 +1,595 @@
+// MorgenGrauen MUDlib
+//
+// living/skills.c -- Gilden-, Skill- und Spellfunktionen fuer Lebewesen
+//
+// $Id: skills.c 8755 2014-04-26 13:13:40Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/std_skills";
+
+#define NEED_PROTOTYPES
+#include <living/skills.h>
+#include <living/skill_attributes.h>
+#include <player/life.h>
+#undef NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <new_skills.h>
+#include <wizlevels.h>
+
+// speichert die Spell-Fatigues (global, Spruchgruppen, Einzelsprueche)
+private mapping spell_fatigues = ([]);
+
+// Prototypen
+private void expire_spell_fatigues();
+
+protected void create()
+{
+  // mainly necessary for players, but there may be some NPC with savefiles.
+  // Additionally, it simplifies expiration of old keys to have it here.
+  call_out(#'expire_spell_fatigues, 4);
+}
+
+
+// Diese - hier scheinbar sinnlose - Funktion wird von /std/player/skills.c dann 
+// ueberladen.
+public int is_deactivated_skill(string sname, string gilde)
+{
+	return 0;
+}
+
+
+
+static string _query_visible_guild()
+{ string res;
+
+  if ( stringp(res=Query(P_VISIBLE_GUILD)) )
+    return res;
+
+  return QueryProp(P_GUILD);
+}
+
+static string _query_visible_subguild_title()
+{ string res;
+
+  if ( stringp(res=Query(P_VISIBLE_SUBGUILD_TITLE)) )
+    return res;
+
+  return QueryProp(P_SUBGUILD_TITLE);
+}
+
+static mixed _query_guild_prepareblock()
+{ mapping res;
+  string  gilde;
+
+  if ( !stringp(gilde=QueryProp(P_GUILD)) )
+    return 0;
+  if ( !mappingp(res=Query(P_GUILD_PREPAREBLOCK)) 
+      || !member(res,gilde) )
+    return 0;
+  return res[gilde];
+}
+
+static mixed _set_guild_prepareblock(mixed arg)
+{ mapping res;
+  string  gilde;
+
+  if ( !stringp(gilde=QueryProp(P_GUILD)) )
+    return 0;
+  if ( !mappingp(res=Query(P_GUILD_PREPAREBLOCK)) )
+    res=([]);
+
+  res[gilde]=arg;
+  Set(P_GUILD_PREPAREBLOCK,res);
+
+  return arg;
+}
+
+
+private nosave int valid_setskills_override;
+// Man sollte eigentlich ja nicht Parameter als globale Variablen
+// uebergeben, aber hier ging es nicht anders
+nomask private int valid_setskills(string gilde)
+{ string fn;
+
+  if ( !query_once_interactive(this_object()) )
+    return 1; // Monster duerfen sich selber Skills setzen :)
+
+  if ( QueryProp(P_TESTPLAYER) || IS_WIZARD(this_object()) )
+      return 1; // Testspieler und Magier sind schutzlose Opfer ;-)
+
+  if ( previous_object() )
+  {
+    if ( previous_object()==this_object()
+        && this_interactive()==this_object() )
+      return 1;
+
+    fn=object_name(previous_object());
+    if ( fn[0..7]=="/gilden/"
+        || fn[0..11]=="/spellbooks/"
+        || fn[0..7]=="/secure/"
+        || fn[0..11]=="/p/zauberer/" )
+      return 1; // Die sollten problemlos aendern duerfen
+
+    if ( file_size("/gilden/access_rights")>0
+       && call_other("/gilden/access_rights",
+                    "access_rights",
+                    getuid(previous_object()),
+                    gilde+".c"))
+      return 1; // Setzendes Objekt kommt vom Gildenprogrammierer
+
+    if ( file_size("/gilden/"+gilde+".c")>0
+        && call_other("/gilden/"+gilde,
+                      "valid_setskills",
+                        explode(fn,"#")[0]) )
+      return 1; // Die Gilde selber kann Ausnahmen zulassen
+  }
+
+  if (valid_setskills_override)
+  {
+    valid_setskills_override=0;
+    return 1; // Fuers Setzen der Closure
+  }
+
+  if ( this_interactive() )
+  {
+    if ( IS_ARCH(this_interactive()) )
+      return 1; // Erzmagier duerfen immer aendern
+
+    if ( call_other("/gilden/access_rights",
+                    "access_rights",
+                    getuid(this_interactive()),
+                    gilde+".c"))
+      return 1;  // Der Gildenprogrammierer selber auch
+  }
+
+  // Fuer die Waffenskills, die sollen sich selbst auch setzen duerfen
+  if (!this_interactive() && this_object()==previous_object())
+	  return 1;
+  
+  
+  log_file("SETSKILLS",sprintf("*****\n%s PO:%O->TO:%O TI:%O\n GUILD:%s VERB_ARGS:'%s'\n",
+            ctime(time())[4..15],
+            previous_object(),
+            this_object(),
+            this_interactive(),
+            gilde,
+            ( this_interactive() ? query_verb() + " " +
+                this_interactive()->_unparsed_args() : "") ));
+
+  return 0;
+}
+
+// Nur interne Verwendung, value wird nicht weiter prueft, muss ok sein.
+// Es wird keine Kopie von value gemacht, wenn es ins Mapping geschrieben
+// wird!
+private mapping internal_set_newskills(mapping value, string gilde) {
+  mapping skills;
+
+  // in der richtigen Gilde setzen.
+  if ( !gilde && !(gilde=QueryProp(P_GUILD)) )
+    gilde="ANY";
+
+  // Query(), hier ist eine Kopie nicht sinnvoll.
+  if ( !mappingp(skills=Query(P_NEWSKILLS,F_VALUE)) ) {
+    skills=([]);
+    Set(P_NEWSKILLS, skills, F_VALUE);
+  }
+
+  // Falls dies hier mal ausgewertet werden sollte, nicht vergessen, dass
+  // einige Funktion hier im File die Prop evtl. via
+  // internal_query_newskills() abrufen und direkt aendern...
+  valid_setskills(gilde); // Sicherheitsueberpruefung
+  
+  // Skills setzen. Set() unnoetig, weil wir das von Query() gelieferte
+  // Mapping aendern und das ja via Referenz bekommen haben.
+  skills[gilde]=value;
+  //Set(P_NEWSKILLS,skills);
+
+  return(value);
+}
+
+// nur zur internen Verwendung, es wird keine Kopie des Skillmappings gemacht!
+private mapping internal_query_newskills(string gilde) {
+  mapping skills;
+
+  // richtige Gilde abfragen.
+  if ( !gilde && !(gilde=QueryProp(P_GUILD)) )
+    gilde="ANY";
+
+  skills=Query(P_NEWSKILLS);
+
+  if (!mappingp(skills) || !mappingp(skills=skills[gilde]) )
+    return ([]);
+
+  return(skills);
+}
+
+// Eigentlich sollte man den _query-Funktionen keine Parameter geben...
+static varargs mapping _query_newskills(string gilde) {
+
+  // sonst Kopie des spellmappings liefern! Kostet zwar, aber verhindert
+  // einige andere Bugs und versehentliche Aenderungen an den Skills!
+  return(deep_copy(internal_query_newskills(gilde)));
+}
+
+// Eigentlich sollte man den _set-Funktionen keine weiteren Parameter geben
+static varargs mapping _set_newskills(mapping value, string gilde) {
+
+  // value auf Mappings normalisieren, ggf. Kopieren
+  if ( !mappingp(value) )
+      value=([]);
+  else
+      //zur Sicherheit, wer weiss, was der setzende noch damit macht...
+      value=deep_copy(value);
+
+  // und setzen...
+  internal_set_newskills(value, gilde);
+
+  // und noch ne Kopie von dem Liefern, was wir gesetzt haben (keine Referenz,
+  // sonst koennte der Aufrufende ja noch im Nachhinein aendern).
+  return(_query_newskills(gilde));
+}
+
+private mapping InternalQuerySkill(string sname, string gilde) {
+  mixed skill, skills;
+  // In is_any wird gespeichert, ob es ein gildenunabhaengier Skill ist,
+  // fuer die is_deactivate_skill-Abfrage.
+  int is_any;
+
+  // Skills komplett abfragen, keine spez. Gilde
+  if (!mappingp(skills=Query(P_NEWSKILLS,F_VALUE)))
+      return 0;
+
+  if (stringp(gilde) && sizeof(gilde)) {
+    //bestimmte Gilde angegeben, gut, dort gucken.
+    if (mappingp(skills[gilde]))
+      skill=skills[gilde][sname]; 
+  }
+  else {
+    gilde=QueryProp(P_GUILD); //reale Gilde holen
+    if (gilde && mappingp(skills[gilde]) && 
+	(skill=skills[gilde][sname])) {
+      // gibt es den Spell in der Gilde des Spielers?
+      // dann hier nix machen...
+    }
+    else if (mappingp(skills["ANY"])) {
+     // Zum Schluss: Gibt es den Skill vielleicht Gildenunabhaengig?
+      skill=skills["ANY"][sname];
+      // wenn man hier reinkommt, dann spaeter mit is_deactivated_skill() 
+      // pruefen!
+      is_any=1;
+    }
+  }
+
+  // wenn kein Skill gefunden, mit 0 direkt raus
+  if (!skill) return 0;
+
+  // Bei gildenunabhaengigen auch im Skillmapping vermerken
+  if ( is_any ) {	
+      skill+=([SI_GUILD:"ANY"]);	
+      // Ist er vielleicht in der Gilde des Spielers deaktiviert? 		
+      // Dies kann nur der Fall sein, wenn es kein Gildenskill ist.		
+      if (is_deactivated_skill(sname,gilde)) {		    
+	  return 0;		
+      }
+  }
+
+  return(skill);
+}
+
+public varargs mapping QuerySkill(string sname, string gilde) {
+ 
+    if (!stringp(sname) || !sizeof(sname))
+	return 0;
+
+    //Kopie zurueckliefern
+    return(deep_copy(InternalQuerySkill(sname,gilde)));
+}
+
+#define SMUL(x,y) ((x<0 && y<0)?(-1*x*y):(x*y))
+public varargs int QuerySkillAbility(string sname, string gilde)
+{ mapping skill;
+  string skill2;
+
+  if ( !(skill=InternalQuerySkill(sname, gilde)) )
+    return 0;
+
+  int val=skill[SI_SKILLABILITY];
+
+  if (skill2=skill[SI_INHERIT])
+  {
+    int val2;
+    val2=QuerySkillAbility(skill2);
+    val=(val*MAX_ABILITY+SMUL(val,val2))/(2*MAX_ABILITY);
+  }
+
+  return val;
+}
+
+protected varargs mixed LimitAbility(mapping sinfo, int diff)
+{ mixed abil;
+  int max,old,d2;
+
+  abil=sinfo[SI_SKILLABILITY];
+
+  if ( !intp(abil) )
+    return sinfo;
+  old=abil;
+
+  // Beim Spieler eingetragene Schwierigkeit gilt vor angegebener.
+  if ( (d2=sinfo[SI_DIFFICULTY]) )
+    diff=d2;
+
+  // diff <-100 soll nicht hemmen und macht keinen Sinn
+  diff=(diff<(-100))?(-100):diff;
+  
+  max=MAX_ABILITY-(diff+100)*(35-QueryProp(P_LEVEL));
+
+// diff|lvl 1:|   3:|	7:| 10:| 13:| 16:| 19:| 22:| 25:| 28:| 31:| 34:|
+// ----+------+-----+-----+----+----+----+----+----+----+----+----+----+
+//  -50|   83%|  84%|  86%| 87%| 89%| 90%| 92%| 93%| 95%| 96%| 98%| 99%|
+//  -10|   69%|  72%|  74%| 77%| 80%| 82%| 85%| 88%| 91%| 93%| 96%| 99%|
+//    0|   66%|  69%|  72%| 75%| 78%| 81%| 84%| 87%| 90%| 93%| 96%| 99%|
+//   10|   62%|  65%|  69%| 72%| 75%| 79%| 82%| 85%| 89%| 92%| 95%| 98%|
+//   20|   59%|  62%|  66%| 70%| 73%| 77%| 80%| 84%| 88%| 91%| 95%| 98%|
+//   30|   55%|  59%|  63%| 67%| 71%| 75%| 79%| 83%| 87%| 90%| 94%| 98%|
+//   40|   52%|  56%|  60%| 65%| 69%| 73%| 77%| 81%| 86%| 90%| 94%| 98%|
+//   50|   49%|  53%|  58%| 62%| 67%| 71%| 76%| 80%| 85%| 89%| 94%| 98%|
+//  100|   32%|  38%|  44%| 50%| 56%| 62%| 68%| 74%| 80%| 86%| 92%| 98%|
+//  150|   15%|  22%|  30%| 37%| 45%| 52%| 60%| 67%| 75%| 82%| 90%| 97%|
+//  200|   -2%|   7%|  16%| 25%| 34%| 43%| 52%| 61%| 70%| 79%| 88%| 97%|
+//  250|  -19%|  -8%|	2%| 12%| 23%| 33%| 44%| 54%| 65%| 75%| 86%| 96%|
+//  300|  -36%| -24%| -12%|  0%| 12%| 24%| 36%| 48%| 60%| 72%| 84%| 96%|
+//  400|  -70%| -55%| -40%|-25%|-10%|  5%| 20%| 35%| 50%| 65%| 80%| 95%|
+//  500| -104%| -86%| -68%|-50%|-32%|-14%|  4%| 22%| 40%| 58%| 76%| 94%|
+//  600| -138%|-117%| -96%|-75%|-54%|-33%|-12%|  9%| 30%| 51%| 72%| 93%|
+
+  if ( abil>max )
+    abil=max;
+  if ( abil>MAX_ABILITY
+    ) abil=MAX_ABILITY;
+  else if ( abil<-MAX_ABILITY )
+    abil=-MAX_ABILITY;
+
+  if ( old && !abil )
+    abil=1;
+  // Faehigkeiten sollen nicht durch die Begrenzung verschwinden
+
+  sinfo[SI_SKILLABILITY]=abil;
+
+  return sinfo;
+}
+
+public varargs void ModifySkill(string sname, mixed val, int diff, string gilde)
+{ 
+  mapping skills;
+  mixed skill;
+
+  if ( !stringp(sname) || !sizeof(sname) )
+    return;
+
+  // internal_query_newskills() macht keine Kopie
+  skills=internal_query_newskills(gilde);
+
+  // Skill ermitteln, wenn nicht existiert, wird er angelegt.
+  if (!skill=skills[sname]) {
+      skill=([]); 
+  }
+  
+  // Zur Sicherheit mal das Mapping kopieren, wer weiss, was der	
+  // Aufrufende dieser Funktion selber spaeter damit noch macht.
+  // ist ok, wenn val kein Mapping ist, dann macht deep_copy nix.
+  val=deep_copy(val);
+
+  // Skill und val vereinigen
+  if ( mappingp(val) )
+    skill+=val;
+  else if (intp(val))
+    skill[SI_SKILLABILITY]=val;
+  else
+    raise_error(sprintf("Bad arg 2 to ModifySkill(): expected 'int', "
+          "got %.10O.\n",val));
+
+  // Lernen entsprechend SI_DIFFICULTY begrenzen.
+  if(diff && !member(skill,SI_DIFFICULTY))
+    skill[SI_DIFFICULTY]=diff;
+  skill=LimitAbility(skill,diff || skill[SI_DIFFICULTY]);
+  
+  // schliesslich im Skillmapping vermerken. Im Normalfall ist der Skill jetzt
+  // schon geaendert, nicht erst nach dem internal_set_newskills().
+  skills[sname]=skill;
+
+  // explizites Abspeichern fast ueberfluessig, weil wir oben eine Referenz
+  // auf das Skillmapping gekriegt haben...
+  // Aber es koennte sein, dass dies der erste Skill fuer diese Gilde ist,
+  // dann ist es noetig. Zum anderen wird internal_set_newskills() nochmal
+  // geloggt.
+  internal_set_newskills(skills,gilde);
+}
+
+public varargs void LearnSkill(string sname, int add, int diff)
+{ mapping skill;
+  string skill2,gilde;
+  int val;
+
+  // Spieler sollen nur lernen, wenn sie interactive sind. Das soll
+  // natuerlich nur fuer Spieler gelten.
+  if (query_once_interactive(this_object()) && !interactive())
+	  return;
+
+  if ( add>MAX_SKILLEARN )
+    add=MAX_SKILLEARN;
+  else if ( add<1 )
+    add=1;
+
+  // Skillmapping ermitteln (hier kommt keine Kopie zurueck)
+  skill=InternalQuerySkill(sname, 0);
+  // wenn kein Skill, dann Abbruch
+  if (!skill) return;
+
+  val=skill[SI_SKILLABILITY];
+  gilde=skill[SI_GUILD];
+ 
+  val+=add;
+
+  ModifySkill(sname,val,diff,gilde);
+  if ( skill2=skill[SI_INHERIT] )
+    LearnSkill(skill2,add/3,diff);
+}
+
+public varargs int UseSpell(string str, string spell)
+{ string gilde,sbook;
+  mapping sinfo;
+  closure cl;
+  
+  if ( !spell && !(spell=query_verb()) )
+    return 0;
+
+  spell=lower_case(spell);
+
+  // QuerySkill() liefert eine Kopie des Skillmappings.
+  // wenn skill unbekannt oder Ability <= 0, ist der Spell nicht nutzbar.
+  if ( !(sinfo=QuerySkill(spell,0))
+        || sinfo[SI_SKILLABILITY] <= 0 )
+    return 0;
+
+  sinfo[SI_SKILLARG]=str; // Argument eintragen
+
+  if ( !closurep(cl=sinfo[SI_CLOSURE]) )
+  {
+    // Wenn ein Spellbook angegeben ist wird der Spell direkt ausgefuehrt
+    if ( stringp(sbook=sinfo[SI_SPELLBOOK]) )
+      cl=symbol_function("UseSpell",SPELLBOOK_DIR+sbook);
+
+    // Wenn der Spieler in einer Gilde ist, so weiss diese, in welchem
+    // Spellbook der Spell zu finden ist...
+    else if ( (gilde=QueryProp(P_GUILD)) && 
+      ( find_object(GUILD_DIR+gilde) || file_size(GUILD_DIR+gilde+".c")>-1))
+      cl=symbol_function("UseSpell",GUILD_DIR+gilde);
+    else
+      cl=function int () {return 0;};
+
+    sinfo[SI_CLOSURE]=cl;
+    valid_setskills_override=1;
+    ModifySkill(spell,([SI_CLOSURE:cl]),0,sinfo[SI_GUILD]);
+    valid_setskills_override=0;
+  }
+  return funcall(cl,this_object(),spell,sinfo);
+}
+
+public varargs mixed UseSkill(string skill, mapping args)
+{ mapping sinfo;
+  string gilde, func,skill2;
+  mixed res;
+  closure cl;
+  
+  if ( !skill ||
+       QueryProp(P_GHOST))
+    return 0;
+
+  skill=capitalize(skill);
+  // QuerySkill() liefert eine Kopie des Skillmappings
+  if ( !(sinfo=QuerySkill(skill,0)) )
+    return 0;
+
+  if (args)
+    sinfo+=args;
+
+  if ( !closurep(cl=sinfo[SI_CLOSURE]) )
+  {
+    if ( !(func=sinfo[SI_SKILLFUNC])    // Keine Funktion angegeben?
+        || !(gilde=QueryProp(P_GUILD))) // Keine Gilde angegeben?
+    {
+      // Dann Standard-Funktion nehmen, wenn es die nicht gibt, den
+      // Ability-Wert zurueckliefern.
+      if (!closurep(cl = symbol_function("StdSkill_"+skill,this_object())))
+        cl=function int (object ob, string sname)
+             {return QuerySkillAbility(sname);} ;
+    }
+    else
+    {
+      // Sonst diese Funktion im Gildenobjekt aufrufen
+      cl=symbol_function(func,GUILD_DIR+gilde);
+    }
+
+    sinfo[SI_CLOSURE]=cl;
+    valid_setskills_override=1;
+    ModifySkill(skill,([SI_CLOSURE:cl]),0,sinfo[SI_GUILD]);
+    valid_setskills_override=0;
+  }
+
+  res=funcall(cl,this_object(),skill,sinfo);
+  if ( (skill2=sinfo[SI_INHERIT]) && mappingp(res) )
+    res=UseSkill(skill2,res); // Fuer Skills, die von anderen abhaengen
+
+  return res;
+}
+
+// ************** Spellfatigues ***************
+
+/*  Prueft die Spellfatigue fuer Spruch(gruppe) <key>.
+ *  <key> darf 0 sein (globale Spruchsperre).
+ *  Liefert 0, wenn keine Sperre und die Ablaufzeit, wenn eine Sperre noch
+ *  gueltig. ist.
+ */
+public varargs int CheckSpellFatigue(string key) {
+  // key==0 is the (default) global spellfatigue.
+  if (spell_fatigues[key] > time())
+    return spell_fatigues[key]; // Ablaufzeit zurueckgeben.
+
+  return 0; // ok, keine Sperre.
+}
+
+/** Speichert eine Spellfatigue von <duration> Sekunden fuer <key>.
+ * <key> darf 0 sein und bezeichnet das globale Spellfatigue.
+ * Rueckgabewert: Ablaufzeit der gesetzten Sperre
+                  -1, wenn noch eine nicht-abgelaufene Sperre auf dem <key> lag.
+                  0, wenn duration 0 ist.
+ */
+public varargs int SetSpellFatigue(int duration, string key) {
+  // aktuelle Sperre abgelaufen?
+  if (CheckSpellFatigue(key))
+    return -1; // alte Sperre noch aktiv.
+
+  duration += time();
+  // 0 is OK for <key>, it is the key for global spell fatigues
+  spell_fatigues[key] = duration;
+  return duration;
+}
+
+/*  Prueft die Spellfatigue fuer Spruch(gruppe) <key>.
+ *  <key> darf fuer diese Funktion 0 (globale Spruchsperre) sein, aber man
+ *  darf das Argument nicht weglassen, damit nicht ein verpeilter Magier
+ *  versehentlich die globale Spruchsperre nullt.
+ */
+public void DeleteSpellFatigue(string key) {
+  // key==0 is the (default) global spellfatigue.
+  m_delete(spell_fatigues, key);
+}
+
+/** Loescht abgelaufene Keys aus dem spell_fatigue mapping.
+ */
+private void expire_spell_fatigues() {
+  foreach(string key, int endtime: spell_fatigues) {
+    if (endtime <= time())
+      m_delete(spell_fatigues, key);
+  }
+}
+
+/** Setmethode fuer P_NEXT_SPELL_TIME.
+  */
+static int _set_next_spell(int fatigue) {
+  return SetSpellFatigue(fatigue - time());
+}
+/** Querymethode fuer P_NEXT_SPELL_TIME.
+  */
+static int _query_next_spell() {
+  return CheckSpellFatigue();
+}
+
diff --git a/std/living/std_skills.c b/std/living/std_skills.c
new file mode 100644
index 0000000..d132efc
--- /dev/null
+++ b/std/living/std_skills.c
@@ -0,0 +1,104 @@
+// MorgenGrauen MUDlib
+//
+// living/std_skills.c -- Standardfaehigkeiten
+//
+// $Id: std_skills.c 6673 2008-01-05 20:57:43Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <living/skills.h>
+#include <living/skill_attributes.h>
+#include <thing/properties.h>
+#include <attributes.h>
+#undef NEED_PROTOTYPES
+#include <properties.h>
+#include <new_skills.h>
+
+#define SIG(x) (x?(x>0?1:-1):0)
+
+protected int StdSkill_Nightvision(object me, string sname, mixed sinfo) {
+  int abil,light,llt,dt,res;
+
+  if (!sinfo || !environment()) return 0;
+  if (intp(sinfo)) sinfo=([SI_SKILLABILITY:sinfo]);
+  if (!mappingp(sinfo)) return 0;
+  if ((light=QueryProp(P_PLAYER_LIGHT))>0) {
+    ModifySkill(sname,([SI_LASTLIGHT:time()]));
+    return light;
+  }
+  abil=sinfo[SI_SKILLABILITY];
+  if (!(llt=sinfo[SI_LASTLIGHT])) {
+    ModifySkill(sname,([SI_LASTLIGHT:time()]));
+    dt=0;
+  } else {
+    dt=time()-llt;
+    if (dt<0) dt=0;
+    if (dt>86400) dt=86400;
+  }
+
+  res=(abil*dt)/(20*MAX_ABILITY)+light;
+  if (res<=0) {
+    res--; // Wert muss !=0 sein
+    if (res<-MAX_ABILITY) res=-MAX_ABILITY;
+  } else {
+    if (res>MAX_ABILITY) res=MAX_ABILITY;
+  }
+  return res;
+}
+
+protected mapping StdSkill_Bihand(object me, string sname, mapping sinfo) {
+  int abil,val;
+
+  // printf("Bihand: %O\n",sinfo);
+  if (!sinfo) return 0;
+  abil=sinfo[SI_SKILLABILITY];
+  val=(abil*(QueryAttribute(A_STR)+33))/MAX_ABILITY;
+  val=(val*QuerySkillAttribute(SA_DAMAGE))/100;
+  sinfo[SI_SKILLDAMAGE]+=val;
+  // + max. 53
+  return sinfo;
+}
+
+protected mapping StdSkill_Fight_sword(object me, string sname, mapping sinfo) {
+  int abil,asig,val;
+
+  abil=sinfo[SI_SKILLABILITY];asig=SIG(abil);
+  val=(abil*(QueryAttribute(A_STR) +
+             QueryAttribute(A_DEX)*asig +
+             33))/MAX_ABILITY;
+  val=(val*QuerySkillAttribute(SA_DAMAGE))/100;
+  sinfo[SI_SKILLDAMAGE]+=val;
+  // + max. 73
+  return sinfo;
+}
+
+protected mapping StdSkill_Fight_hands(object me, string sname, mapping sinfo) {
+  int abil,asig,val;
+
+  if (!sinfo) return 0;
+  abil=sinfo[SI_SKILLABILITY];asig=SIG(abil);
+  val=(abil*(QueryAttribute(A_STR) +
+             QueryAttribute(A_DEX)*3*asig +
+             100))/MAX_ABILITY;
+  val=(val*QuerySkillAttribute(SA_DAMAGE))/100;
+  sinfo[SI_SKILLDAMAGE]+=val;
+  // + max. 180
+  return sinfo;
+}
+
+protected int StdSkill_Booze(object me, string sname, mapping sinfo) {
+  int abil,val;
+
+  val=0;
+  if (!sinfo || (val=sinfo[SI_SKILLARG])<=0)
+    return val;
+  abil=sinfo[SI_SKILLABILITY];
+  val-=(val*abil)/(MAX_ABILITY+2500); // Bis zu 80% Abzug bei Alkoholikern.
+  if (val<=0) val=1;
+  return val;
+}
+
diff --git a/std/living/team.c b/std/living/team.c
new file mode 100644
index 0000000..2b31db9
--- /dev/null
+++ b/std/living/team.c
@@ -0,0 +1,650 @@
+// MorgenGrauen MUDlib
+//
+// living/team.c
+//
+// $Id: team.c 9138 2015-02-03 21:46:56Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include <properties.h>
+#include <thing/properties.h>
+#include <living/combat.h>
+#include <combat.h>
+#include <living/team.h>
+#include <wizlevels.h>
+#include <hook.h>
+
+#define ME this_object()
+#define TP this_player()
+#define PO previous_object()
+#define ENV environment()
+
+private nosave string team_attack_cmd;
+private nosave mapping team_follow_todo;
+private nosave int team_autofollow;
+private nosave object teammove;
+
+void create() {
+  Set(P_TEAM_ATTACK_CMD,-1,F_SET_METHOD);
+  Set(P_TEAM_ATTACK_CMD,PROTECTED,F_MODE_AS);
+  Set(P_TEAM_AUTOFOLLOW,-1,F_SET_METHOD);
+  Set(P_TEAM_AUTOFOLLOW,PROTECTED,F_MODE_AS);
+  teammove=0;
+  offerHook(H_HOOK_TEAMROWCHANGE, 1);
+}
+
+void add_team_commands() {
+  add_action("teamcmd","gruppe");
+  add_action("teamcmd","g");
+  add_action("teamcmd","team");
+}
+
+string _query_team_attack_cmd() {
+  return Set(P_TEAM_ATTACK_CMD,team_attack_cmd);
+}
+
+int _query_team_autofollow() {
+  return Set(P_TEAM_AUTOFOLLOW,team_autofollow);
+}
+
+private int team_help() {
+  // Syntax-Kompatiblitaet (Avalon) ist ganz nett :-)
+  write("\
+(Befehle des Teamleiters sind mit * gekennzeichnet\n\
+\n\
+* team angriff\n\
+  team angriffsbefehl <befehl>\n\
+* team aufnahme <name>\n\
+  team autof[olge] <ein/aus>\n\
+* team autoi[nfo] <ein/aus> [+[lp]] [+[kp]] [sofort]\n\
+* team entlasse <name>\n\
+  team farben lp_rot lp_gelb kp_rot kp_gelb\n\
+  team flucht[reihe] <reihe>\n\
+  team folge <name>\n\
+* team formation <min[-max]> [<min[-max]> ...]\n\
+  team hilfe|?\n\
+  team [info] [sortiert|alphabetisch]\n\
+  team [kampf]reihe <reihe>\n\
+* team leiter[in] <name>\n\
+  team liste\n\
+* team name <gruppenname>\n\
+  team orte [alle]\n\
+  team ruf[e]\n\
+  team uebersicht\n\
+  team verlasse\n");
+  return 1;
+}
+
+object IsTeamLeader() {
+  object team;
+
+  if (!objectp(team=Query(P_TEAM))
+      || team!=Query(P_TEAM_LEADER)
+      || team->Leader()!=ME)
+    return 0;
+  return team;
+}
+
+object *TeamMembers() {
+  object team;
+
+  if (!objectp(team=Query(P_TEAM)))
+    return ({ME});
+  return team->Members();
+}
+
+string TeamPrefix() {
+  object team;
+
+  if (!objectp(team=Query(P_TEAM)))
+    return "";
+  return "["+team->Name()+"] ";
+}
+
+
+private int team_aufnahmewunsch(string arg) {
+  object pl;
+
+  if ((!objectp(pl=find_player(arg)) && !objectp(pl=present(arg,ENV)))
+      || pl->QueryProp(P_INVIS) || environment(pl)!=ENV)
+    return notify_fail(capitalize(arg)+" nicht gefunden.\n"),0;
+  if (!living(pl))
+    return notify_fail(pl->Name(WER)+" ist etwas zu inaktiv.\n"),0;
+  if (pl==ME)
+    return notify_fail("Du bist eine Person zu wenig fuer ein Team.\n"),0;
+  SetProp(P_TEAM_NEWMEMBER,pl);
+  if (pl->IsTeamLeader()) {
+    write("Du bittest "+pl->name(WEN)+" um Aufnahme ins Team.\n");
+    tell_object(pl,TP->Name(WER)+" bittet Dich um Aufnahme ins Team.\n");
+  } else {
+    write("Du bittest "+pl->name(WEN)+" um Gruendung eines Teams.\n");
+    tell_object(pl,TP->Name(WER)+" bittet Dich um Gruendung eines Teams.\n");
+  }
+  return 1;
+}
+
+private int team_aufnahme(string arg) {
+  object pl,team;
+  int res;
+
+  if ((!objectp(pl=find_player(arg)) && !objectp(pl=present(arg,ENV)))
+      || pl->QueryProp(P_INVIS) || environment(pl)!=ENV)
+    return notify_fail(capitalize(arg)+" nicht gefunden.\n"),0;
+  if (pl->QueryProp(P_TEAM_NEWMEMBER)!=ME)
+    return notify_fail(pl->Name(WER)+" hat Dich nicht um Aufnahme gebeten.\n"),
+      0;
+  if (pl==ME)
+    return notify_fail("Du bist eine Person zu wenig fuer ein Team.\n"),0;
+  if (!objectp(team=QueryProp(P_TEAM)))
+    team=clone_object(TEAM_OBJECT);
+  res=team->AddMember(pl);
+  if (!sizeof(team->Members()))
+    team->remove();
+  return res;
+}
+
+object IsTeamMove() {
+  if (!objectp(teammove) || (teammove!=Query(P_TEAM)))
+    teammove=0;
+  return teammove;
+}
+
+static void DoTeamAttack(object env, object callbackto) {
+  if (env==ENV && stringp(team_attack_cmd) && !IS_LEARNER(ME)
+      && (interactive(ME) || !query_once_interactive(ME))
+      && objectp(callbackto) && callbackto==Query(P_TEAM)) {
+    teammove=callbackto;
+    command(team_attack_cmd);
+  }
+  if (objectp(callbackto))
+    callbackto->TeamAttackExecuted_Callback(teammove?1:0);
+  teammove=0;
+}
+
+int CallTeamAttack(object env) {
+  if (stringp(team_attack_cmd)
+      && find_call_out("DoTeamAttack")<0
+      && PO
+      && PO==Query(P_TEAM))
+    return call_out("DoTeamAttack",0,env,PO),1;
+  return 0;
+}
+
+static int DoTeamFollow() {
+  string cmd;
+
+  if (!team_autofollow
+      || (!interactive(ME) && query_once_interactive(ME))
+      || IS_LEARNER(ME)
+      || !mappingp(team_follow_todo))
+    return 0;
+  if (!stringp(cmd=team_follow_todo[ENV]))
+    return team_follow_todo=0;
+
+  do {
+    m_delete(team_follow_todo,ENV);
+    tell_object(ME,sprintf("Du folgst Deinem Team mit \"%s\".\n",cmd));
+    command(cmd);
+  } while (get_eval_cost()>900000 && random(1000)>20 && objectp(ME)
+           && stringp(cmd=team_follow_todo[ENV]));
+
+  // Ist Spieler in Umgebung gelandet, fuer die noch ein
+  // Befehl auszufuehren ist?
+  if (!objectp(ME) || !stringp(team_follow_todo[ENV]))
+    return team_follow_todo=0;
+  while  (remove_call_out("DoTeamFollow")!=-1) ;
+  call_out("DoTeamFollow",0);
+  return 0;
+}
+
+int CallTeamFollow(object env, string cmd) {
+  if (!team_autofollow
+      || PO!=Query(P_TEAM)
+      || !PO
+      || !objectp(env)
+      || !stringp(cmd))
+    return 0;
+  if (!mappingp(team_follow_todo))
+    team_follow_todo=([]);
+  if (ENV!=env && !team_follow_todo[ENV])
+    return 0;
+  team_follow_todo[env]=cmd;
+  if (find_call_out("DoTeamFollow")<0)
+    call_out("DoTeamFollow",0);
+  return 1;
+}
+
+int ClearTeamFollow() {
+  if (PO!=Query(P_TEAM) || !PO)
+    return 0;
+  team_follow_todo=([]);
+  return 1;
+}
+
+mixed *PresentTeamRows() {
+  object team;
+  mixed *res;
+  int i;
+
+  if (!objectp(team=Query(P_TEAM))) {
+    res=EMPTY_TEAMARRAY;
+    res[0]=({ME});
+    return res;
+  }
+  res=team->PresentRows(ENV);
+  for (i=0;i<MAX_TEAMROWS;i++)
+    if (member(res[i],ME)>=0)
+      return res;
+  res[0]+=({ME});
+  return res;
+}
+
+varargs mixed *PresentEnemyRows(object *here) {
+  mixed *res,*rows;
+  mapping added_teams;
+  int i,j;
+  object ob,team;
+
+  added_teams=([Query(P_TEAM):1]); // Nicht auf eigenes Team hauen
+  res=EMPTY_TEAMARRAY;
+  if (!pointerp(here))
+    here=PresentEnemies();
+  for (i=sizeof(here)-1;i>=0;i--) {
+    if (!objectp(ob=here[i]))
+      continue;
+    if (!objectp(team=ob->QueryProp(P_TEAM))) {
+      res[0]+=({ob});
+      continue;
+    }
+    if (added_teams[team])
+      continue;
+    added_teams[team]=1;
+    rows=team->PresentRows(ENV);
+    for (j=0;j<MAX_TEAMROWS;j++)
+      res[j]+=rows[j];
+  }
+  return res;
+}
+
+varargs object SelectNearEnemy(object *here, int forcefrom) {
+  object ob,en,team;
+  mixed *rows;
+  int *prob,prot,i,r,sz,upsz,sum;
+
+  if (!pointerp(here))
+    here=PresentEnemies();
+  if (!objectp(ob=SelectEnemy(here)))
+    return 0;
+  en=ob->QueryProp(P_TEAM);          // Feindliches Team
+  if (objectp(team=Query(P_TEAM))) { // Eigenes Team
+    if (en==team) // Feind im eigenen Team, kein ANDERES Mitglied waehlen.
+      return ob;  // Aber auch ausserhalb Reihe 1 draufhauen
+    rows=team->PresentRows(ENV);
+    if (member(rows[0],ME)<0) // Stehe ich in der ersten Reihe?
+      return 0; // Falls nein ist auch kein Gegner nahe.
+  }
+  if (!objectp(en))
+    return ob; // Ist nicht in einem Team, also drauf.
+  rows=en->PresentRows(environment(ob));
+  prob=({1,0,0,0,0});
+  prot=sum=0;
+  for (i=0;i<MAX_TEAMROWS;i++) {
+    if (prot>0) prot--;                // Schutzkegel nimmt ab.
+    if (!sz=sizeof(rows[i])) continue; // Gegner in dieser Reihe
+    upsz=sz-prot;if (upsz<0) continue; // Anzahl ungeschuetzter Gegner
+    prob[i]+=(upsz+sum);               // Wahrscheinlichkeit += ungeschuetzt
+    sum=prob[i];                       // Summe bisheriger Wahrscheinlichkeiten
+    if (sz>prot) prot=sz;              // Neuer Schutzkegel
+  }
+  r=random(sum);
+  for (i=0;i<MAX_TEAMROWS;i++)
+    if (r<prob[i])
+      break;
+  if (i>=MAX_TEAMROWS)
+    i=0;
+  if (objectp(en=SelectEnemy(forcefrom?(here&rows[i]):rows[i])))
+    return en;
+  if (i && objectp(en=SelectEnemy(forcefrom?(here&rows[0]):rows[0])))
+    return en;
+  return ob;
+}
+
+varargs object SelectFarEnemy(object *here, int min, int max, int forcefrom) {
+  mixed *rows;
+  int *prob,i,r,sum;
+  object en;
+
+  if (max<0 || min>=MAX_TEAMROWS || max<min)
+    return 0;
+  if (min<0) min=0;
+  if (max>=MAX_TEAMROWS) max=MAX_TEAMROWS-1;
+  if (!pointerp(here))
+    here=PresentEnemies();
+  rows=PresentEnemyRows(here);
+  prob=({0,0,0,0,0});
+  sum=0;
+  for (i=min;i<=max;i++)
+    sum=prob[i]=sum+sizeof(rows[i])+max-i;
+
+  r=random(sum);
+  for (i=min;i<=max;i++)
+    if (r<prob[i])
+      break;
+  if (i>max)
+    i=min;
+  if (objectp(en=SelectEnemy(forcefrom?(here&rows[i]):rows[i])))
+    return en;
+  for (i=min;i<=max;i++)
+    if (objectp(en=SelectEnemy(forcefrom?(here&rows[i]):rows[i])))
+      return en;
+  return 0;
+}
+
+mixed _query_friend() {
+  mixed res;
+
+  if (res=Query(P_FRIEND))
+    return res;
+  if (objectp(res=Query(P_TEAM_ASSOC_MEMBERS))
+      && query_once_interactive(res))
+    return res;
+  return 0;
+}
+
+int DeAssocMember(object npc) {
+  mixed obs;
+  object team;
+
+  if (extern_call() && PO!=npc &&
+      member(({"gilden","spellbooks"}),
+             explode(object_name(PO),"/")[1])<0)
+    return 0;
+  obs=QueryProp(P_TEAM_ASSOC_MEMBERS);
+  if (!pointerp(obs))
+    return 0;
+  obs-=({npc,0});
+  SetProp(P_TEAM_ASSOC_MEMBERS,obs);
+  if (objectp(team=QueryProp(P_TEAM)))
+    team->RemoveAssocMember(ME,npc);
+  return 1;
+}
+
+int AssocMember(object npc) {
+  mixed obs;
+  object team;
+
+  if (extern_call() && PO!=npc &&
+      member(({"gilden","spellbooks"}),
+             explode(object_name(PO),"/")[1])<0)
+    return 0;
+  if (!objectp(npc)
+      || npc->QueryProp(P_TEAM_ASSOC_MEMBERS)
+      || IsEnemy(npc)
+      || npc==ME
+      || query_once_interactive(npc))
+    return 0;
+  obs=QueryProp(P_TEAM_ASSOC_MEMBERS);
+  if (objectp(obs))
+    return 0;
+  if (!pointerp(obs))
+    obs=({});
+  obs=(obs-({npc,0}))+({npc});
+  SetProp(P_TEAM_ASSOC_MEMBERS,obs);
+  npc->SetProp(P_TEAM_ASSOC_MEMBERS,ME);
+  if (objectp(team=QueryProp(P_TEAM)))
+    team->AddAssocMember(ME,npc);
+  return 1;
+}
+
+varargs void InsertEnemyTeam(mixed ens, int rek) {
+  object *obs,ob,eteam,team;
+  int i;
+
+  team=Query(P_TEAM);
+  // Alle Teammitglieder des Gegners sind Feind:
+  if (objectp(ens)) {
+    if (objectp(eteam=ens->QueryProp(P_TEAM))) {
+      if (eteam==team) // feindliches Team = eigenes Team?
+        return;        // also nicht alle Teammitglieder gegeneinander hetzen
+      ens=eteam->Members();
+    } else {
+      ens=({ens});
+    }
+  }
+  if (!pointerp(ens))
+    return;
+  ens-=({ME});
+
+  // Interactives sollen keine Interactives durch Team angreifen:
+  if (query_once_interactive(ME)) {
+    for (i=sizeof(ens)-1;i>=0;i--)
+      if (objectp(ob=ens[i]) && environment(ob)==environment()
+          && !query_once_interactive(ob))
+        InsertSingleEnemy(ob);
+  } else {
+    for (i=sizeof(ens)-1;i>=0;i--)
+      if (objectp(ob=ens[i]) && environment(ob)==environment())
+        InsertSingleEnemy(ob);
+  }
+
+  // Alle anderen Teammitglieder Informieren:
+  if (rek || !objectp(team) || !pointerp(obs=team->Members()))
+    return;
+  obs-=({ME});
+  obs-=ens;
+  for (i=sizeof(obs)-1;i>=0;i--)
+    if (objectp(ob=obs[i]))
+      ob->InsertEnemyTeam(ens,1);
+}
+
+int TeamFlee() {
+  object team;
+
+  if (Query(P_TEAM_WIMPY_ROW)<2 || !objectp(team=Query(P_TEAM)))
+    return 0;
+  if (!team->FleeToRow(ME))
+     return 0;
+  if (Query(P_TEAM_LEADER)==team) {
+    if (team_autofollow)
+      tell_object(ME,"Du versuchst zu fliehen, "+
+                  "Dein Team folgt Dir nicht mehr.\n");
+    team_autofollow=0;
+  }
+  return 1;
+}
+
+varargs mapping PresentTeamPositions(mixed pres_rows) {
+  mapping res;
+  int i,j;
+  object *obs,ob;
+
+  res=([]);
+  if (!pointerp(pres_rows))
+    pres_rows=PresentTeamRows();
+  for (i=0;i<MAX_TEAMROWS;i++) {
+    obs=pres_rows[i];
+    for (j=sizeof(obs)-1;j>=0;j--)
+      if (objectp(ob=obs[j]) && !res[ob])
+        res[ob]=i+1;
+  }
+  return res;
+}
+
+varargs int PresentPosition(mixed pmap) {
+  object team;
+  int i;
+
+  if (!objectp(team=Query(P_TEAM)))
+    return 1;
+  if (mappingp(pmap))
+    return pmap[ME];
+  if (!pointerp(pmap))
+    pmap=team->PresentRows(ENV);
+  for (i=1;i<MAX_TEAMROWS;i++)
+    if (member(pmap[i],ME)>=0)
+      return i+1;
+  return 1;
+}
+
+#define FILLSTRING "                                        "
+varargs private string center_string(string str, int w) {
+  return (FILLSTRING[0..((w-sizeof(str))/2-1)]+str+FILLSTRING)[0..(w-1)];
+}
+
+private int ShowTeamRows() {
+  int i,j,sz;
+  mixed *pres_rows;
+  object *obs,ob;
+  string str;
+
+  pres_rows=PresentEnemyRows();
+  for (sz=MAX_TEAMROWS-1;sz>=0;sz--)
+    if (sizeof(pres_rows[sz]))
+      break;
+  for (i=sz;i>=0;i--) {
+    obs=pres_rows[i];str="";
+    for (j=sizeof(obs)-1;j>=0;j--)
+      if (objectp(ob=obs[j])) {
+        if (str!="") str+=" / ";
+        str+=ob->Name(RAW);
+      }
+    printf("%d. %s\n",i+1,center_string(str,75));
+  }
+  if (sz>=0)
+    write("   ---------------------------------------------------------------------------\n");
+  pres_rows=PresentTeamRows();
+  for (sz=MAX_TEAMROWS-1;sz>0;sz--)
+    if (sizeof(pres_rows[sz]))
+      break;
+  for (i=0;i<=sz;i++) {
+    obs=pres_rows[i];str="";
+    for (j=sizeof(obs)-1;j>=0;j--)
+      if (objectp(ob=obs[j])) {
+        if (str!="") str+=" / ";
+        str+=ob->Name(RAW);
+      }
+    printf("%d. %s\n",i+1,center_string(str,75));
+  }
+  return 1;
+}
+
+varargs int team_list(string arg) {
+  object *tobs,*obs,tob,ob,ld;
+  string *nms,*tnms,str;
+  int i,j;
+
+  if (!pointerp(tobs=TEAM_MASTER->ListTeamObjects())) return 0;
+  if (arg!="alle") arg=0;
+  tnms=({});
+  for (i=sizeof(tobs)-1;i>=0;i--) {
+    if (!objectp(tob=tobs[i])
+        || !objectp(ld=tob->Leader())
+        || (!query_once_interactive(ld) && !arg)
+        || !pointerp(obs=tob->Members()))
+      continue;
+    nms=({});
+    for (j=sizeof(obs)-1;j>=0;j--) {
+      if (!objectp(ob=obs[j])
+          || (!query_once_interactive(ob) &&!arg))
+        continue;
+      if (!stringp(str=ob->Name(WER))) str="?";
+      if (ob==ld) str+="(*)";
+      nms+=({str});
+      nms=sort_array(nms,#'>);
+    }
+    if (!stringp(str=tob->Name())) str="Team ?";
+    str+=": ";
+    tnms+=({break_string(implode(nms,", "),78,str)});
+    tnms=sort_array(tnms,#'<);
+  }
+  if (sizeof(tnms))
+    tell_object(ME, sprintf("%@s\n", tnms));
+  else
+    tell_object(ME, "Keine Teams gefunden.\n"); 
+  
+  return 1;
+}
+
+varargs int teamcmd(string arg) {
+  string *words,narg;
+  object team;
+
+  if (!arg)
+    arg="";
+  if (!stringp(narg=TP->_unparsed_args()))
+    narg = arg;
+  if (!sizeof(words=explode(arg," ")))
+    return 0;
+ 
+  if (sizeof(words) > 1) {
+      arg=implode(words[1..]," ");
+      narg = implode(explode(narg, " ")[1..], " ");
+  }
+  else
+      arg = narg = "";
+
+  switch(words[0]) { // Befehle die keine Mitgliedschaft erfordern:
+  case "aufnahme":
+    return team_aufnahme(arg);
+  case "folge":
+    return team_aufnahmewunsch(arg);
+  case "?":
+  case "hilfe":
+    return team_help();
+  case "liste":
+    return team_list(arg);  
+  case "uebersicht":
+    return ShowTeamRows();
+  default:;
+  }
+
+  if (!objectp(team=QueryProp(P_TEAM)))
+    return notify_fail("Du bist in keinem Team.\n"),0;
+
+  switch(words[0]) {
+  case "angriffsbefehl":
+    if (narg=="") narg=0;
+    team_attack_cmd=narg;
+    if (stringp(narg))
+      write("Du beginnst den Kampf mit \""+narg+"\"\n");
+    else
+      write("Du hast den Teamangriffsbefehl deaktiviert.\n");
+    break; // NICHT return!
+  case "autofolge":
+  case "autof":
+    if (arg=="ein" || arg=="an") {
+      team_autofollow=1;
+      if (IsTeamLeader())
+        write("Dein Team folgt Dir.\n");
+      else
+        write("Du folgst jetzt dem Teamleiter.\n");
+    } else {
+      team_autofollow=0;
+      if (IsTeamLeader())
+        write("Dein Team folgt Dir nicht mehr.\n");
+      else
+        write("Du folgst jetzt nicht mehr dem Teamleiter.\n");
+    }
+    break; // NICHT return!
+  default: ;
+  }
+  return team->TeamCmd(words[0],narg); // Befehle die Mitgliedschaft erfordern:
+}
+
+varargs void InformRowChange(int from, int to, object caster) {
+
+  if (caster) return; // Fuer den Fall, dass Gildenobjekt==ME ist
+  if (PO!=Query(P_TEAM)) return;
+#if __BOOT_TIME__ < 1281904437
+  mixed gilde = QueryProp(P_GUILD);
+  if (!stringp(gilde)) return;
+  if (!objectp(gilde=find_object("/gilden/"+gilde))) return;
+  gilde->InformRowChange(from,to,ME);
+#endif
+  HookFlow(H_HOOK_TEAMROWCHANGE, ({from,to}) );
+}
diff --git a/std/mailcabin.c b/std/mailcabin.c
new file mode 100644
index 0000000..1a94e85
--- /dev/null
+++ b/std/mailcabin.c
@@ -0,0 +1,12 @@
+#pragma strict_types,save_types
+
+inherit "/p/service/loco/std/mailcabin";
+
+void create() {
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);
+      return;
+  }
+  ::create();
+}
+
diff --git a/std/more.c b/std/more.c
new file mode 100644
index 0000000..8518981
--- /dev/null
+++ b/std/more.c
@@ -0,0 +1,143 @@
+// MorgenGrauen MUDlib
+//
+// more.c -- more files
+//
+// $Id: more.c 9142 2015-02-04 22:17:29Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/util/pager";
+
+#include <pager.h>
+
+#ifdef GERMAN
+# define MSG_WRAPPED	" VOM ANFANG"
+# define MSG_NOT_FOUND	" NICHTS GEFUNDEN"
+# define MSG_NO_REGX	" FEHLENDER REG. AUSDRUCK"
+# define MSG_ILLEGAL	" ILLEGALE ZEILENZAHL"
+# define MSG_HELP       "\
+Hilfe fuer more:\n\
+\n\
+b,B        -- Eine Seite zurueck.\n\
+u,U        -- Eine halbe Seite zurueck.\n\
+f,F        -- Eine Seite vorwaerts.\n\
+d,D        -- Eine halbe Seite vorwaerts.\n\
+<Zeile>    -- Springe zu Zeile <Zeile>\n\
+q,x        -- Verlassen von more.\n\
+/<RegExp>  -- Nach dem regulaeren Ausdruck <RegExp> suchen.\n"
+#else
+# define MSG_WRAPPED	" WRAPPED"
+# define MSG_NOT_FOUND	" NOTHING FOUND"
+# define MSG_NO_REGX	" NO PREV REGULAR EXPR"
+# define MSG_ILLEGAL	" ILLEGAL LINE NUMBER"
+# define MSG_HELP       "\
+Help for more:\n\
+\n\
+b,B        -- One page back.\n\
+u,U        -- Half a page back.\n\
+f,F        -- One page forward.\n\
+d,D        -- Half a page forward.\n\
+<line>     -- Jump to line number <line>\n\
+q,x        -- Quit or eXit more.\n\
+/<regexp>  -- Search for the regular expression <regexp>.\n"
+#endif
+
+private nosave string cprompt = "";
+
+string prompt(mixed pinfo, string add)
+{
+  int line,max; max = 1;
+  if(pointerp(pinfo))
+  {
+     if(pinfo[CURL] + pinfo[PAGE] >= pinfo[MAXL]) line = pinfo[MAXL];
+     else line = pinfo[CURL] + pinfo[PAGE];
+     max = pinfo[MAXL];
+  }
+  if (pinfo[FLAG] & E_ABS)
+    return sprintf("%s (%d/%d)%s %s",
+		   ::prompt(pinfo, ""),
+		   line-1, pinfo[MAXL],
+		   cprompt, stringp(add)?add:"");
+  else
+    return sprintf("%s(%d%%)%s %s",
+		   ::prompt(pinfo, ""),
+		   line*100/max, 
+		   cprompt, stringp(add)?add:"");
+}
+
+int search(mixed pinfo)
+{
+  int l, lines;
+  mixed tmp;
+  l = pinfo[CURL];
+  while((tmp = fread(pinfo, l, pinfo[PAGE])) &&
+        !sizeof(regexp(old_explode(tmp, "\n"), pinfo[REGX])))
+    l += pinfo[PAGE];
+  if(!tmp)
+  {
+    cprompt += MSG_WRAPPED;
+    l = 1;
+    while(l < pinfo[CURL] &&
+          (tmp = fread(pinfo, l, pinfo[PAGE])) &&
+          !sizeof(regexp(old_explode(tmp, "\n"), pinfo[REGX])))
+      l += pinfo[PAGE];
+    if(l >= pinfo[CURL]) return 0;
+  }
+  return l;
+}
+
+varargs int eval_command(mixed in, mixed pinfo)
+{
+  cprompt = "";
+  if(stringp(in))
+    switch(in)
+    {
+    case "?":
+      write(MSG_HELP+"\n");
+      return 0; 
+    case "b":
+    case "B":
+      pinfo[CURL] -= pinfo[PAGE];
+      break;
+    case "u":
+    case "U":
+      pinfo[CURL] -= pinfo[PAGE] / 2;
+      break;
+    case "f":
+    case "F":
+      pinfo[CURL] += pinfo[PAGE];
+      break;
+    case "d":
+    case "D":
+      pinfo[CURL] += pinfo[PAGE] / 2;
+      break;
+    default:
+    {
+      int l;
+      if(l = to_int(in))
+      {
+        if(l > pinfo[MAXL] || l < 1) return (cprompt = MSG_ILLEGAL, 0);
+        pinfo[CURL] = l;
+        break;
+      }
+      if(sizeof(in) && in[0] == '/')
+      {
+	if(sizeof(in) == 1)
+	{
+	  if(!pinfo[REGX]) return (cprompt = MSG_NO_REGX, 0);
+	}
+	else pinfo[REGX] = in[1..];
+	if(l = search(pinfo)) pinfo[CURL] = l;
+	else return (cprompt = MSG_NOT_FOUND, 0);
+      }
+      else return ::eval_command(in, pinfo);    
+      break;
+    }
+    }
+  else return ::eval_command(in, pinfo);
+  return ::eval_command(-1, pinfo);
+}   
diff --git a/std/newsclient.c b/std/newsclient.c
new file mode 100644
index 0000000..b047e41
--- /dev/null
+++ b/std/newsclient.c
@@ -0,0 +1,244 @@
+// MorgenGrauen MUDlib
+//
+// newsclient.c -- Nachrichtenbretter
+//
+// $Id: newsclient.c 8349 2013-02-04 21:15:28Z Zesstra $
+
+/* 1992 JOF */
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma range_check
+#pragma pedantic
+
+inherit "/std/thing";
+inherit "/std/more";
+
+#include <config.h>
+#include <properties.h>
+#include <news.h>
+#include <wizlevels.h>
+#include <language.h>
+#include <defines.h>
+#include <input_to.h>
+
+string GROUP;
+string writer;
+string usemsg;
+
+void SetInUseMessage(string str)
+{
+  usemsg=str;
+}
+
+string SetGroup(string str)
+{
+  return GROUP=str;
+}
+
+protected void create()
+{
+  thing::create();
+  writer=="";
+
+}
+
+void thinginit() /* If someone doesnt want our add_action he may call this */
+{
+  thing::init();
+}
+
+varargs int remove(int silent)
+{
+  return thing::remove();
+}
+
+void init()
+{
+  thing::init();
+  add_action("schreib","schreib",1);
+  add_action("lies","lies");
+  add_action("loesche","loesch",1);
+}
+
+string ladj(string str, int weite)
+{
+  return((str+"                                            ")[0..weite-1]);
+}
+
+string QueryLong()
+{
+  mixed *messages;
+  string desc;
+  int i;
+
+  desc=QueryProp(P_LONG);
+  desc+="\n";
+  messages=NEWSSERVER->GetNotes(GROUP);
+  if (!pointerp(messages) || !sizeof(messages)) 
+    return desc+"Zur Zeit befinden sich keine Nachrichten am Brett.\n";
+  if (sizeof(messages)==1)
+    desc+="Zur Zeit befindet sich eine Nachricht am Brett:\n\n";
+  else
+    desc+="Zur Zeit befinden sich "+sizeof(messages)+" Nachrichten am Brett:\n\n";
+  for (i=0;i<sizeof(messages);i++)
+  {
+    if (i<9) desc+=" ";
+    desc+=""+(i+1)+". ";
+    desc+=(ladj(messages[i][M_TITLE],45)+" ("+
+	   ladj(messages[i][M_WRITER]+",",13)+
+	   dtime(messages[i][M_TIME])[4..24] + ")\n");
+  }
+  return desc;
+}
+
+varargs string long(int mode)
+{
+  return QueryLong();
+}
+
+mixed *message;
+
+int schreib(string str)
+{
+  int err;
+
+  if (!this_interactive() || !this_interactive()->query_real_name()) return 0;
+  if (writer)
+  {
+    write("Das geht im Moment nicht, da "+capitalize(writer)+" gerade an diesem Brett schreibt.\n");
+    if (usemsg) write(usemsg+"\n");
+    return 1;
+  }
+  _notify_fail("Du musst einen Titel fuer die Notiz angeben !\n");
+  if (!str) return 0;
+  switch (err=NEWSSERVER->AskAllowedWrite(GROUP))
+  {
+    case  1: break;
+	     case -1: write("Du darfst keine Notes an dieses Brett heften !\n"); return 1;
+	     case -3: write("Das Brett ist leider voll !\n"); return 1;
+	     default: write("Interner Fehler "+err+", Erzmagier verstaendigen !\n"); return 1;
+	   }
+  writer=this_interactive()->query_real_name();
+  message=allocate(6);
+  message[M_BOARD]=GROUP;
+  message[M_TITLE]=str;
+  message[M_MESSAGE]="";
+  write("Ok, beende Deine Notiz mit '**' oder brich sie mit '~q' ab !\n");
+  if (IS_WIZARD(this_player()))
+    write("Als Magier"+(this_player()->QueryProp(P_GENDER)==FEMALE ? "in" : "")+
+	  " kannst Du per '~r<filename>' ein File in die Notiz einlesen.\n");
+  input_to("get_note_line", INPUT_PROMPT, ":");
+  return 1;
+}
+
+int get_note_line(string str)
+{
+  int err;
+  if (str=="**" || str=="~.")
+  {
+    writer=0;
+    switch (err=(NEWSSERVER->WriteNote(message)))
+    {
+      case 1:  write("Ok, Nachricht aufgehaengt.\n");
+	       say(message[M_WRITER]+" hat eine Nachricht ans Brett gehaengt.\n");
+	       return 1;
+	       case -1: write("Du darfst leider keine Nachrichten hier aufhaengen.\n");
+	       return 1;
+	       case -3: write("Das Brett ist leider voll !\n");
+	       return 1;
+	       default: write("Interner Fehler "+err+" aufgetreten ! Bitte Erzmagier verstaendigen.\n");
+	       return 1;
+	     }
+  }
+  if (str=="~q")
+  {
+    writer=0;
+    write("Notiz nicht angeheftet.\n");
+    return 1;
+  }
+  if (str[0..1]=="~r" && IS_WIZARD(this_player())) {
+    str=str[2..<1];
+    if (str[0]==' ') str=str[1..<1];
+    if (!str || catch(err=file_size(str);publish) || err<0) {
+      write("File nicht gefunden.\n");
+      input_to("get_note_line", INPUT_PROMPT, ":");
+      return 1;
+    }
+    str=read_file(str);
+    if (!str){
+      write("Zu gross!\n");
+      input_to("get_note_line", INPUT_PROMPT, ":");
+      return 1;
+    }
+    write("Ok.\n");
+  }
+  if (message[M_MESSAGE] && message[M_MESSAGE]!="") message[M_MESSAGE]+="\n";
+  message[M_MESSAGE]+=str;
+  input_to("get_note_line", INPUT_PROMPT, ":");
+  return 1;
+}
+
+int lies(string str)
+{
+  int num;
+  mixed *messages;
+
+  if (!str || str=="" || sscanf(str,"%d",num)!=1 || num<=0)
+  {
+    _notify_fail("WELCHE Nachricht willst Du lesen ?\n");
+    return 0;
+  }
+  num--;
+  messages=(NEWSSERVER->GetNotes(GROUP));
+  if (sizeof(messages)<=num)
+  {
+    _notify_fail("So viele Nachrichten sind da nicht !\n");
+    return 0;
+  }
+  this_player()->More(messages[num][M_TITLE]+" ("+messages[num][M_WRITER]+", "+
+      dtime(messages[num][M_TIME])[5..26] + "):\n" + 
+       messages[num][M_MESSAGE]);
+  return 1;
+}
+
+int loesche(string str)
+{
+  int num;
+
+  if (!str || str=="" || sscanf(str,"%d",num)!=1 || num<=0)
+  {
+    _notify_fail("WELCHE Nachricht willst Du loeschen ?\n");
+    return 0;
+  }
+  num--;
+  switch (NEWSSERVER->RemoveNote(GROUP, num)){
+    case 1: write("Ok.\n");
+	    say(this_player()->name()+" entfernt eine Nachricht vom Brett.\n");
+	    return 1;
+	    case -1: write("Das darfst Du nicht.\n");
+	    say(this_player()->name()+" versucht, eine Nachricht vom Brett zu reissen.\n");
+	    return 1;
+	    case -3: write("So viele Nachrichten sind da nicht !\n");
+	    return 1;
+	    default: write("Interner Fehler. Bitte Erzmagier verstaendigen !\n");
+	    return 1;
+	  }
+  return 0;
+}
+
+void Crumble()
+{
+  if (environment() && living(environment()))
+    return;
+  say(capitalize(name(WER))+" zerfaellt zu Staub, der sich sofort verteilt.\n");
+  write(capitalize(name(WER))+" zerfaellt zu Staub, der sich sofort verteilt.\n");
+  remove();
+}
+
+varargs int move(object target,mixed method)
+{
+  if (objectp(target)&&!living(target)&&environment()&&living(environment()))
+    call_out("Crumble",30+random(30));
+  return ::move(target, method);
+}
diff --git a/std/npc.c b/std/npc.c
new file mode 100644
index 0000000..f612f1e
--- /dev/null
+++ b/std/npc.c
@@ -0,0 +1,175 @@
+// MorgenGrauen MUDlib
+//
+// npc.c -- a generic npc (non player character)
+//
+// Testversion mit support fuer AddItem
+// soll spaeter in npc.c aufgehen
+//
+// $Id: npc.c 9478 2016-02-19 21:18:35Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/thing/properties";
+inherit "/std/hook_provider";
+inherit "/std/living/description";
+inherit "/std/living/light";
+inherit "/std/living/life";
+inherit "/std/living/attributes";
+inherit "/std/npc/moving";
+inherit "/std/living/skill_attributes";
+inherit "/std/living/skills";
+inherit "/std/living/clothing";
+inherit "/std/npc/combat";
+inherit "/std/npc/chat";
+inherit "/std/npc/comm";
+inherit "/std/container/restrictions";
+inherit "/std/thing/commands";
+inherit "/std/thing/language";
+inherit "/std/npc/info";
+inherit "/std/npc/put_and_get";
+inherit "/std/npc/items";
+inherit "/std/thing/envchk";
+inherit "/std/living/helpers";
+
+#include <config.h>
+#include <properties.h>
+#include <ansi.h>
+#include <wizlevels.h>
+#include <living.h>
+#include <language.h>
+#include <attributes.h>
+#include <defines.h>
+#include <health.h>
+#include <npc.h>
+#include <moving.h>
+#include <guard.h>
+
+static int _set_level(int arg);
+
+protected void create()
+{ 
+  seteuid(getuid());
+  properties::create();
+  restrictions::create();
+  commands::create();
+  light::create();
+  description::create();
+  attributes::create();
+  clothing::create();
+  life::create();
+  enable_commands();
+  comm::create();
+  combat::create();
+  info::create();
+  put_and_get::add_put_and_get_commands();
+  add_team_commands();
+  items::create();
+  envchk::create();
+  moving::create();
+
+  add_action("UseSpell","",1);
+
+  SetProp(P_LIGHT_MODIFIER, 1);
+  SetProp(P_WEIGHT_PERCENT, 100);
+  SetProp(P_NAME, "Niemand");
+  SetProp(P_MSGIN, "kommt an");
+  SetProp(P_MSGOUT, "geht");
+  SetProp(P_MMSGIN, "erscheint in einer Rauchwolke");
+  SetProp(P_MMSGOUT, "verschwindet mit Knall und Schwefelduft");
+  SetProp(P_GENDER, NEUTER );
+  SetProp(P_WEIGHT, 75000);
+  SetProp(P_MAX_WEIGHT, 50000);
+  SetProp(P_RACE, "Npc");
+  SetProp(P_MAX_HP,100);
+  SetProp(P_MAX_SP,100);
+  SetProp(P_HP,50);
+  SetProp(P_SP,50);
+  SetProp(P_MAX_ALCOHOL,100);
+  SetProp(P_MAX_DRINK,100);
+  SetProp(P_MAX_FOOD,100);
+  SetProp(P_DRINK, 0);
+  SetProp(P_FOOD, 0);
+  SetProp(P_ALCOHOL, 0);
+  SetProp(P_HANDS, ({ "", 30 }) );
+  SetProp(P_MAX_HANDS, 2);
+  SetProp(P_NPC, 1);
+  SetProp(P_GUARD,100);
+  SetProp(P_NO_TPORT,NO_TPORT_IN);
+
+  set_heart_beat(1);
+  heartbeat=1;
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void reset(){
+  items::reset();
+  combat::reset();
+}
+
+static int _set_level(int arg)
+{
+  Set(P_LEVEL, arg);
+  SetAttr(A_CON, arg);
+  SetAttr(A_DEX, arg);
+  SetAttr(A_INT, arg);
+  SetAttr(A_STR, arg);
+  return(arg); //Rueckgabewert noetig!
+}
+
+varargs void create_default_npc(int level, int maxhp)
+{ 
+  if(level < 1) return;
+  SetProp(P_LEVEL, level);
+  if (maxhp<=0) maxhp = 42 + level*8;
+  SetProp(P_MAX_HP, maxhp);
+  SetProp(P_MAX_SP, maxhp);
+  SetProp(P_HANDS, ({" mit blossen Haenden", level*10 }) );
+  SetProp(P_BODY, (level*20)/3);
+  SetProp(P_XP, level * maxhp * 50);
+} 
+
+protected void heart_beat()
+{
+  if( environment() ) life::heart_beat();
+  if (!this_object()) return; // Evtl. an Gift gestorben...
+  combat::heart_beat();
+  chat::heart_beat();
+}
+
+void give_notify(object ob)
+{
+  put_and_get::give_notify(ob);
+}
+
+// Force the monster to do a command.
+int command_me(string cmd) { return command(cmd); }
+
+void init()
+{
+  combat::init();
+  info::init();
+  commands::init();
+//  description::init();
+}
+
+// items initialisieren?
+protected void NotifyMove(object dest, object oldenv, int method) {
+    
+  ::NotifyMove(dest, oldenv, method);
+  // gestorben?
+  if (!objectp(this_object())) return;
+
+  if ( Query(NPC_NEEDS_ITEM_INIT, F_VALUE))
+      _clone_items();
+}
+
+string _query_race()
+{
+   if (stringp(Query(P_RACE))) return capitalize(Query(P_RACE));
+}
diff --git a/std/npc/chat.c b/std/npc/chat.c
new file mode 100644
index 0000000..7f4f89b
--- /dev/null
+++ b/std/npc/chat.c
@@ -0,0 +1,66 @@
+// MorgenGrauen MUDlib
+//
+// npc/chat.c -- Labernde NPCs
+//
+// $Id: chat.c 6801 2008-03-21 23:34:46Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <properties.h>
+#define NEED_PROTOTYPES
+#include <living/combat.h>
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+#define ME this_object()
+
+/*
+ * Some simple chat variables
+ */
+
+/*
+ * heart_beat is called so the monster may chat.
+ */
+void SetChats(int chance, mixed strs) {
+  if (!pointerp(strs))
+    return;
+  SetProp(P_CHAT_CHANCE,chance);
+  SetProp(P_CHATS,strs);
+}
+
+void SetAttackChats(int chance, mixed strs) {
+  if (!pointerp(strs))
+    return;
+  SetProp(P_ACHAT_CHANCE,chance);
+  SetProp(P_ACHATS,strs);
+}
+
+void DoAttackChat() {
+  string* c;
+  if (!ME || !environment(ME))
+    return;
+  if (QueryProp(P_DISABLE_ATTACK)>0)return ;
+  if (random(100) < QueryProp(P_ACHAT_CHANCE))
+    if ((c = QueryProp(P_ACHATS)) && sizeof(c)) 
+      tell_room(environment(ME),
+		process_string(c[random(sizeof(c))]));
+}
+
+void DoChat() {
+  string *c;
+  if (!ME || !environment(ME))
+    return;
+  if (random(100) < QueryProp(P_CHAT_CHANCE))
+    if ((c = QueryProp(P_CHATS)) && sizeof(c)) 
+      tell_room(environment(ME),
+		process_string(c[random(sizeof(c))]));
+}
+
+protected void heart_beat()
+{
+  if( InFight() ) DoAttackChat();
+  else            DoChat();
+}
diff --git a/std/npc/combat.c b/std/npc/combat.c
new file mode 100644
index 0000000..747714b
--- /dev/null
+++ b/std/npc/combat.c
@@ -0,0 +1,407 @@
+// MorgenGrauen MUDlib
+//
+// npc/combat.c -- NPC-spezifische Kampffunktionen
+//
+// $Id: combat.c 9488 2016-02-19 21:24:04Z Arathorn $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "std/living/combat";
+
+#include <combat.h>
+#include <language.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+
+#define NEED_PROTOTYPES 1
+#include <living/life.h>
+#undef NEED_PROTOTYPES
+
+#define HB_CHECK 7
+#define ME this_object()
+#define STATMASTER "/p/service/rochus/guildstat/master"
+
+nosave int heartbeat, beatcount;
+
+private void catch_up_hbs();
+
+protected void create() {
+  ::create();
+  beatcount=1;
+  heartbeat=1;
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// aktuelles Lebewesen, fuer das dieser NPC gerade taetig ist. Default:
+// Spieler, bei dem er als Helfer-NPC registriert ist.
+public object QueryUser()
+{
+  mixed helperdata = QueryProp(P_HELPER_NPC);
+  if (pointerp(helperdata) && objectp(helperdata[0]))
+    return helperdata[0];
+  return 0;
+}
+
+// ggf. Feinde expiren. Soll das Problem verringern, dass Spieler nach Tagen
+// erst die Meldung kriegen, dass der NPC sie nicht mehr jagt, wenn der HB
+// reaktivert wird.
+void reset() {
+  // ggf. die abgeschalteten HBs nachholen.
+  if (!heartbeat)
+    catch_up_hbs();
+  // ggf. P_ENEMY_DAMAGE zuruecksetzen
+  ResetEnemyDamage();
+}
+
+static void _set_max_hp(int i) {
+  Set(P_MAX_HP,i);
+  SetProp(P_HP,i);
+}
+
+static void _set_max_sp(int i) {
+  Set(P_MAX_SP,i);
+  SetProp(P_SP,i);
+}
+
+
+// Check-Funktion fuer die P_NO_ATTACK-QueryMethod
+static mixed _check_immortality()
+{
+    int t;
+
+    if ( !(t = Query("time_to_mortality")) || time() > t ){
+        // Zeit ist abgelaufen - wieder angreifbar machen
+        Set( P_NO_ATTACK, 0, F_QUERY_METHOD );
+        heartbeat = 1;
+        beatcount = 1;
+        set_heart_beat(1);
+
+        return 0;
+    }
+
+    // der NPC ist noch unangreifbar
+    return break_string( capitalize(name( WER, 1 )) + " versteckt sich hinter "
+                         "einem Fehler im Raum-Zeit-Gefuege und entgeht so "
+                         "voruebergehend allen Angriffen.", 78 );
+}
+
+
+// wenn der HeartBeat buggt, wird diese Funktion vom Master aufgerufen
+public void make_immortal()
+{
+    // fuer 5 Minuten unangreifbar machen
+    Set( P_NO_ATTACK, #'_check_immortality, F_QUERY_METHOD );
+
+    Set( "time_to_mortality", time() + 300, F_VALUE );
+
+    // damit die Spieler keinen Vorteil durch den Bug haben, heilen
+    heal_self(10000);
+
+    // da nun der Heartbeat abgeschaltet ist und normalerweise erst
+    // reaktiviert wird, sobald jemand nach 5min P_NO_ATTACK abfragt, muss man
+    // aber auf Viecher achten, die immer nen Heartbeat haben wollen. In dem
+    // fall per call_out selber die Prop abfragen.
+    if (QueryProp(P_HB))
+      call_out(#'QueryProp, 301, P_NO_ATTACK);
+}
+
+
+// Damit NPCs gegeneinander weiterkaempfen, auch wenn kein Spieler
+// in der Naehe ist:
+static int _query_hb()
+{
+    // TODO: return InFight() || Query(P_HB, F_VALUE), sobald InFight()
+    // geaendert.
+    return (InFight() || Query(P_HB,F_VALUE)) ? 1 : 0;
+}
+
+
+#define SPELL_TOTALRATE 0
+#define SPELL_DAMAGE 1
+#define SPELL_TEXT_FOR_ENEMY 2
+#define SPELL_TEXT_FOR_OTHERS 3
+#define SPELL_DAMTYPE 4
+#define SPELL_FUNC 5
+#define SPELL_ARG 6
+
+varargs int AddSpell(int rate, int damage, string TextForEnemy,
+                     string TextForOthers, string|string* dam_type, string func,
+                     int|mapping spellarg) {
+  mixed *spells;
+  int total_rates;
+
+  if (rate<0 || damage<=0 || !stringp(TextForEnemy) ||
+      !stringp(TextForOthers))
+     return 0;
+
+  if (stringp(dam_type))
+     dam_type = ({dam_type});
+  else if (!pointerp(dam_type))
+      dam_type = ({DT_MAGIC});
+  
+  // Tatsaechlich ist es immer ein nicht-physischer Angriff, wenn spellarg ein
+  // int ist, weil wenn spellarg==0 ist der Default nicht-physischer Angriff
+  // und bei spellarg!=0 auch. Nur mit einem mapping kann man einen phys.
+  // Angriff erzeugen.
+  if (intp(spellarg))
+    spellarg = ([SP_PHYSICAL_ATTACK: 0]);
+
+  // Falls vorhanden, alte Syntax auf die von replace_personal() anpassen,
+  // die im heart_beat() beim Ausgeben der Meldung verwendet wird.
+  if ( strstr(TextForOthers, "@", 0) != -1 )
+  {
+    // Zeichen nach @WER & Co in runde Klammern einschliessen, damit es als
+    // Sub-Pattern im Ausgabestring wiederholt werden kann. Ansonsten wuerde
+    // es mit ersetzt.
+    TextForOthers = regreplace(TextForOthers, "@WER([^1-9])", "@WER1\\1", 1);
+    TextForOthers = regreplace(TextForOthers, "@WESSEN([^1-9])",
+                               "@WESSEN1\\1", 1);
+    TextForOthers = regreplace(TextForOthers, "@WEM([^1-9])", "@WEM1\\1", 1);
+    TextForOthers = regreplace(TextForOthers, "@WEN([^1-9])", "@WEN1\\1", 1);
+  }
+  total_rates=Query("npc:total_rates")+rate;
+  spells=Query(P_SPELLS);
+  if (!pointerp(spells))
+    spells=({});
+  spells+=({({total_rates, damage, TextForEnemy, TextForOthers,
+              dam_type, func, spellarg})});
+  Set(P_SPELLS,spells);
+  Set("npc:total_rates",total_rates);
+  return 1;
+}
+
+int AutoAttack(object ob) {
+  mixed m;
+
+  if (!query_once_interactive(ob))
+    return 0;
+  if (mappingp(m=QueryProp(P_AGGRESSIVE))) {
+    mixed *ind,x,z;
+    float f;
+    int i,n;
+
+    ind=m_indices(m)-({0});n=0;f=0.0;
+    for (i=sizeof(ind)-1;i>=0;i--) {
+      x=ind[i];
+      if ((z=m[x][ob->QueryProp(x)]) || (z=m[x][0])) {
+  f=f+(float)z;
+  n++;
+      }
+    }
+    if (n)
+      m=f/((float)n);
+    else
+      m=m[0];
+  }
+  if (((int)(100*(m+ob->QueryProp(P_AGGRESSIVE))))<=random(100))
+    return 0;
+  if  (IS_LEARNER(ob)
+       && (ob->QueryProp(P_INVIS)
+     || ob->QueryProp(P_WANTS_TO_LEARN)))
+    return 0;
+  return 1;
+}
+
+void SpellAttack(object enemy) {
+}
+
+#if 0
+TJ(string s) {
+  object o;
+  if (o=find_player("jof"))
+    tell_object(o,sprintf("%O: %s\n",this_object(),s));
+}
+#else
+#define TJ(x)
+#endif
+
+protected void heart_beat() {
+  int r,i;
+  mixed env,*spells, sinfo;
+  object enemy;
+
+  if ( --beatcount < 0 )
+      beatcount = 0;
+  
+  if (!beatcount && !Query(P_HB)) {
+    if (!environment()) {
+      set_heart_beat(0);
+      heartbeat = 0;
+      if( clonep(this_object()) ) remove();
+      return;
+    }
+    if (!QueryProp(P_POISON)) {
+      // Spieler anwesend?
+      env = filter(all_inventory(environment()), #'query_once_interactive);
+      if (!sizeof(env)) {
+	// Nein, HBs abschalten.
+	set_heart_beat(0);
+	heartbeat=0;
+	TJ("OFF\n");
+	beatcount=HB_CHECK;
+	Set("npc:beat_off_num",absolute_hb_count());
+	return;
+      }
+    }
+  }
+  ::heart_beat();
+  if (!ME)
+      return;
+  enemy=SelectEnemy();
+  if (QueryProp(P_AGGRESSIVE)
+      && (!enemy || environment()!=environment(enemy))
+      && !beatcount) {
+    beatcount=HB_CHECK;
+    env=filter(all_inventory(environment()),#'AutoAttack);
+    if (!sizeof(env))
+       return;
+    i=random(sizeof(env));
+    Kill(env[i]);
+  }
+  else if (!beatcount)
+    beatcount=HB_CHECK;
+  if (!objectp(enemy) ||QueryProp(P_DISABLE_ATTACK)>0)
+    return;
+  SpellAttack(enemy);
+
+  if (!pointerp(spells=Query(P_SPELLS))
+      || !sizeof(spells)
+      || !objectp(enemy=SelectEnemy())
+      || environment(enemy)!=environment()
+      || (QueryProp(P_DISABLE_ATTACK)>0)
+      || random(100)>Query(P_SPELLRATE))
+    return;
+  r=random(Query("npc:total_rates"));
+  for (i=sizeof(spells)-1;(i>0 && spells[i-1][SPELL_TOTALRATE]>r);i--)
+    ;
+  string akt_spell_mess = spells[i][SPELL_TEXT_FOR_ENEMY];
+  if ( sizeof(akt_spell_mess) )
+    tell_object(enemy, break_string(akt_spell_mess, 78));
+  akt_spell_mess = spells[i][SPELL_TEXT_FOR_OTHERS];
+  // Nur, wenn ueberhaupt eine Meldung gesetzt wurde, muss diese verarbeitet
+  // werden.
+  if (stringp(akt_spell_mess) && sizeof(akt_spell_mess))
+  {
+    akt_spell_mess = replace_personal(akt_spell_mess, ({enemy}), 1);
+    say(break_string(akt_spell_mess, 78),({enemy, this_object()}));
+  }
+  sinfo = deep_copy(spells[i][SPELL_ARG]);
+  if(!mappingp(sinfo))
+    sinfo=([ SI_MAGIC_TYPE :({ MT_ANGRIFF }) ]);
+  else if(!sinfo[SI_MAGIC_TYPE])
+    sinfo[ SI_MAGIC_TYPE]=({ MT_ANGRIFF });
+  if(!sinfo[SP_PHYSICAL_ATTACK] &&
+     (enemy->SpellDefend(this_object(),sinfo) >
+      random(MAX_ABILITY+QueryProp(P_LEVEL)*50))){
+    tell_object(enemy,"Du wehrst den Spruch ab.\n");
+    say(enemy->Name(WER,1)+" wehrt den Spruch ab.\n",
+        ({ enemy, this_object()}));
+    return ;
+  }
+  int damage = random(spells[i][SPELL_DAMAGE])+1;
+  enemy->Defend(damage, spells[i][SPELL_DAMTYPE],
+                spells[i][SPELL_ARG],
+                this_object());
+
+  // Falls der Gegner (oder wir) im Defend stirbt, hier abbrechen
+  if ( !objectp(ME) || !objectp(enemy) 
+      || enemy->QueryProp(P_GHOST) ) return;
+  
+  if (spells[i][SPELL_FUNC] && stringp(spells[i][SPELL_FUNC]))
+    catch(call_other(this_object(),
+         spells[i][SPELL_FUNC],
+         enemy,
+         damage,
+         spells[i][SPELL_DAMTYPE]);publish);
+}
+
+// Heartbeats nachholen.
+private void catch_up_hbs() {
+  // gibt es HBs zum nachholen?
+  int beat_off_num = Query("npc:beat_off_num");
+  if (!beat_off_num)
+    return; // nein.
+  // wieviele HBs nachholen?
+  beat_off_num = absolute_hb_count() - beat_off_num;
+  
+  if (beat_off_num>0) {
+    // Nicht ausgefuehrtes HEILEN nachholen
+    int rlock=QueryProp(P_NO_REGENERATION);
+    int hp=QueryProp(P_HP);
+    int sp=QueryProp(P_SP);
+    int alc=QueryProp(P_ALCOHOL);
+    if (!(rlock & NO_REG_HP)) {
+      hp+=beat_off_num/HEAL_DELAY+alc/ALCOHOL_DELAY;
+      SetProp(P_HP,hp);
+    }
+    if (!(rlock & NO_REG_SP)) {
+      sp+=beat_off_num/HEAL_DELAY+alc/ALCOHOL_DELAY;
+      SetProp(P_SP,sp);
+    }
+    alc-=beat_off_num/ALCOHOL_DELAY;
+    if ( alc < 0 )
+      alc = 0;
+    SetProp(P_ALCOHOL,alc);
+    int da = QueryProp(P_DISABLE_ATTACK);
+    // Paralysen abbauen
+    if ( da > 0 ) {
+      da -= beat_off_num;
+      if ( da < 0 )
+	da = 0;
+      SetProp( P_DISABLE_ATTACK, da );
+    }
+    // Hunttimes aktualisieren, Feinde expiren
+    update_hunt_times(beat_off_num);
+    if (!heartbeat)
+      // HBs immer noch abgeschaltet, naechstes Mal HBs seit jetzt nachholen.
+      Set("npc:beat_off_num",absolute_hb_count());
+    else
+      // HB laeuft wieder, nix mehr nachholen, bis zur naechsten Abschaltung.
+      Set("npc:beat_off_num",0);
+  }
+}
+
+void init() {
+
+  // ggf. Heartbeats nachholen und wieder einschalten.
+  if (!heartbeat) {
+    set_heart_beat(1);
+    heartbeat=1;
+    catch_up_hbs();
+  }
+
+  if (AutoAttack(this_player()))
+    Kill(this_player());
+}
+
+private nosave closure mod_att_stat;
+
+int Defend(int dam, mixed dam_type, mixed spell, object enemy) {
+  if (objectp(enemy=(enemy||this_player()))
+      && query_once_interactive(enemy)
+      && !IS_LEARNER(enemy)) {
+    if (!objectp(get_type_info(mod_att_stat,2))) {
+      object ma;
+      if (!objectp(ma=find_object(STATMASTER)))
+        return ::Defend(dam,dam_type,spell,enemy);
+      // Keine Statistik wenn Master nicht geladen ist.
+      mod_att_stat=symbol_function("ModifyAttackStat",ma);
+    }
+    funcall(mod_att_stat,
+            enemy->QueryProp(P_GUILD),
+            enemy->QueryProp(P_GUILD_LEVEL),
+            dam,
+            dam_type,
+            spell);
+  }
+
+  return ::Defend(dam,dam_type,spell,enemy);
+}
diff --git a/std/npc/comm.c b/std/npc/comm.c
new file mode 100644
index 0000000..892d8fa
--- /dev/null
+++ b/std/npc/comm.c
@@ -0,0 +1,57 @@
+// MorgenGrauen MUDlib
+//
+// npc/comm.c -- Basiskommunikation fuer NPCs
+//
+// $Id: comm.c 9358 2015-10-22 18:35:04Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/comm";
+
+#include <language.h>
+#include <living/comm.h>
+#define NEED_PROTOTYPES
+#include <thing/description.h>
+
+
+void create() {
+  add_action( "sage", "sag", 1 );
+  add_action( "echo", "echo" );
+  add_action( "emote", "emote" );
+}
+
+int echo( string str ) {
+  say( str + "\n" );
+  return 1;
+}
+
+int sage( string str ) {
+  say( break_string(str, 78, capitalize(name(WER,2))+" sagt: "));
+  return 1;
+}
+
+int emote( string str ) {
+  say( capitalize(name(WER,2))+" "+str+"\n" );
+  return 1;
+}
+
+// zum ueberschreiben - DEPRECATED! USE ReceiveMsg()!
+public void catch_msg(mixed *arr, object obj) {}
+public void catch_tell(string str) {}
+
+// by default, the msg is delivered to catch_tell() for compatibility reasons
+// and otherwise ignored.
+public varargs int ReceiveMsg(string msg, int msg_typ, string msg_action,
+                              string msg_prefix, object origin)
+{
+  // compatibility...
+  if (msg_typ & MSG_DONT_WRAP)
+    catch_tell(sprintf("%s%s", msg_prefix||"", msg));
+  else
+    catch_tell(sprintf("%s%s\n", msg_prefix||"", msg));
+  return MSG_DELIVERED;
+}
+
diff --git a/std/npc/info.c b/std/npc/info.c
new file mode 100644
index 0000000..d66ee26
--- /dev/null
+++ b/std/npc/info.c
@@ -0,0 +1,345 @@
+// MorgenGrauen MUDlib
+//
+// npc/info.c -- Behandeln von Fragen an den NPC
+//
+// $Id: info.c 9522 2016-03-01 19:20:10Z Arathorn $
+
+/* Letzte Aenderungen von Wim 8.1.99
+ *
+ * AddInfo( schluessel, antwort [, indent [, [silent [, casebased] ] ] )
+ *  Wenn ein Spieler dieses Monster nach "schluessel" fragt, so gib die
+ *  Programmierte Antwort aus.
+ *  Erweiterung von Wim: ist silent gesetzt, so erfolgt eine "persoenliche"
+ *    Antwort. d.h. umstehende Personen bekommen bei silent==1 keinen, bei
+ *    stringp(silent), den String ausgegeben, dabei kann er auch die Schluessel-
+ *    Worte @WER @WESSEN @WEM @WEN enthalten.
+ *  - Ergaenzt um @CAP_WER... fuer zwangs-capitalisierte Namen an Satzanfaengen.
+ *    ist bei fragenden NPCs und PCs mit Tarnkappe wichtig! (Silvana)
+ *  - Auch in der Antwort des fragenden wird nun ersetzt (Vanion)
+ *  Enthaelt casedbased einen Funktionsnamen oder verweist auf eine closure, so
+ *    wird die Beantwortung der Frage von dem return-Wert abhaengig gemacht.
+ *    Bei 0 wird die Frage normal beantwortet, bei 1 erfolgt die Ausgabe des
+ *    unter DEFAULT_NOINFO gespeicherten Textes.
+ *    Wird ein String zurueckgegeben, so wird er unter Beachtung von ident an
+ *    Stelle der urspruenglichen Information ausgegeben.
+ *
+ * RemoveInfo( schluessel )
+ *  Das Monster antwortet nicht mehr auf diesen Schluessel.
+ *
+ * SetProp( P_DEFAULT_INFO, antwort [, indent ] )
+ *  Setze die Antwort, die das Monster auf unverstaendliche Fragen geben
+ *  soll. (Diese Funktion ist obsolet! Benutze stattdessen
+ *  AddInfo( "\ndefault info", antwort [, indent ] );
+ *
+ * GetInfo( [schluessel] )
+ *  Wenn Schluessel gesetzt ist, so wird die dazugehoerige Info,
+ *  ansonsten werden alle Infos zurueckgegeben.
+ *
+ * Die Antworten sollten wie emote - kommandos aussehen.
+ * Der optionale Indent wird zum Umbrechen von langen Infos benutzt.
+ * (Typischerweise sollte indent="sagt: " sein.)
+ *
+ * In den Infos darf mit process_string gearbeitet werden. Das Ergebnis von
+ * process_string wird dann mit umgebrochen!
+ *
+ *---------------------------------------------------------------------------
+ */
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/description.h>
+#include <thing/properties.h>
+#include <npc.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+#include <config.h>
+#include <exploration.h>
+
+// TODO: langfristig waer hier private schoen.
+nosave mapping infos;
+
+protected void create()
+{
+    // Initialisierung nur wenn noetig, damit beim virtuellen Erben von
+    // /std/npc in npc1 und npc2 dann in npc3 beim npc1::create();
+    // npc2:create(); im zweiten create() die Infos nicht
+    // ueberschrieben/geloescht werden.
+    if (!mappingp(infos)) {
+        infos = ([
+            DEFAULT_INFO:"schaut Dich fragend an.\n";0;
+            "schaut @WEN fragend an.\n";0,
+            DEFAULT_NOINFO:"moechte Dir nicht antworten.\n";0;
+            "verweigert @WEM die Antwort.\n";1
+            ]);
+    }
+}
+
+
+void init() {
+  add_action( "frage", "frag", 1 );
+}
+
+
+static void smart_npc_log(string str)
+{
+  string creat, creat_det;
+
+  if (!stringp(creat=QueryProp(P_LOG_INFO))) {
+    creat = MASTER->creator_file(this_object());
+    if (creat == ROOTID)
+      creat = "ROOT";
+    else if( creat==BACKBONEID )
+      creat="STD";
+    creat_det="report/"+explode(creat, ".")[<1]+"_INFO.rep";
+    creat="report/"+explode(creat, ".")[<1]+".rep";
+  }
+  log_file(creat,
+           sprintf("INFO von %s [%s] (%s):\n%s\n",
+                   getuid(this_interactive()),
+                   explode(object_name(this_object()),"#")[0],
+                   strftime("%d. %b %Y"),
+                   str));
+  if (stringp(creat_det) && sizeof(creat_det))
+    log_file(creat_det,
+             sprintf("INFO von %s [%s] (%s):\n%s\n",
+                     getuid(this_interactive()),
+                     explode(object_name(this_object()),"#")[0],
+                     strftime("%d. %b %Y"),
+                     str));
+}
+
+public int frage(string str) {
+  string myname, text;
+
+  str=(extern_call()?this_player()->_unparsed_args():str);
+  if( !str || sscanf( str, "%s nach %s", myname, text ) != 2 ) {
+    _notify_fail( "WEN willst Du nach WAS fragen?\n" );
+    return 0;
+  }
+
+  if( !id( lower_case(myname) )
+    || QueryProp(P_INVIS) ) {
+    _notify_fail( "So jemanden findest Du hier nicht.\n" );
+    return 0;
+  }
+  say( capitalize(this_player()->name(WER))+" fragt " +
+    name(WEN,2)+" nach "+capitalize(text)+".\n",
+      this_player() );
+
+  text = lower_case(text);
+  GiveEP(EP_INFO, text);
+
+  return do_frage( text );
+}
+
+static string infoDefaultReplace(string pstring, object pl)
+{
+  pstring=" "+pstring;
+  if (strstr(pstring,"@WER",0) >-1 )
+    pstring= regreplace(pstring,"@WER",pl->name(WER,1),1);
+  if (strstr(pstring,"@WESSEN",0) >-1 )
+    pstring= regreplace(pstring,"@WESSEN",pl->name(WESSEN,1),1);
+  if (strstr(pstring,"@WEM",0) >-1 )
+    pstring= regreplace(pstring,"@WEM",pl->name(WEM,1),1);
+  if (strstr(pstring,"@WEN",0) >-1 )
+    pstring= regreplace(pstring,"@WEN",pl->name(WEN,1),1);
+  if (strstr(pstring,"@CAP_WER",0) >-1 )
+    pstring= regreplace(pstring,"@CAP_WER",pl->Name(WER,1),1);
+  if (strstr(pstring,"@CAP_WESSEN",0) >-1 )
+    pstring= regreplace(pstring,"@CAP_WESSEN",pl->Name(WESSEN,1),1);
+  if (strstr(pstring,"@CAP_WEM",0) >-1 )
+    pstring= regreplace(pstring,"@CAP_WEM",pl->Name(WEM,1),1);
+  if (strstr(pstring,"@CAP_WEN",0) >-1 )
+    pstring= regreplace(pstring,"@CAP_WEN",pl->Name(WEN,1),1);
+
+  return pstring[1..];
+}
+
+static mixed *GetInfoArr(string str)
+{  
+   return ({ infos[str, 0], infos[str, 1], infos[str,2], infos[str, 3] });
+}
+
+public int do_frage(string text)
+{
+  string indent,answer;
+  mixed silent, preinfo, noanswer;
+  mixed *info;
+
+  if (stringp(preinfo = QueryProp(P_PRE_INFO)))
+  {
+     // Die message action wird auf "frage" fixiert, damit dies immer das
+     // gleiche ist, egal, mit welchem Verb man in diese Funktion kommt.
+     this_player()->ReceiveMsg(preinfo, MT_LISTEN, "frage",
+                               Name(WER,2)+" ",this_object());
+     send_room(environment(this_object()),
+               "ist nicht gewillt, "+this_player()->Name(WEM,2)
+               +" zu antworten.",
+               MT_LISTEN, "frage",
+               Name(WER,2)+" ",
+               ({this_player()}) );
+     return 1;
+  }
+  else
+  {
+    if (intp(preinfo) && preinfo > 0)
+      return 1;
+  }
+
+  info=GetInfoArr(text);
+  if (!info[0])
+  {
+    if( this_interactive() && QueryProp(P_LOG_INFO) )
+      smart_npc_log(text);
+    text = DEFAULT_INFO;
+    info=GetInfoArr(text);
+  }
+
+  if (closurep(info[0]) ) {
+    answer=funcall(info[0]);
+    if( !answer || answer=="") return 1;
+  } else {
+    answer=process_string(info[0]);
+  }
+
+  if (closurep(info[3]) )
+   {
+    noanswer=funcall(info[3]);
+    if ( intp(noanswer) && noanswer > 0)
+     {
+       text = DEFAULT_NOINFO;
+       info = GetInfoArr(text);
+       if (closurep(info[0]) ) {
+         answer=funcall(info[0]);
+         if( !answer || answer=="") return 1;
+       } else {
+         answer=process_string(info[0]);
+       }
+     }
+    else if ( stringp(noanswer) )
+      answer = noanswer;
+   }
+
+  silent=info[2];
+
+  // Replacements gehen auch in der Antwort des NPC. Das gibt den Antworten 
+  // eine persoenliche Note, und so teuer is das auch nicht :)
+  answer = infoDefaultReplace(answer, this_player());
+
+  if( indent=info[1] )
+  {
+    if (stringp(silent) || (intp(silent) && silent > 0) )
+    {  // Persoenliche Antwort mit indent
+       this_player()->ReceiveMsg(answer, MT_LISTEN|MSG_BS_LEAVE_LFS,
+                                 "frage",
+                                 Name(WER,2)+" "+indent,
+                                 this_object());
+       if (stringp(silent))
+       {
+          silent=infoDefaultReplace(silent, this_player());
+          send_room(environment(), silent, MT_LISTEN, "frage",
+                    Name(WER,2)+" ", ({this_player()}));
+       }
+    }
+    else // "normale Antwort" mit Indent
+    {
+      send_room(environment(), answer, MT_LISTEN|MSG_BS_LEAVE_LFS,
+                "frage",
+                Name(WER,2)+" "+indent, ({this_object()}));
+    }
+  }
+  else
+  {
+    if (stringp(silent) || (intp(silent) && silent > 0) )
+    {  // Persoenliche Antwort ohne indent
+       this_player()->ReceiveMsg(answer, MT_LISTEN|MSG_DONT_WRAP,
+                                 "frage",
+                                 Name(WER,2)+" ",
+                                 this_object());
+       if (stringp(silent))
+       {
+          silent=infoDefaultReplace(silent, this_player());
+          send_room(environment(), silent, MT_LISTEN, "frage",
+                    Name(WER,2)+" ", ({this_player()}) );
+       }
+    }
+    else // "normale Antwort" ohne Indent
+      send_room(environment(), answer, MT_LISTEN|MSG_DONT_WRAP,
+                "frage", Name(WER,2)+" ");
+  }
+  return 1;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * Setzen von Infos
+ *---------------------------------------------------------------------------
+ */
+
+public varargs void AddInfo(mixed key, mixed info, string indent, 
+                            mixed silent, mixed casebased ) {
+
+  if (stringp(casebased))
+    casebased=symbol_function(casebased,this_object());
+
+  if( pointerp( key ) ) {
+    int i;
+    for ( i=sizeof( key )-1; i>=0; i-- )
+      infos += ([ key[i]: info; indent; silent; casebased ]);
+  }
+  else
+    infos += ([ key: info; indent; silent; casebased ]);
+}
+
+public varargs void AddSpecialInfo(mixed keys, string functionname, 
+                      string indent, mixed silent, mixed casebased )
+{
+  int i;
+  closure cl;
+
+  if(!(cl=symbol_function(functionname,this_object()))) return;
+  return AddInfo(keys,cl,indent,silent,casebased);
+}
+
+
+public void RemoveInfo( string key )
+{
+  m_delete(infos,key);
+}
+
+static varargs void _set_default_info( mixed info )
+{
+  if (pointerp(info))
+    apply(#'AddInfo/*'*/,DEFAULT_INFO,info);
+  else
+    AddInfo(DEFAULT_INFO,info);
+}
+
+public varargs mixed GetInfo(string str)
+{
+  if (!str) return deep_copy(infos);
+  return infos[str];
+}
+
+
+static mapping _query_npc_info()
+{
+    return deep_copy(infos);
+}
+
+
+static mapping _set_npc_info( mapping map_ldfied )
+{
+    if ( !mappingp(map_ldfied) )
+        return 0;
+
+    return infos = map_ldfied;
+}
+
diff --git a/std/npc/items.c b/std/npc/items.c
new file mode 100644
index 0000000..f0260cf
--- /dev/null
+++ b/std/npc/items.c
@@ -0,0 +1,240 @@
+// MorgenGrauen MUDlib
+//
+// /std/npc/items.c -- Item-Verwaltung fuer NPCs
+//
+// $Id: items.c 8146 2012-10-30 18:18:14Z Zesstra $
+//
+// (c) by Padreic (Padreic@mg.mud.de)
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <living/combat.h>
+#undef NEED_PROTOTYPES
+#include <npc.h>
+#include <properties.h>
+#include <combat.h>
+#include <config.h>
+#include <moving.h>
+#include <sys_debug.h>
+
+protected void create()
+{
+  // dieses flag ist nur bis zum ersten move gesetzt. Nimmt danach also auch
+   Set(NPC_NEEDS_ITEM_INIT, 1);  // keinen Speicher mehr in Anspruch.
+   // Set(P_ITEMS, ({})); // keine initialisierung spart Speicher  
+}
+
+protected void create_super() {
+      set_next_reset(-1);
+}
+
+protected object* maybe_own_stuff() {
+  object* obs = all_inventory(environment());
+  object haufen = present("\nhaufen "+name(WEM),environment());
+  if( objectp(haufen) ) obs += all_inventory(haufen);
+  object leiche = present("\nleiche "+name(WEM),environment());
+  if( objectp(leiche) ) obs += all_inventory(leiche);
+  return obs;
+}
+
+// clont alle Objekte die geclont werden muessen...
+protected void _clone_items()
+{
+  int i, j, mode, refresh;
+  object ob, *inv1;
+  mixed  props, *items;
+  string file, *inv2;
+
+  if (!pointerp(items=QueryProp(P_ITEMS))) return;
+  if (environment()) { // Liste mit filenamen (inv2) erstellen.
+    inv1 = maybe_own_stuff();
+    inv2 = map(inv1, #'load_name);
+  }
+  else inv2=({}); // inv1 wird gar nicht mehr benoetigt
+
+  for (i=sizeof(items)-1; i>=0; i--) {
+    ob = items[i][RITEM_OBJECT];
+    file = items[i][RITEM_FILE];
+    mode = items[i][RITEM_REFRESH];
+    refresh = mode & 0x0000ffff;
+    props = items[i][RITEM_PROPS];
+    if (props && intp(props)) { // unique Gegenstand?
+      ob=find_object(file);
+      if (ob) {
+        // kann ob aufgenommen werden? Wenn das Objekt in inv1 drin ist, ist es
+        // im Env oder in einer Leiche oder einem Haufen.
+        if (member(inv1,ob) > -1) {
+          if (environment()) {
+            if (environment(ob) == environment())
+              tell_room(environment(),
+                  capitalize(name(WER))+" hebt "+ob->name(WEN)+" auf.\n");
+            else
+              tell_room(environment(),break_string(
+                  capitalize(name(WER))+" nimmt "+ob->name(WEN)+ " aus "
+                  +environment(ob)->name(WEM) + ".",78));
+          }
+          ob->remove();  // zerstoeren und neuladen.
+          if (ob) destruct(ob);
+          if (catch(ob=load_object(file);publish))             
+              raise_error(sprintf(
+                "_clone_items(): %O does not exist or is not loadable\n", file)); 
+          ob->move(this_object(), M_NOCHECK);
+          items[i][RITEM_OBJECT]=ob;
+        }
+        else switch( refresh ) {
+          case REFRESH_DESTRUCT:
+            if (environment(ob)) {
+              ob=0; // nicht zuecken, tragen, initialisieren...
+              break;
+            }
+          case REFRESH_ALWAYS:
+          case REFRESH_REMOVE:                
+            ob->remove();
+            if (ob) destruct(ob);
+            if (catch(ob=load_object(file);publish)) 
+                raise_error(sprintf("_clone_items(): "
+                      "%O does not exist or is not loadable\n", file));
+            ob->move(this_object(), M_NOCHECK);
+            items[i][RITEM_OBJECT]=ob;
+            break;
+          case REFRESH_NONE:
+          default: ob=0; // nicht zuecken, tragen, initialisieren...
+        }
+      }
+      else {
+        if (catch(ob=load_object(file);publish)) raise_error(sprintf(
+          "_clone_items(): %O does not exist or is not loadable\n", file));
+        if (objectp(ob)) ob->move(this_object(), M_NOCHECK);
+        items[i][RITEM_OBJECT]=ob;
+      }
+    } // if (props ...) // also nicht unique
+    else {
+      switch( refresh ) {
+        case REFRESH_DESTRUCT: // erfuellt auch REFRESH_REMOVE Bedingung...
+          if (ob) {
+            ob=0; // nicht zuecken, tragen, initialisieren...
+            break;
+          }
+        case REFRESH_REMOVE:
+          if (ob) {
+            if(present(ob, this_object())) {
+              ob=0; // nicht zuecken, tragen, initialisieren...
+              break;
+            }
+            else {
+              if ((member(inv2, file))==-1) {  
+                ob=0;
+              }
+            }
+          }
+        case REFRESH_NONE: // wird entfernt nach dem ersten clonen!!!
+        case REFRESH_ALWAYS:
+          // schauen ob der Gegenstand im environment liegt.
+          if ((j=member(inv2, file))!=-1) {
+            ob=inv1[j]; // inv1 kann leer sein, aber dann ist inv2 auch leer
+            inv1[j]=0; // wichtig falls mehrere gleiche AddItems
+            inv2[j]=0; // = 0 setzen um Array kopieren zu vermeiden
+            if (environment()) {
+              if (environment() == environment(ob))
+                tell_room(environment(),
+                    capitalize(name(WER))+" hebt "+ob->name(WEN)+" auf.\n");
+              else
+                tell_room(environment(),break_string(
+                    capitalize(name(WER))+" nimmt "+ob->name(WEN)+" aus "
+                    +environment(ob)->name(WEM) + ".",78));
+            }
+          }
+          if (ob) {
+            ob->remove();
+            if (ob) destruct(ob);
+          }
+          if (catch(ob=clone_object(file);publish)) raise_error(sprintf(
+            "_clone_items(): %O does not exist or is not loadable\n", file));
+          ob->move(this_object(), M_NOCHECK);
+          switch( refresh ) {
+          case REFRESH_NONE:
+            items[i] = 0; // Speicher freimachen
+            break;
+          case REFRESH_ALWAYS:
+            items[i][RITEM_OBJECT] = 0; // Objekt "vergessen"
+            break;
+          default:
+            items[i][RITEM_OBJECT] = ob;
+          }
+          break;
+        default: ob=0; // nicht zuecken, tragen, initialisieren...
+      }
+    } // if (props ...) else 
+    if (ob) {
+      if (mappingp(props)) walk_mapping(props, symbol_function("SetProp", ob));
+      // Eigentlich will man hier sowas wie command("zuecke schwert")
+      // machen, aber das handling der id's kann nicht sicherstellen,
+      // dass der NPC die richtige Waffe erwischt, deshalb machen wir
+      // das tragen hier von Hand.
+      if (mode & CLONE_WEAR) {
+        if( mode & CLONE_NO_CHECK ) {
+          object *armours;
+          ob->DoUnwear(); // evtl. dem Vorgaenger abnehmen.
+          UseHands(ob, ob->QueryProp(P_NR_HANDS));
+          armours=QueryProp(P_ARMOURS)+({ ob });
+          SetProp(P_ARMOURS, armours);
+          SetProp(P_TOTAL_AC, QueryProp(P_TOTAL_AC)+ob->QueryProp(P_AC));
+          ob->SetProp(P_WORN, this_object());
+        } else {
+          command( "trage \n"+object_name(ob) );
+        }
+      }
+      if (mode & CLONE_WIELD) {
+        if( mode & CLONE_NO_CHECK ) {
+          ob->DoUnwield(); // evtl. dem Vorgaenger abnehmen. 
+          UseHands(ob, ob->QueryProp(P_NR_HANDS));
+          SetProp(P_WEAPON, ob);
+          SetProp(P_TOTAL_WC, ob->QueryProp(P_WC));
+          ob->SetProp(P_WIELDED, this_object());
+        } else {
+          command( "zuecke \n"+object_name(ob) );
+        }
+      }
+    } // if (ob) 
+  } // for i
+  items-=({ 0 }); // REFRESH_NONEs von nicht unique Objekten 
+  if (!sizeof(items)) items=0; // Speicher sparen...
+  SetProp(P_ITEMS, items);
+  Set(NPC_NEEDS_ITEM_INIT, 0);
+}
+
+public varargs object AddItem(mixed filename, int refresh, mixed props)
+{
+  // Aus Array ein Element auswaehlen
+  if (pointerp(filename)) 
+    filename = filename[random(sizeof(filename))];
+ 
+  // Kein String? -> Fehler
+  if (!stringp(filename)){
+    raise_error("AddItem: filename or array of filenames expected.\n"); 
+  } else {
+    // Pfad normieren und eventuell vorhandenes ".c" entfernen
+    filename = MASTER->_get_path(
+                  filename[<2..]==".c"?filename=filename[0..<3]
+                                        : filename,"?"); 
+    // Property setzen
+    SetProp(P_ITEMS, (QueryProp(P_ITEMS)||({}))+
+                     ({ ({ 0, filename, refresh, props }) }));
+
+  if (environment()) _clone_items();
+  }
+  return 0;
+}
+
+void reset()
+// fuer REFRESH_ Objekte...
+{
+  _clone_items();
+}
+
diff --git a/std/npc/moving.c b/std/npc/moving.c
new file mode 100644
index 0000000..f06e0da
--- /dev/null
+++ b/std/npc/moving.c
@@ -0,0 +1,39 @@
+// MorgenGrauen MUDlib
+//
+// living/moving.c -- moving of living objects
+//
+// $Id$
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/moving";
+
+#include <hook.h>
+#define NEED_PROTOTYPES
+#include <moving.h>
+
+//<int|<string|closure>* >* GuardExit(object room, int hookid,
+//                                    <string|closure>* hdata)
+//{
+//}
+
+protected int _reg_exit_hook(object dest, object oldenv)
+{
+  closure cl = symbol_function("GuardExit", this_object());
+  if (cl)
+  {
+    if (oldenv)
+      oldenv->HUnregisterFromHook(H_HOOK_EXIT_USE, cl);
+    return dest->HRegisterModifier(H_HOOK_EXIT_USE, cl);
+  }
+  return 0;
+}
+// Krams nach dem Move machen und nebenbei zum Ueberschreiben.
+protected void NotifyMove(object dest, object oldenv, int method)
+{
+  _reg_exit_hook(dest, oldenv);
+  return ::NotifyMove(dest, oldenv, method);
+}
diff --git a/std/npc/put_and_get.c b/std/npc/put_and_get.c
new file mode 100644
index 0000000..8b72cf4
--- /dev/null
+++ b/std/npc/put_and_get.c
@@ -0,0 +1,36 @@
+// MorgenGrauen MUDlib
+//
+// npc/put_and_get.c.c -- Geben und nehmen fuer NPCs
+//
+// $Id: put_and_get.c 6371 2007-07-17 22:46:50Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "std/living/put_and_get";
+#include <moving.h>
+#include <properties.h>
+
+void give_notify( object obj )
+{
+   mixed* msg;
+   if (pointerp(msg=QueryProp(P_REJECT))) {
+      switch(msg[0]) {
+         case REJECT_GIVE:
+            say(msg[1]);
+            give_obj( obj, this_player() );
+            break;
+         case REJECT_LIGHT_MODIFIER:
+            if (obj->QueryProp(P_LIGHT_MODIFIER) ||
+                obj->QueryProp(P_LIGHT)) break;
+         case REJECT_DROP:
+            say(msg[1]);
+            drop_obj( obj );
+            break;
+         case REJECT_KEEP:
+         default: say(msg[1]); /* keep it */
+    }
+  }
+}
diff --git a/std/npc/sequencer.c b/std/npc/sequencer.c
new file mode 100644
index 0000000..50c85e3
--- /dev/null
+++ b/std/npc/sequencer.c
@@ -0,0 +1,125 @@
+// MorgenGrauen MUDlib
+//
+// npc/sequencer.c -- Scripte und Trigger fuer NPCs :)
+//
+// $Id: sequencer.c 7526 2010-04-02 00:01:26Z Arathorn $
+
+// seq2.c
+// Version 2.0 des Sequencers von Don Rumata 28.06.93
+// 
+// Sinn:
+//   Bereitstellung von Funktionen, ddamit ein npc mehrere Befehle
+//   bei Eintritt eines bestimmten Ereignisses nacheinander
+//   automatisch durchfuehrt.
+//
+// Ereignisse:
+//   TellEvent: Es wird etwas in dem Raum, in dem der npc sich
+//              befindet, gesagt.
+//   GiveEvent: Es wird dem npc etwas gegeben.
+//
+// Programm:
+//   Ein Programm ist eine Liste von Befehlen.
+//   Jeder Befehl ist eine Liste, bestehend aus einem Kommando
+//   und einer Zahl.
+//   Das Kommendo wird aehnlich der Befehle, die ein Spieler ein-
+//   gibt ausgefuehrt.
+//   Vorsicht: NPCs koennen nur einen Teil der Befehle, die ein
+//   Spieler kann, dafuer aber immer 'echo' und 'emote'.
+//   Die Zahl gibt die Anzahl der Sekunden an, in der der naechste
+//   Befehl ausgefuehrt wird.
+//
+// Funktionen:
+//   RegisterTell( funktion, programm )
+//     Wenn dem npc etwas gesagt wird, so wird die gesagte Meldung
+//     an die Funktion uebergeben. Gibt die Funktionen nicht 0
+//     zurueck, wird das Programm gestartet.
+//   RegisterGive( funktion, programm )
+//     Wird dem npc etwas gegeben, so wird das Objekt an die
+//     Funktion uebergeben. Gibt die Funktion nicht 0 zurueck, so
+//     wird das Programm gestartet.
+//   Load( programm )
+//     Starte das angegebene Programm.
+//
+//   give_notify() gibt eine 1 zurueck, wenn das Objekt akzeptiert
+//     wurde. (Es muss - falls gewuenscht - dann von Hand zuruech-
+//     gegeben werden. (give_obj(ob,this_player())) in der Funk.)
+//   mittels add_action() kann man im create() ds NPCs eigene
+//     Verben fuer den NPC einfuehren.
+//
+// Es kann immer nur eine Funktion (egal ob Tell oder Give) angemeldet
+// sein. Es kann immer nur ein Programm gleichzeitig laufen.
+//
+// Ideen und Bugreports an Rumata
+
+string givefun, tellfun;
+mixed *program;
+int isRunning;
+
+// zur sicherheit nicht von aussen aufrufbar !!!!
+static string RegisterGive( string fun, mixed prog )
+{
+  if( isRunning ) return 0;
+  program = prog;
+  tellfun = 0;
+  return givefun = fun;
+}
+
+// zur sicherheit nicht von aussen aufrufbar !!!!
+static string RegisterTell( string fun, mixed prog )
+{
+  if( isRunning ) return 0;
+  program = prog;
+  givefun = 0;
+  return tellfun = fun;
+}
+
+// zur sicherheit nicht von aussen aufrufbar !!!!
+static void Start()
+{
+  if( isRunning || !objectp(this_object())) return;
+  isRunning = 1;
+  call_out( "nextStep", 0, 0 );
+}
+
+static void Load( mixed prog )
+{
+  if( isRunning ) return;
+  program = prog;
+  Start();
+}
+
+public void catch_tell( string str )
+{
+  if( isRunning || previous_object()==this_object()) return;
+  if( stringp(tellfun) && call_other( this_object(), tellfun, str ) )
+    Start();
+}
+
+int give_notify( object ob )
+{
+  if( isRunning ) return 0;
+  if( stringp(givefun) && call_other( this_object(), givefun, ob ) )
+  {
+    Start();
+    return 1;
+  }
+  return 0;
+}
+
+static void nextStep( int line )
+{
+  if( !isRunning ) return;
+  if( line >= sizeof( program ) )
+  {
+    isRunning = 0;
+    return;
+  }
+  command( process_string( program[line][0] ) );
+  if( !isRunning || !objectp(this_object()) ) return;
+  call_out( "nextStep", program[line][1], line+1 );
+}
+
+mixed QueryProg()
+{
+  return program[0..];
+} 
diff --git a/std/pile.c b/std/pile.c
new file mode 100644
index 0000000..dcfe5ef
--- /dev/null
+++ b/std/pile.c
@@ -0,0 +1,124 @@
+// Ein transparenter Container, in dem die Gegenstaende landen,
+// die ein Spieler bei seinem Tod verliert.
+// Sinn dieses Objektes ist es, das invenotry eines Raumes klein
+// zu halten und die kosten im init() eines Spielers zu senken.
+
+#pragma strict_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+
+inherit "std/container";
+
+// Spielerhaufen sollen sich etwas anders als NPC-haufen verhalten.
+private nosave status spielerhaufen;
+
+#include <properties.h>
+#include <moving.h>
+
+void create() {
+	string von;
+	if (object_name(this_object()) == __FILE__[0..<3]) {
+	  set_next_reset(-1);
+	  return;
+	}   
+	if( !clonep(this_object()) ) return;
+	::create();
+	von = (string)previous_object()->QueryProp(P_PILE_NAME);
+	SetProp( P_PILE_NAME, "\nhaufen "+von );
+	spielerhaufen = (int)previous_object()->IsPlayerCorpse();
+
+	AddId( ({"haufen","krempel","kluengel","gegenstaende"}) );
+	AddId( QueryProp(P_PILE_NAME) );
+	SetProp( P_NAME, "Haufen Krempel" );
+	SetProp( P_GENDER, MALE );
+	SetProp( P_INFO, "Diese Gegenstaende gehoerten einmal " + von + ".\n" );
+	SetProp( P_LONG, break_string("Du betrachtest einen Haufen, der aus einer von Leichenresten zusammengebackenen Menge diverser Gegenstaende besteht. Zum Glueck ist von der Leiche nicht mehr genug uebrig, um Ekelgefuehle zu wecken. Ganz im Gegenteil: Dir juckt es ein wenig in den Fingern, Dich an diesem Haufen zu bedienen. Aber man kann sich nie ganz sicher sein, ob nicht doch ein Fluch auf den Dingen lastet. Vielleicht hat der verschiedene Besitzer sie auch jemandem vererbt? Andererseits, wenn gerade niemand guckt ...\n" ) );
+	AddDetail( ({"fetzen","leiche","leichenteile"}), "Mit blossen Augen kann man nicht mehr viel erkennen, aber einige Fetzen deuten darauf hin, dass die Sachen von "+von+" stammen.\n" );
+	AddDetail( ({"ekel","ekelgefuehle"}), "Es ist nicht mehr genug da, um welche zu wecken.\n" );
+	AddDetail( "menge", "Viele kleine Fetzen, die an den Gegenstaenden kleben.\n" );
+	AddDetail( "fluch", "Dem Anschein nach lastet auf den Sachen kein Fluch, aber wer weiss ...?\n" );
+	call_out( "merge_pile", 1 );
+}
+
+mixed _query_short() {
+	int sz = sizeof(all_inventory());
+	if( sz==0 ) {
+		call_out("remove",0,1); // fallback, wenn inhalt auf unerwartete weise verschwindet
+		return 0;
+	} else if( sz<10 ) {
+		return "Ein kleiner Haufen Krempel";
+	} else if( sz<30 ) {
+		return "Ein Haufen Krempel";
+	} else {
+		return "Ein grosser Haufen Krempel";
+	}
+  return 0;
+}
+
+// Prueft, ob wir in einen Spieler gemovt werden sollen.
+// Liefert passendes Moveresult, ansonsten 0.
+protected int PreventMove(object dest, object oldenv, int method) {
+    if( !objectp(dest) ) return 0;
+    if( method & M_NOCHECK ) return 0;
+    if( query_once_interactive(dest) ) {
+      return ME_NOT_ALLOWED;
+    // default meldung in put_and_get:pick
+    // IDEE: im spieler per hook eine andere meldung unterbringen
+    }
+    return ::PreventMove(dest, oldenv, method);
+}
+
+// Sorgt dafuer, dass im NPC sich der Haufen selbst zerstoert.
+protected void NotifyMove(object dest, object oldenv, int method) {
+    if( query_once_interactive(dest)) {
+	log_file( "PILE.log", dtime(time()) + ": pile in " 
+	    + dest->query_real_name() + " gelandet.\n" );
+    }
+    if( living(dest) ) {
+	filter_objects( all_inventory(), "move", dest, M_SILENT | M_NOCHECK );
+	// NPCs erwarten nicht, dass dinge im move verschwinden, deshalb
+	// abwarten
+	call_out( "remove", 0, 1 );
+    }
+    return ::NotifyMove(dest, oldenv, method);
+}
+
+// wenn nur noch ein Objekt im Haufen ist, soll dieser zerstoert werden.
+public void NotifyLeave( object ob, object dest ) {
+	if( sizeof(all_inventory())>1 ) return;
+	call_out( "remove", 0, 1 ); // verzoegern, um prepare_destruct gnaedig zu stimmen
+}
+
+// dito fuers loeschen
+public void NotifyRemove( object ob ) {
+    NotifyLeave( ob, 0);
+}
+
+// Haufen, die von zweimal dem selben NPC kommen, werden
+// zusammengelegt
+void merge_pile() {
+	object* other = filter_objects(
+		all_inventory(environment()) - ({ this_object() }),
+		"id",
+		QueryProp( P_PILE_NAME )
+	);
+	if( !sizeof(other) ) return;
+	filter_objects( all_inventory(), "move", other[0], M_SILENT | M_NOCHECK );
+	remove();
+}
+
+void reset() {
+  // wenn es ein NPC-Haufen ist, werden Gegenstaende, die oefter als 3mal
+  // vorkommen, zerstoert. Schraenkt die Objektflut an Stellen ein, wo NPC
+  // ihren Kram nicht mit AddItem clonen.
+  if (!spielerhaufen)
+    remove_multiple(3);
+  ::reset();
+  // wenn nur noch unsichtbare items im Haufen: aufloesen
+  if (!sizeof(filter(all_inventory(), function int (object o)
+          { return !o->short();} )))
+    remove(1);
+
+}
+
diff --git a/std/player/base.c b/std/player/base.c
new file mode 100644
index 0000000..9c9a6e1
--- /dev/null
+++ b/std/player/base.c
@@ -0,0 +1,4355 @@
+// MorgenGrauen MUDlib
+//
+// player/base.c -- the basic player object
+//
+// $Id: base.c 9467 2016-02-19 19:48:24Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <sys_debug.h>
+#include <regexp.h>
+#include <input_to.h>
+#include <logging.h>
+#include <werliste.h>
+#include <time.h>
+#include <errord.h>
+#include <wizlevels.h>
+#include <money.h>
+
+inherit "/std/hook_provider";
+inherit "/std/player/restrictions";
+inherit "/std/living/attributes";
+inherit "/std/living/put_and_get";
+inherit "/std/living/clothing";
+inherit "/std/thing/properties";
+inherit "/std/player/util";
+inherit "/std/thing/language";
+inherit "/std/player/travel";
+inherit "/std/player/combat";
+inherit "/std/player/description";
+inherit "/std/player/moving";
+inherit "/std/player/life";
+inherit "/std/player/comm";
+inherit "/std/player/viewcmd";
+inherit "/std/player/moneyhandler";
+inherit "/std/player/command";
+inherit "/std/living/skill_attributes";
+inherit "/std/living/light";
+inherit "/std/player/skills";
+inherit "/std/player/quests";
+inherit "/std/player/potion";
+inherit "/std/player/soul";
+inherit "/std/more";
+inherit "/std/user_filter";
+inherit "/secure/telnetneg";
+inherit "/std/player/guide";
+inherit "/std/player/reputation";
+inherit "/std/player/protocols/gmcp";
+inherit "/std/living/helpers";
+
+#define NEED_PROTOTYPES
+#include <player/skills.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+#include <player.h>
+#include <properties.h>
+#include <udp.h>
+#include <config.h>
+#include <ansi.h>
+#include <wizlevels.h>
+#include <living.h>
+#include <attributes.h>
+#include <language.h>
+#include <moving.h>
+#include <defines.h>
+#include <terminal.h>
+#include <new_skills.h>
+#include <pager.h>
+#include <combat.h>
+#include "/secure/questmaster.h"
+#include "/secure/lepmaster.h"
+#include <events.h>
+
+#undef NAME /* DEFINED BY UDP.H; BAD NAME CLASH :( */
+#define NAME(who) capitalize(getuid(who))
+
+mapping autoload;           /* autoload-mapping */
+int hc_play;
+
+private nosave mapping autoload_rest;
+private nosave string  *autoload_error;
+private nosave string realip; 
+
+private nosave string passw;        /* temporarily for password change */
+private nosave string passwold;     /* temporarily for password change */
+
+// HB-Zaehler. Wenn 0 erreicht wird, wird  ein Telnet TM Paket als Keep-Alive
+// an den Client gesendet und der Counter wieder auf hochgesetzt. 
+// Wenn == 0, ist das Keep-Alive abgeschaltet.
+private int telnet_tm_counter;
+
+nosave string default_home; /* Where to move us if we dont have a home set */
+
+nosave int ndead_lasttime;
+nosave mixed ndead_location;
+nosave string ndead_l_filename;
+nosave int ndead_currently;
+nosave int ndead_next_check;
+nosave object *hb_obs;
+
+private nosave string default_pray_room;
+
+nosave mixed env_ndead_info;
+
+static int _set_invis(int a);
+static string _set_tty(string str);
+static mixed _set_fraternitasdonoarchmagorum(mixed arg);
+
+
+static string al_to_title(int a);
+
+static void ndead_revive();
+
+static int wegmeldung(string player);
+
+int quit();
+void save_me(mixed value_items);
+varargs int remove(mixed arg);
+mixed RaceDefault(string arg);
+
+/** Setzt Defaultwerte vor dem Laden des Savefiles.
+    Nur die Werte bleiben spaeter uebrig, die NICHT aus dem Savefile geladen
+    werden und diese hier ersetzen.
+    Ausserdem kann man hier nicht mit den Werten aus den Savefiles arbeiten.
+    Hierzu muss man updates_after_restore() verwenden.
+*/
+protected void create()
+{
+  if(QueryProp(P_LEVEL)) 
+  {
+    return; // darf nur EINMAL gemacht werden
+  }
+  call_out("checkConsistency", 0);
+
+  ndead_next_check=NETDEAD_CHECK_TIME;
+  ndead_lasttime=0;
+  ndead_location=0;
+  ndead_l_filename=0;
+  ndead_currently=0;
+  ndead_next_check=0;
+  hc_play=0;
+  
+  command::create();
+  properties::create();
+  description::create();
+  light::create();
+  attributes::create();
+  clothing::create();
+  combat::create();
+  life::create();
+  comm::create();
+  viewcmd::create();
+  quests::create();
+  restrictions::create();
+  moving::create();
+  travel::create();
+  skills::create();
+   
+  SetProp(P_LEVEL, -1);
+  Set(P_LEVEL, SAVE|SECURED, F_MODE_AS);
+  Set(P_GHOST, SAVE, F_MODE_AS);
+  SetProp(P_SCREENSIZE, -1);
+  Set(P_SCREENSIZE, SAVE,F_MODE_AS);
+  Set(P_MORE_FLAGS, SAVE, F_MODE_AS);
+  SetProp(P_WEIGHT_PERCENT,100);
+  SetProp(P_LONG, 0);
+  SetProp(P_TITLE, "der hoffnungsvolle Anfaenger");
+  SetProp(P_ALIGN, 0);
+  SetProp(P_GENDER, NEUTER);
+  Set(P_GENDER, SAVE, F_MODE_AS);
+  SetProp(P_TTY, "vt100");
+  Set(P_TTY, SAVE, F_MODE_AS);
+  SetProp(P_WEIGHT, 75000);
+  SetProp(P_MAX_HP,50);
+  SetProp(P_MAX_SP,50);
+  SetProp(P_MAX_FOOD,100);
+  SetProp(P_MAX_DRINK,100);
+  SetProp(P_MAX_ALCOHOL,100);
+  Set( P_WIMPY, 20, F_VALUE);
+
+  SetProp(P_HANDS, ({" mit blossen Haenden", 30}));
+  Set(P_HANDS, SAVE, F_MODE_AS);
+  SetProp(P_MAX_HANDS, 2);
+
+  Set(P_MARRIED, SAVE, F_MODE_AS);
+  Set(P_EXTRA_LOOK, SAVE, F_MODE_AS);
+  Set(P_SHOW_EXITS, SAVE, F_MODE_AS);
+  Set(P_SHOW_EXITS, 1);
+  Set(P_WANTS_TO_LEARN, SAVE, F_MODE_AS);
+  Set(P_CAN_FLAGS, SAVE, F_MODE_AS);
+  Set(P_TESTPLAYER, SAVE|PROTECTED, F_MODE_AS);
+  Set(P_ALLOWED_SHADOW, SAVE|SECURED, F_MODE_AS);
+  Set(P_SECOND, SAVE, F_MODE_AS);
+  Set(P_INVIS, SAVE, F_MODE_AS);
+  Set(P_READ_NEWS, SAVE, F_MODE_AS);
+  Set(P_START_HOME, SAVE, F_MODE_AS);
+  Set(P_PRAY_ROOM, SAVE, F_MODE_AS);
+  Set(P_MAILADDR, SAVE, F_MODE_AS);
+  Set(P_HOMEPAGE, SAVE, F_MODE_AS);
+  Set(P_ICQ, SAVE, F_MODE_AS);
+  Set(P_MESSENGER, SAVE, F_MODE_AS);
+  Set(P_LOCATION, SAVE, F_MODE_AS);
+
+  Set(P_NO_ASCII_ART, SAVE, F_MODE_AS);
+
+  Set(P_VISUALBELL, SAVE, F_MODE_AS);
+  Set(P_CARRIED_VALUE, SAVE, F_MODE_AS);
+
+  Set(P_PROMPT, "> ");
+  Set(P_PROMPT, SAVE, F_MODE_AS);
+  Set(P_CALLED_FROM_IP, SAVE, F_MODE_AS);
+  Set(P_INFORMME,SAVE|PROTECTED,F_MODE_AS);
+  Set(P_WAITFOR,SAVE|PROTECTED,F_MODE_AS);
+  Set(P_WAITFOR_REASON,SAVE|PROTECTED,F_MODE_AS);
+  Set(P_DAILY_PLAYTIME,SAVE|PROTECTED,F_MODE_AS);
+  Set(P_NETDEAD_ENV, PROTECTED|NOSETMETHOD, F_MODE_AS);
+  
+  autoload = ([]);
+  autoload_rest = ([]);
+  autoload_error = ({});
+  SetProp(P_ARTICLE,0);
+  Set(P_GUILD,SAVE,F_MODE_AS);
+  Set(P_GUILD_TITLE,SAVE,F_MODE_AS);
+  Set(P_GUILD_LEVEL,SAVE,F_MODE_AS);
+  Set(P_GUILD_RATING,SAVE,F_MODE_AS);
+  Set(P_NEWSKILLS,SAVE,F_MODE_AS);
+  Set(P_NEEDED_QP,REQ_QP);
+  Set(P_DEADS,0);
+  Set(P_DEADS, NOSETMETHOD,F_SET_METHOD);
+  Set(P_DEADS,SAVE|PROTECTED|SECURED,F_MODE_AS);
+  Set(P_LAST_LOGIN,-1);
+  Set(P_LAST_LOGIN, NOSETMETHOD,F_SET_METHOD);
+  Set(P_LAST_LOGIN,SAVE|PROTECTED|SECURED,F_MODE_AS);
+  Set(P_LAST_LOGOUT,-1);
+  Set(P_LAST_LOGOUT, NOSETMETHOD,F_SET_METHOD);
+  Set(P_LAST_LOGOUT,SAVE|PROTECTED|SECURED,F_MODE_AS);
+  Set(P_CLOCKMSG,SAVE,F_MODE_AS);
+  Set(P_TIMEZONE,SAVE,F_MODE_AS);
+  Set(P_SHOWEMAIL,SAVE,F_MODE_AS);
+  Set(P_LAST_QUIT,SAVE|PROTECTED|SECURED,F_MODE_AS);
+
+  Set(P_CMSG, 0, F_MODE); // to clean out the old clone messages
+  Set(P_DMSG, 0, F_MODE);
+  Set(P_CLONE_MSG, SAVE, F_MODE);
+  SetProp(P_CLONE_MSG, "zaubert etwas hervor");
+  Set(P_DESTRUCT_MSG, SAVE, F_MODE);
+  SetProp(P_DESTRUCT_MSG, "verschwindet einfach");
+
+  Set(P_FAO, SAVE|SECURED, F_MODE_AS);
+  Set(P_FAO_PORTALS, SAVE, F_MODE_AS);
+
+  SetProp(P_NEWBIE_GUIDE,0);
+  Set(P_NEWBIE_GUIDE,SAVE,F_MODE_AS);
+
+  //TODO: Remove - Property ist not needed any more.
+  Set(P_TELNET_KEEP_ALIVE, PROTECTED|SAVE, F_MODE_AD);
+  SetProp(P_TELNET_KEEP_ALIVE, 0);
+
+  AddId("Interactive");
+
+  realip="";
+}
+
+// ACHTUNG: Falls hier mal sonst noch weitere Resets geerbt werden, muss das
+// hier ordentlich definiert werden!
+void reset() { 
+  comm::reset();
+  // momentan kann der Reset jetzt abgeschaltet werden, da er nur die
+  // TM-History loescht und ggf. von Netdead() und Disconnect() reaktiviert
+  // wird.
+  set_next_reset(-1);
+}
+
+protected void NotifyMove(object dest, object oldenv, int method)
+{
+  moving::NotifyMove(dest,oldenv,method);
+  // ggf. Daten ueber neues Env per GMCP senden.
+  if (dest != oldenv)
+    GMCP_Room();
+}
+
+string Forschung()
+{
+  return LEPMASTER->QueryForschung();
+}
+
+/** Setzt Defaultwerte fuer Rassen - wird von den Shells ueberschrieben.
+*/
+mixed RaceDefault(string arg)
+{
+  if (!arg)
+    return 0;
+  switch(arg)
+  {
+    case P_HANDS :
+      return ({" mit blossen Haenden",30,DT_BLUDGEON});
+    case P_BODY :
+      return 0;
+  }
+  return 0;
+}
+
+/** Prueft Spielerobjekt auf Konsistenz.
+  Ueberprueft das Spielerobjekt nach kompletter Initialisierung (im 
+  callout) auf korrekte Werte einiger Props.
+*/
+void checkConsistency()
+{ mixed h;
+  int   m;
+
+  if (pointerp(h=RaceDefault(P_HANDS)) && sizeof(h)>1)
+    m=(int)h[1];
+  else
+    m=30;
+  if((h=Query(P_HANDS))[1] > m && !IS_LEARNER(this_object())) {
+    log_file("inconsistent", sprintf(
+      "[%s] %O: HANDS: %d\n", dtime(time()), this_player(), h[1]));
+    h[1] = m;
+    Set(P_HANDS,h);
+  }
+
+  if (Query(P_BODY)!=(m=RaceDefault(P_BODY)))
+     Set(P_BODY,m);
+
+  if (!Query(P_SECOND_MARK,F_MODE))
+    Set(P_SECOND_MARK,SAVE|PROTECTED,F_MODE_AS);
+  if (!Query(P_TTY_SHOW,F_MODE)&SAVE)
+  {
+    Set(P_TTY_SHOW,0,F_VALUE);
+    Set(P_TTY_SHOW,SAVE,F_MODE_AS);
+  }
+  if (Query(P_TTY_COLS,F_MODE)&SAVE)
+  {
+    Set(P_TTY_COLS,SAVE,F_MODE_AD);
+    Set(P_TTY_ROWS,SAVE,F_MODE_AD);
+    Set(P_TTY_TYPE,SAVE,F_MODE_AD);
+  }
+}
+
+/** Bittet das Spielerobjekt, sich zu zerstoeren.
+  \param[in] silent Flag, ob ohne Textausgaben zerstoert werden soll
+  \return Erfolg der Selbstzerstoerung
+*/
+varargs int remove(int silent)
+{
+  return moving::remove(silent);
+}
+
+/** Schaltet in allen Objekten im Inv HBs aus.
+  Schaltet im Inv in allen Objekten (rekursiv, deep_inventory)
+  die Heartbeats aus. Falls obs uebergeben wird, werden diese Objekte
+  statt des Inventars benutzt.
+  Speichert die Objekte, die einen HB hatten, in der glob. Var. hb_obs.
+  @param[in] obs mixed - Objekte, in denen der HB abgeschaltet werden soll. 
+    Container werden rekursiv behandelt.
+  @attention Achtung! Niemals zweimal hintereinander rufen, ohne 
+    zwischendurch restart_heart_beats() gerufen zu haben!
+  @sa restart_heart_beats
+*/
+varargs static void stop_heart_beats(mixed obs)
+{
+  int i;
+
+  if (!obs)
+  {
+    hb_obs=({});
+    obs=deep_inventory(ME);
+  } 
+  foreach(mixed ob: obs) {
+    if (pointerp(ob))
+        stop_heart_beats(ob);
+    else if (set_object_heart_beat(ob,0))  
+        hb_obs+=({ob});
+  }
+}
+
+/** Schaltet HBs in Objekten im Inv wieder ein.
+  Schaltet in allen Objekten in hb_obs den Heartbeat ein.
+    In hb_obs (glob. Var.) stehen alle Objekte, die beim letzten Aufruf von
+    stop_heart_beats() einen HB hatten.
+  @sa stop_heart_beats
+*/
+static void restart_heart_beats()
+{
+  int i;
+
+  if (pointerp(hb_obs))
+  {
+    foreach(object ob: hb_obs)
+      set_object_heart_beat(ob,1);
+    hb_obs=0;
+  }
+}
+
+/** Prueft auf abgelaufene Spielzeit.
+  Prueft in Spielerobjekten, ob die taegliche Maximalspielzeit
+    abgelaufen ist.
+    Wird im heart_beat() gerufen.
+  @return int - Flag, ob Spielzeit abgelaufen und Logout erfolgen soll
+*/
+static int CheckDailyPlaytime() {
+  int *spieldauer,d;
+
+  if (!pointerp(spieldauer=Query(P_DAILY_PLAYTIME)))
+    return 0;
+  // 0:Minuten pro Tag, 1:Nr. des letzen Tages, 2:Nr. des angefangenen Tages,
+  // 3:letzte Zeitpruefung, 4:verbleibende Zeit
+  d=time()/86400;
+  if (spieldauer[1]<=d) { // Ende der zeitbeschraenkten Tage?
+    Set(P_DAILY_PLAYTIME,0);
+    return 0;
+  } else if (spieldauer[2]!=d) { // Neuer Tag?
+    spieldauer[4]=spieldauer[0];
+    spieldauer[2]=d;
+  } else {
+    spieldauer[4]-=(time()-spieldauer[3]);
+  }
+  spieldauer[3]=time();  // Letzte Zeitpruefung
+  Set(P_DAILY_PLAYTIME,spieldauer);
+  if (spieldauer[4]<0) { // Keine Zeit mehr uebrig fuer heute
+    if (!interactive(ME))
+      return 1;
+    write("Du hast lange genug gemuddet fuer heute.\n");
+    say(Name(WER)+" hat fuer heute genug gemuddet.\n");
+    remove_interactive(ME);
+    return 1;
+  }
+  return 0;
+}
+
+/** Gibt Erwartemeldung mit Grund aus.
+  @param[in] who   string - Wer ist hereingekommen?
+  @param[in] invis int - Ist der Spieler Invis?
+*/
+static void Show_WaitFor_Reason(string who, int invis)
+{
+  mixed list;
+  string reason,name;
+
+  if (invis) name="("+who+")";
+    else name=who;
+  if ((mappingp(list=QueryProp(P_WAITFOR_REASON))) && (reason=list[who]))
+    tell_object(ME,sprintf("\nDu erwartest %s wegen:\n%s\n",name,reason));
+  else
+    tell_object(ME,sprintf("Du erwartest %s aus keinem bestimmten Grund.\n",
+      name));
+}
+
+/** Gibt Liste der Erwarteten Spieler an this_player() aus.
+*/
+static void ListAwaited() //Anwesende Erwartete auflisten
+{
+    string *list;
+    mixed mlist;
+    object ob;
+    int mag;
+
+    mag=IS_LEARNER(ME);
+
+    list=({});
+    foreach(string erwartet : QueryProp(P_WAITFOR)) {
+        if (objectp(ob=find_player(lower_case(erwartet)))) {
+            if (ob->QueryProp(P_INVIS)) {
+                if (mag) list+=({ sprintf("(%s)",erwartet) });
+            }
+            else list+=({erwartet});
+        }
+    }
+    if (sizeof(list))
+        printf("Anwesende Erwartete: %s.\n",
+            CountUp(sort_array(list,#'>)));
+
+    if ((mappingp(mlist=QueryProp(P_WAITFOR_REASON))) && (sizeof(mlist)))
+        {
+          foreach(string erwartet : mlist) {
+                if (!(ob=find_player(lower_case(erwartet))) ||
+                    (!mag && ob->QueryProp(P_INVIS)));
+                else Show_WaitFor_Reason(erwartet,ob->QueryProp(P_INVIS));
+          }
+        }
+}
+/** Teilt den Gilden und anderen Spielern mit, wer reingekommen ist.
+  Ausserdem wird ggf. PlayerQuit() im Environment gerufen.
+  \param[in] rein int - wahr, wenn der Spieler einloggt.
+*/
+protected void call_notify_player_change(int rein)
+{
+  string wer = getuid(ME);
+  // erst die Gilde informieren
+  string gilde = QueryProp(P_GUILD);
+  if (stringp(gilde) && (find_object("/gilden/"+gilde)
+        || file_size("/gilden/"+gilde+".c")>0))
+    catch(("/gilden/"+gilde)->notify_player_change(ME, rein); publish);
+
+  // dann die anderen Spieler
+  int mag = IS_LEARNER(ME);
+  int invis = QueryProp(P_INVIS);
+  object *u = users() - ({ME}); // sich selber nicht melden 
+  if (mag && invis) {   // Invismagier nur Magiern melden
+    u = filter(u, function int (object o)
+        { return query_wiz_level(o) >= LEARNER_LVL; }
+        );
+  }
+  u->notify_player_change(capitalize(wer),rein,invis);
+
+  // und beim Ausloggen noch das Env informieren.
+  if (!rein) {
+    if(environment()) catch(environment()->PlayerQuit(ME);publish);
+  }
+}
+
+/** Ruft im uebergebenen Objekt ein init() auf, sofern notwendig.
+  Ruft in ob ein init() auf, falls das Objekt nach dem
+    letzten Ausloggen geschaffen wurde.
+  \param[in] ob object - Objekt, in dem init() gerufen wird.
+  \param[in] logout int - Letzter Logout
+  \return 1, falls init() gerufen wurde. 0 sonst.
+*/
+static int call_init( object ob, int logout )
+{
+    if ( objectp(ob) && object_time(ob) > logout )
+        return catch(ob->init(); publish), 1;
+    return(0);
+}
+
+/** Holt seit dem letzten Ausloggen ausgefallene Inits nach.
+  Ruft in den uebergebenen Objekten call_init(), was einen init()
+  ausloest, falls das Objekt seit dem letzten Ausloggen erstellt wurde.
+  \param[in] logout Zeitpunkt des letzten Logouts
+  \param[in] obs Array von Objekten
+  \sa call_init()
+*/
+static void inits_nachholen( int logout, object *obs )
+{
+    filter( obs, "call_init", ME, logout );
+}
+
+/** Belebt einen Netztoten wieder.
+  Wird im Login gerufen, wenn der Spieler netztot war. Aequivalent zu
+  start_player()
+  @param[in] silent Wenn Flag gesetzt, werden keine Meldung an den Raum
+  ausgegeben.
+  @param[in] ip Textuelle Repraesentation der IP-Adresse, von der der Spieler
+  kommt.
+  @see start_player()
+*/
+varargs void Reconnect( int silent )
+{
+    int num;
+    string called_from_ip;
+    object *inv;
+
+    if ( query_once_interactive(ME) )
+    {
+        // perform the telnet negotiations. (all that are available)
+        "*"::startup_telnet_negs();
+        Set( P_LAST_LOGIN, time() );
+    }
+
+    enable_commands();
+    set_living_name( getuid() );
+    _remove_netdead();
+    set_heart_beat(1);
+    // Hunttimes aktualisieren und ggf. Feinde vergessen.
+    update_hunt_times((time()-QueryProp(P_LAST_LOGOUT)) /__HEART_BEAT_INTERVAL__);
+    // Heartbeats in Objekten im Inv reaktiveren.
+    restart_heart_beats();
+    // life.c will ggf. was aufraeumen
+    life::reconnect();
+
+    log_file( "REENTER", sprintf( "%-11s %s, %-15s (%s).\n",
+                                  capitalize(getuid(ME)), ctime(time())[4..15],
+                                  query_ip_number(ME)||"Unknown",
+                                  query_ip_name(ME)||"Unknown" ),
+              200000 );
+
+    if ( ndead_currently )
+        ndead_revive();
+
+    if ( !silent && interactive(ME) )
+        call_notify_player_change(1);
+
+    command::reconnect();
+    if ( query_once_interactive(ME) )
+        modify_prompt();
+
+    // Login-event ausloesen
+    EVENTD->TriggerEvent(EVT_LIB_LOGIN, ([
+          E_OBJECT: ME,
+          E_PLNAME: getuid(ME),
+          E_ENVIRONMENT: environment() ]) );
+
+    catch( num = "secure/mailer"->FingerMail(geteuid());publish );
+
+    if ( num )
+        write( "Du hast " + num + " neue" + (num == 1 ? "n Brief" : " Briefe")
+               + " im Postamt liegen.\n" );
+
+    if ( QueryProp(P_AWAY) )
+        write( break_string( "Du bist als abwesend gekennzeichnet: " +
+                             QueryProp(P_AWAY) + ".", 78 ) );
+
+    catch( RegisterChannels(); publish );
+
+    if ( (called_from_ip = Query(P_CALLED_FROM_IP)) &&
+         query_ip_number(ME) != called_from_ip ) {
+        string tmp;
+
+        if ( stringp(tmp = query_ip_name(called_from_ip)) &&
+             tmp != called_from_ip )
+            tmp = " [" + tmp + "]";
+        else
+            tmp = "";
+
+        write( "Das letzte Mal kamst Du von " + called_from_ip + tmp + ".\n" );
+    }
+
+    Set( P_CALLED_FROM_IP, query_ip_number(ME) );
+
+    // falls Gegenstaende mit 'upd -ar' upgedated wurden, muessen die
+    // "verloren gegangenen" init()'s nachgeholt werden
+    if ( query_once_interactive(ME) && !IS_LEARNER(ME) )
+        call_out( "inits_nachholen", 0, Query(P_LAST_LOGOUT),
+                  all_inventory(ME) );
+
+    // noch nicht geclonte Autoloader "nach"clonen
+    while ( remove_call_out("load_auto_objects") != -1 )
+        /* do nothing */;
+
+    if ( sizeof(autoload_rest) )
+        call_out( "load_auto_objects", 0, autoload_rest );
+
+    if (ndead_location) {
+      catch( ndead_location->BecomesNetAlive(ME);publish );
+      inv = all_inventory(ndead_location);
+      ndead_location = 0;
+    }
+    else
+      inv = ({});
+
+    inv += deep_inventory(ME);
+
+    //ZZ foreach statt call_other(), damit nen bug in BNA nicht die anderen
+    //BNA verhindert.
+    foreach(object ob: inv) {
+        //es ist nicht auszuschliessen, dass Items durch BecomesNetAlive()
+        //eines anderen zerstoert werden.
+        if (objectp(ob))
+          catch( call_other(ob, "BecomesNetAlive", ME);publish );
+    }
+
+    // Erst an dieser Stelle, weil der Spieler u.U. durch ein BecomesNetAlive()
+    // noch bewegt wurde.
+    if ( !silent && environment() && object_name(environment()) != NETDEAD_ROOM )
+    {
+        if(query_hc_play()<=1)
+          tell_room(environment(),QueryProp(P_NAME) + " weilt wieder unter den Lebenden.\n",({ME}) );
+        else 
+          tell_room(environment(),QueryProp(P_NAME) + " weilt wieder unter den Verstorbenen.\n",({ME}) );
+    }
+
+    NewbieIntroMsg();
+
+    if ( query_once_interactive(ME) )
+        ListAwaited();
+}
+
+/** Loggt einen Spieler aus und macht ihn netztot.
+  Bewegt einen Spieler in den Netztotenraum, deaktiviert Heartbeats im
+  Inventar, ruft BecomesNetDead(), loest Erwartemeldungen aus, triggert
+  Ausloggevent.
+*/
+void NetDead()
+{
+  object *inv;
+  int num;
+
+  catch(RemoveChannels();publish);
+
+  if(query_hc_play()>1)
+    say("Ploetzlich weicht alle spirituelle Energie aus "+QueryProp(P_NAME)+".\n");
+  else
+    say("Ploetzlich weicht alles Leben aus "+QueryProp(P_NAME)+".\n");
+
+  _set_netdead();
+  remove_call_out("quit");
+  remove_living_name();
+  // Wird zwar im save_me() gemacht, aber die Zeitunterschiede beim
+  // fingern direkt nach dem Ausloggen fuehren immer wieder zu Verwirrungen
+  if(query_once_interactive(ME) && !QueryProp(P_INVIS))
+      Set(P_LAST_LOGOUT,time());
+  if (ME)
+    ndead_location = environment();
+
+  if (query_once_interactive(ME))
+    call_notify_player_change(0);
+
+  // Logout-event ausloesen
+  EVENTD->TriggerEvent(EVT_LIB_LOGOUT, ([
+          E_OBJECT: ME,
+          E_PLNAME: getuid(ME),
+          E_ENVIRONMENT: environment() ]) );
+
+  set_next_reset(900);
+  /* Bei Nicht-Magier-Shells wird comm::reset() aufgerufen, das prueft, ob
+     der Spieler immer noch netztot ist, und falls ja, die tmhist loescht.
+     Die Methode wird von /std/shells/magier.c ueberschrieben, netztote
+     Magier (die eigentlich schon anderweitig beseitigt worden sein sollten)
+     werden remove()d und destruct()ed. --Amynthor 05.05.2008 */
+
+  if (environment()) {
+    catch(environment()->BecomesNetDead(ME);publish);
+    inv = deep_inventory(ME)+all_inventory(environment());
+  }
+  else inv=deep_inventory(ME);
+  foreach(object ob: inv) {
+      if (objectp(ob)) //man weiss nie was BND() macht...
+          catch( call_other(ob, "BecomesNetDead", ME);publish );
+  }
+}
+
+
+/** Sendet ggf. Telnet Timing Marks als Keep-Alive Pakete an den Client.
+  * Wird in heart_beat() gerufen.
+  * @return 1, falls der Spieler Keep-Alive Paket wuenscht, sonst 0. 
+  * @see heart_beat()
+*/
+protected int CheckTelnetKeepAlive() {
+  if (telnet_tm_counter > 0) {
+    // Spieler hat offenbar ein Keep-Alive konfiguriert ...
+    if (!(--telnet_tm_counter)) {
+      // und das Intervall ist gerade abgelaufen.
+      // Prop offenbar gesetzt, FEature ist wirklich gewuenscht,
+      // Telnet Timing Mark senden 
+      send_telnet_timing_mark();
+      // alle 120 HBs (240s, 4min).
+      // sollte eigentlich 240 / __HEART_BEAT_INTERVAL__ sein. Aber spart
+      // eine Operation im HB. ;-)
+      telnet_tm_counter = 120;
+    }
+    return 1; // Keep-Alive ist eingeschaltet
+  }
+  return 0;
+}
+
+static void ndead_move_me();
+
+/** Heartbeat des Spielerobjektes.
+  Prueft taegliche Spielzeit, speichert regelmaessig den Spieler,
+  bewegt Netztote in den Netztodenraum, ruft die HBs aus living/combat und
+  player/life.
+*/
+protected void heart_beat() {
+  if (!ME)
+    return;
+  if (ndead_currently)
+  {
+    if (interactive(ME))
+    {
+      ndead_revive();
+      ndead_location=0;
+    }
+    else return;
+  }
+  else
+    if (!(ndead_next_check--))
+    {
+      ndead_next_check=NETDEAD_CHECK_TIME;
+      if (!interactive(ME))
+        if (ndead_lasttime)
+        {
+          save_me(1);
+          if (IS_LEARNER(ME))
+          {
+            quit();
+            if (ME)
+              remove();
+            if (ME)
+              destruct(ME);
+            return;
+          }
+          ndead_move_me();
+          // Zumindest bei Gaesten ist das Objekt jetzt zerstoert
+          if (!objectp(this_object()))
+            return;
+        }
+        else
+          ndead_lasttime=1;
+    }
+  if (ME && ndead_lasttime && interactive(ME))
+    ndead_lasttime=0;
+  if (CheckDailyPlaytime())
+    return;
+
+  CheckTelnetKeepAlive();
+
+  life::heart_beat();
+  combat::heart_beat();
+  skills::heart_beat();
+}
+
+/** ID-Funktion fuer Spielerobjekte.
+ * id() fuer Spieler. Besondere Behandlung fuer Froesche und Geister,
+ * sowie Invis-Status.
+ * Gibt immer 0 zurueck, wenn P_INVIS && lvl < P_LEVEL
+ * @param[in] str angefragter ID-String
+ * @param[in] lvl Level des Anfragenden
+ * @return 1, falls str auf den Spieler zutrifft, 0 sonst.
+ */
+varargs int id(string str, int lvl)
+{
+  if (::id(str))
+    return 1;
+  if (Query(P_INVIS) && lvl < QueryProp(P_LEVEL))
+    return 0;
+  if (QueryProp(P_GHOST)&& str == "geist von "+ lower_case(QueryProp(P_NAME)))
+    return 1;
+  if (QueryProp(P_FROG) && str == "frosch") return 1;
+  return(0);
+}
+
+/** Setzt Spielerhomepage (Spielerkommando).
+  * @param[in] str Spielereingabe
+  * @return 1 bei Erfolg, 0 sonst.
+  */
+static int set_homepage(string str)
+{
+  mixed tmp;
+  if (!(str=_unparsed_args())) {
+    if (!QueryProp(P_HOMEPAGE))
+      write("Du hast keine URL-Adresse gesetzt!\n");
+    else
+      write("Deine offizielle URL-Adresse lautet: " + QueryProp(P_HOMEPAGE)
+            +"\n");
+    return 1;
+  }
+  write("Deine offizielle URL-Adresse wurde geaendert.\n");
+  if (str=="keine") 
+    SetProp(P_HOMEPAGE, 0);
+  else {
+    tmp = filter(regexplode(str, "[<][^>]*[>]"),
+                       lambda(({'e}), ({#'!=, ({#'[, 'e, 0}), '<'})));
+    write("Sie lautet jetzt: "+(str = implode(tmp, ""))+"\n");
+    SetProp(P_HOMEPAGE, str);
+  }
+  return 1;
+}
+
+/** Setzt Spieler-Wohnort (Spielerkommando).
+  * \param[in] str Spielereingabe
+  * \return 1 bei Erfolg, 0 sonst.
+  */
+static int set_location( string str )
+{
+    mixed ort;
+
+    if ( str == "0" || str == "loeschen" ){
+        Set( P_LOCATION, 0 );
+        write( "Du loescht Deine Ortsangabe.\n" );
+        return 1;
+    }
+
+    if ( stringp(str = _unparsed_args()) && str != "" ){
+        Set( P_LOCATION, capitalize(str) );
+        printf( "Du aenderst Deine Ortsangabe auf \"%s\".\n",
+                Query(P_LOCATION) );
+    }
+    else if ( stringp(ort = Query(P_LOCATION)) )
+        printf( "Deine Ortsangabe lautet \"%s\".\n", Query(P_LOCATION) );
+    else{
+        Set( P_LOCATION, 0, F_VALUE );
+        write( "Du hast keine Ortsangabe gesetzt.\n" );
+    }
+
+    return 1;
+}
+
+/** Setzt ICQ-UIN des Spielers (Spielerkommando).
+  * \param[in] str Spielereingabe
+  * \return 1 bei Erfolg, 0 sonst.
+  */
+static int set_icq(string str) {
+  int num;
+
+  if (!str || str=="") {
+    if (!num=QueryProp(P_ICQ))
+      write("Du hast keine ICQ-Nummer gesetzt!\n");
+    else
+      printf("Deine ICQ-Nummer lautet: %d\n",num);
+    return 1;
+  }
+  if (sscanf(str,"%d",num)!=1 || !num) {
+    write("Deine ICQ-Nummer wurde geloescht.\n");
+    SetProp(P_ICQ, 0);
+  } else {
+    write("Deine ICQ-Nummer wurde geaendert.\n");
+    printf("Sie lautet jetzt: %d\n",num);
+    SetProp(P_ICQ, num);
+  }
+  return 1;
+}
+
+/** Setzt Instant Messanger vom Spieler (Spielerkommando).
+  * \param[in] str Spielereingabe
+  * \return 1 bei Erfolg, 0 sonst.
+  */
+static int set_messenger(string str) {
+  int num;
+  string s;
+
+  if (!str || str=="") {
+    if (!s=QueryProp(P_MESSENGER))
+      if (!num=QueryProp(P_ICQ)) 
+        write("Du hast keine Messenger-ID gesetzt.\n");
+      else 
+        printf("Du hast keine Messenger-ID gesetzt, aber eine ICQ-Nummer: %d\n", num);
+    else
+      printf("Deine Messenger-ID lautet: %s\n", s);
+    return 1;
+  }
+  if (str=="loeschen" || str=="keine") {
+    write("Deine Messenger-ID wurde geloescht.\n");
+    SetProp(P_MESSENGER, 0);
+  } else {
+    s = _unparsed_args();
+    printf("Deine Messenger-ID lautet nun: %s\n", s);
+    SetProp(P_MESSENGER, s);
+  }
+  return 1;
+}
+
+
+// Prueft, ob der String vermutlich eine eMail-Adresse ist.
+// dies ist nicht narrensicher, wird aber die meisten eMail-Adressen zulassen
+// und viel Schrott ablehnen.
+private string check_email(string str) {
+  if (!stringp(str)) return 0;
+  return regmatch(lower_case(str),
+      "[a-z0-9._%+-]+@(?:[a-z0-9-]+\.)+[a-z]{2,4}",RE_PCRE);
+}
+
+/** Setzt Email-Adresse des Spielers (Spielerkommando).
+  * \param[in] str Spielereingabe
+  * \return 1 bei Erfolg, 0 sonst.
+  */
+static int set_email(string str)
+{
+  if (!(str=_unparsed_args())) {
+    write("Deine offizielle Email-Adresse lautet: " + QueryProp(P_MAILADDR)
+          +"\n");
+    return 1;
+  }
+  str = check_email(str);
+  if (!str) {
+    notify_fail("Deine Eingabe scheint keine gueltige EMail-Adresse "
+        "zu sein.\n");
+    return 0;
+  }
+  write("Deine EMail-Adresse wurde geaendert zu:\n"
+      +str+"\n");
+  SetProp(P_MAILADDR, str);
+  return 1;
+}
+
+/** Spielerkommando 'selbstloeschung'.
+  * Gibt Meldung aus und fragt nach einer Bestaetigung.
+  * \return 1 bei Erfolg, 0 sonst.
+  */
+static int self_delete()
+{
+  write(
+    "     B I S T  D U  D I R  W I R K L I C H  S I C H E R ????????????\n"+
+    "Wenn Du Dich selbstloeschen willst, ist Dein Charakter UNWIDERRUFLICH\n"+
+    "verloren. Es gibt KEINE Moeglichkeit ihn wiederzuerschaffen. Solltest\n"+
+    "Du nur zeitweilig vom "MUDNAME" wegbleiben wollen, so benutze bitte\n"+
+    "den Befehl 'spielpause'.\n"+
+    "Fallst Du Dich immer noch selbstloeschen willst, gib Dein Password ein."+
+    "\n\n");
+  input_to("self_delete2",INPUT_PROMPT|INPUT_NOECHO, "Bitte das Password angeben: ");
+  return 1;
+}
+
+/** Spielerkommando 'selbstloeschung'.
+  * Empfaengt Bestaetigung des Spielers und ruft Loeschfunktion in 
+  * /secure/master auf.
+  * \param[in] str Spielereingabe
+  * \return 1 bei Erfolg, 0 sonst.
+  */
+int self_delete2(string str)
+{
+  int ret;
+  ret=(int)"secure/master"->delete_player(str, getuid(PL));
+  if (!ret)
+  {
+    write("Das hat nicht hingehauen (Gott sei Dank ....)\n");
+    return 1;
+  }
+  if (QueryProp(P_GUILD)&&file_size(GUILD_DIR+QueryProp(P_GUILD)+".c")>-1)
+    catch(call_other(GUILD_DIR+QueryProp(P_GUILD), "austreten");publish);
+
+  if (QueryProp(P_DEADS) < 5) {
+    write("Adios! Man sieht sich.\n");
+    say(name(WER,1)+" hat sich gerade selbst zerstoert.\n");
+  }
+  else {
+    write(
+      "\nTod kommt auf seinem weissen Pferd angeritten.\n"
+     +"Er steigt ab, baut sich drohend vor Dir auf und mustert Dich schadenfroh.\n"
+     +"\nTod sagt: ENDLICH! NUN KANN DIR AUCH LARS NICHT MEHR HELFEN!\n"
+     +"\nTod holt weit mit seiner Sense aus. Mit grossem Schwung laesst er sie auf\n"
+     +"Dich zusausen und dann...\n");
+    say(name(WER,1)+" schied gerade endgueltig von uns.\n");
+  }
+
+  // Event ausloesen. ;-)
+  EVENTD->TriggerEvent(EVT_LIB_PLAYER_DELETION, ([
+        E_PLNAME: getuid(ME),
+        E_ENVIRONMENT: environment(),
+        E_GUILDNAME: QueryProp(P_GUILD) ]) );
+
+  remove(1);
+  return 1;
+}
+
+/** Setzt neue taegliche Spieldauer (Spielerkommando).
+  * \param[in] str Spielereingabe
+  * \return 1 bei Erfolg, 0 sonst.
+  */
+static int spieldauer(string str) {
+  int min,day;
+  string foo;
+
+  notify_fail("  spieldauer <x> minuten fuer %d tage\noder\n"+
+              "  spieldauer <x> stunden fuer %d tage\n");
+  if (!str)
+    return 0;
+  if (sscanf(str,"%d stunde%s fuer %d tag%s",min,foo,day,foo)==4)
+    min*=60;
+  else if (sscanf(str,"%d minute%s fuer %d tag%s",min,foo,day,foo)!=4)
+    return 0;
+  if (min<5)
+    min=5;
+  if (min>=1440)
+    return notify_fail("Witzbold.\n"),0;
+
+  Set(P_DAILY_PLAYTIME,
+      ({min*60,time()/86400+day,time()/86400,time(),min*60}));
+  // 0:Minuten pro Tag, 1:Nr. des letzen Tages, 2:Nr. des angefangenen Tages,
+  // 3:letzte Zeitpruefung, 4:verbleibende Zeit
+  printf("Du darfst die naechsten %d Tag(e) nur noch\n"+
+   "%d Minuten am Tag mudden.\n",day,min);
+  return 1;
+}
+
+/** Interpretiert Angabe des Spielers fuer Spielpause.
+  * \param[in] a Zeitangabe fuer Spielpause.
+  * \return Zeitpunkt, Ende der Spielpause.
+  */
+private int InterpretTime(string a){
+  // akzeptiert folgende Formate:
+  //   dd.mm.jj     (Rueckgabe: 0:00 des entsprechenden Tages)
+
+  int *ts = allocate(9);
+  int i,j,k,nrargs;
+
+  if ((nrargs=sscanf(a,"%d.%d.%d",i,j,k))==3 ||
+      (nrargs=sscanf(a,"%d.%d.",i,j))==2) {
+    // wenn kein jahr angegeben ist, das aktuelle nehmen.
+    if (nrargs == 2)
+       ts[TM_YEAR] = localtime()[TM_YEAR];
+    else {
+       // Zwei-Ziffern-Angabe des Jahres...
+       if (k<100)
+           k += 2000;
+       ts[TM_YEAR] = k;
+    }
+    ts[TM_MDAY] = i;
+    ts[TM_MON] = j - 1;
+
+    int zeit = mktime(ts);
+
+    // negative und vergangene Zeiten pruefen.
+    if (zeit <= time()) {
+      write("Dieser Zeitpunkt liegt in der Vergangenheit.\n");
+      return 0;
+    }
+    return zeit;
+  }
+  return 0;
+}
+
+/** Setzt neue Spielpause (Spielerkommando).
+  * Fragt vorher nach Bestaetigung durch den Spieler.
+  * \param[in] str Spielereingabe
+  * \return 1 bei Erfolg, 0 sonst.
+  * \sa spielpause2()
+  */
+static int spielpause(string str)
+{
+  int days,endezeit;
+  string foo;
+
+  notify_fail("spielpause <x> tage          oder\n"+
+              "spielpause bis tt.mm[.jj]\n");
+  if (!str) return 0;
+  if(sscanf(_unparsed_args(),"bis %s",foo)==1) {
+    endezeit = InterpretTime(foo);
+    if (endezeit == 0) 
+        return 0;
+    days = ((endezeit - time()) / 86400) + 1;
+  }
+  else if(sscanf(str, "%d tag%s", days, foo) == 2) {
+    if (days < 0)
+        days = -1;
+    else
+        endezeit = (time()/86400) * 86400 + days * 86400;
+  }
+  else return 0;
+
+  if (days > 0)
+    write(strftime("Du wirst Dich erst wieder am %d.%m.%Y einloggen koennen!\n",
+          endezeit));
+  else if (days < 0)
+    write( "Du wirst Dich auf unbestimmte Zeit nicht mehr einloggen koennen.\n"
+          +"Wenn Du wieder spielen willst, musst Du Dich an einen Gott oder\n"
+          +"Erzmagier wenden (mit einem Gast oder Mail von aussen).\n" );
+  else {
+    write( "Die Spielpause ist aufgehoben.\n" );
+    master()->TBanishName(getuid(this_object()), 0);
+    return 1;
+  }
+  write( "Wenn Du das wirklich willst, gib jetzt 'ja' ein.\n" );
+  input_to( "spielpause2", INPUT_PROMPT, "]", days);
+  return 1;
+}
+
+/** Setzt neue taegliche Spieldauer, wird ueber spielpause() gerufen.
+  * \param[in] str Spielereingabe, Bestaetigung
+  * \param[in] days Dauer der Spielpause (in Tagen).
+  * \sa spielpause()
+  */
+static void spielpause2(string str, int days)
+{
+  if (str && (str == "ja" || str == "Ja" || str == "JA")) {
+    master()->TBanishName(getuid(this_object()), days);
+    write(
+      "Ok, die Spielpause wird mit dem naechsten Ausloggen wirksam.\n"
+     +"Solltest Du es Dir bis dahin noch einmal ueberlegt haben, so kannst\n"
+     +"Du den Vorgang mit 'spielpause 0 tage' wieder rueckgaengig machen.\n" );
+    return;
+  }
+  write("Vorgang wurde abgebrochen.\n" );
+}
+
+/** Setzt neues Passwort (Spielerkommando).
+  * Fragt nach altem Passwort und ruft change_password2().
+  * \return 1 bei Erfolg, 0 sonst.
+  * \sa change_password2(), change_password3(), change_password4()
+  */
+static int change_password() {
+  string verb;
+  verb=query_verb();
+  if (verb!="passwd"&&verb!="password"&&verb!="passwort")
+    return 0;
+  input_to("change_password2",INPUT_NOECHO|INPUT_PROMPT,
+      "Bitte das ALTE Passwort angeben: ");
+  return 1;
+}
+
+/** Setzt neues Passwort (Spielerkommando).
+  * Prueft altes Passwort, fragt nach neuem Passwort und ruft 
+  *   change_password3().
+  * \param[in] str Spielereingabe des alten Passwortes
+  * \return 1 bei Erfolg, 0 sonst.
+  * \sa change_password(), change_password3(), change_password4()
+  */
+static int change_password2(string str) {
+  write("\n");
+  if (!str)
+    str="";
+  if (MASTER->update_password(str,str) == 0) {
+    write("Falsches Passwort!\n");
+    return 1;
+  }
+  passwold = str;
+  input_to("change_password3",INPUT_NOECHO|INPUT_PROMPT,
+      "Bitte das NEUE Passwort eingeben: ");
+  return 1;
+}
+
+/** Setzt neues Passwort (Spielerkommando).
+  * Prueft neues Passwort, fragt nach Bestaetigung des neues
+  * Passwortes und ruft change_password4().
+  * \param[in] str Spielereingabe des neuen Passwortes
+  * \return 1 bei Erfolg, 0 sonst.
+  * \sa change_password(), change_password2(), change_password4()
+  */
+static int change_password3( string str )
+{
+    write( "\n" );
+
+    if ( !str || str == "" ){
+        write( "Abgebrochen !\n" );
+        passwold = passw = 0;
+        return 1;
+    }
+
+    if ( passwold == str ){
+        write( "Das war Dein altes Passwort.\n" );
+        input_to( "change_password3", INPUT_NOECHO|INPUT_PROMPT,
+            "Bitte das NEUE Passwort eingeben (zum Abbruch Return druecken): ");
+        return 1;
+    }
+
+    if ( !MASTER->good_password( str, getuid(ME) ) ){
+        input_to( "change_password3", INPUT_NOECHO|INPUT_PROMPT,
+            "Bitte das NEUE Passwort eingeben: ");
+        return 1;
+    }
+
+    passw = str;
+    input_to( "change_password4", INPUT_NOECHO|INPUT_PROMPT,
+        "Bitte nochmal: ");
+    return 1;
+}
+
+/** Setzt neues Passwort (Spielerkommando).
+  * Prueft neues Passwort und setzt neues Passwort.
+  * \param[in] str Spielereingabe des neuen Passwortes
+  * \return 1 bei Erfolg, 0 sonst.
+  * \sa change_password(), change_password2(), change_password3()
+  */
+static int change_password4( string str )
+{
+    write( "\n" );
+
+    if ( !str || str != passw ){
+        write( "Das war verschieden! Passwort NICHT geaendert.\n" );
+        passwold = passw = 0;
+        return 1;
+    }
+
+    if ( MASTER->update_password( passwold, passw ) )
+        write( "Passwort geaendert.\n" );
+    else
+        write( "Hat nicht geklappt!\n" );
+
+    passwold = passw = 0;
+    return 1;
+}
+
+
+/*
+ *-----------------------------------------------------------------
+ * Rueckmeldungen von Spielern an Magier
+ *-----------------------------------------------------------------
+ */
+static int fehlerhilfe(string str) {
+  write("Welche Art von Fehler moechtest Du denn melden?\n"
+      "Fehlfunktionen    ->  bug\n"
+      "Ideen/Anregungen  ->  idee\n"
+      "Tippfehler/Typos  ->  typo\n"
+      "fehlende Details  ->  detail\n");
+
+  return 1;
+}
+
+/** Setzt eine Fehlermeldung an Magier ab (Spielerkommando).
+  * Fragt nach der Fehlermeldung und liest sie via bug2() ein, fall der
+  * Spieler kein Argument angeben hat.
+  * \param[in] str optionale Spielereingabe der Fehlerbeschreibung
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see bug2(string)
+  */
+static int bug(string str) {
+  if (!(str=_unparsed_args())) {
+    write( "Wie sieht der Fehler denn aus?\n" );
+    input_to("bug2", INPUT_PROMPT, "]");
+    return 1;
+  }
+  write("Vielen Dank fuer die Hilfe.\n");
+  smart_log("BUGS",str);
+  return 1;
+}
+
+/** Setzt eine Fehlermeldung an Magier ab (Spielerkommando).
+  * Lies Fehlerbeschreibung ein und speichert sie ab.
+  * \param[in] str Spielereingabe der Fehlerbeschreibung.
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see bug(string)
+  */
+static int bug2(string str) {
+  if (!str || str == "") {
+    write("Bug abgebrochen...\n");
+    return 1;
+  }
+  write("Vielen Dank fuer die Hilfe.\n");
+  smart_log("BUGS",str);
+  return 1;
+}
+
+/** Setzt eine Typomeldung an Magier ab (Spielerkommando).
+  * Fragt nach der Typomeldung und liest sie via typo2() ein, fall der
+  * Spieler kein Argument angeben hat.
+  * \param[in] str optionale Spielereingabe der Typobeschreibung
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see typo2(string)
+  */
+static int typo(string str) {
+  if (!(str=_unparsed_args())) {
+    write( "Wo ist denn der Tippfehler?\n" );
+    input_to("typo2", INPUT_PROMPT, "]");
+    return 1;
+  }
+  write("Vielen Dank fuer die Hilfe.\n");
+  smart_log("TYPO",str);
+  return 1;
+}
+
+/** Setzt eine Fehlermeldung an Magier ab (Spielerkommando).
+  * Liest die Typobeschreibung ein und speichert sie.
+  * \param[in] str Spielereingabe der Typobeschreibung
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see typo(string)
+  */
+static int typo2(string str) {
+  if (!str || str == "") {
+    write("Typo abgebrochen...\n");
+    return 1;
+  }
+  smart_log("TYPO",str);
+  write("Vielen Dank fuer die Hilfe.\n");
+  return 1;
+}
+
+/** Setzt eine Idee an Magier ab (Spielerkommando).
+  * Fragt nach der Idee und liest sie via idee2() ein, falls der
+  * Spieler kein Argument angeben hat.
+  * \param[in] str optionale Spielereingabe der Idee
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see idea2(string)
+  */
+static int idea(string str) {
+  if (!(str=_unparsed_args())) {
+    write( "Was fuer eine Idee hast Du denn?\n" );
+    input_to("idea2",INPUT_PROMPT, "]");
+    return 1;
+  }
+  write("Vielen Dank fuer die Hilfe.\n");
+  smart_log("IDEA",str);
+  return 1;
+}
+
+/** Setzt eine Idee an Magier ab (Spielerkommando).
+  * Liest die Idee ein und speichert sie.
+  * \param[in] str Spielereingabe der Idee
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see idea(string)
+  */
+static int idea2(string str) {
+  if (!str || str == "") {
+    write("Idee abgebrochen...\n");
+    return 1;
+  }
+  write("Vielen Dank fuer die Hilfe.\n");
+  smart_log("IDEA",str);
+  return 1;
+}
+
+/** Setzt ein fehlendes Detail an Magier ab (Spielerkommando).
+  * Fragt nach dem Detail und liest es via idee2() ein, falls der
+  * Spieler kein Argument angeben hat.
+  * \param[in] str optionale Spielereingabe des fehlenden Details
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see md2(string)
+  */
+static int md(string str) {
+  if (!(str=_unparsed_args())) {
+    write( "Fuer welches Detail fehlt denn die Beschreibung?\n" );
+    input_to("md2",INPUT_PROMPT, "]");
+    return 1;
+  }
+  write("Vielen Dank fuer die Hilfe.\n");
+  smart_log("DETAILS",str);
+  return 1;
+}
+
+/** Setzt ein fehlendes Detail an Magier ab (Spielerkommando).
+  * Liest das Detail ein und speichert es.
+  * \param[in] str Spielereingabe des fehlenden Details.
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see md(string)
+  */
+static int md2(string str) {
+  if (!str || str == "") {
+    write("Details abgebrochen...\n");
+    return 1;
+  }
+  write("Vielen Dank fuer die Hilfe.\n");
+  smart_log("DETAILS",str);
+  return 1;
+}
+
+/** Loggt eine Spielermeldung an Magier.
+  * Loggt die Spielermeldung in das passende File unter /log/report/ oder im
+  * vom Magier gewuenschten File. Hierbei werden Fehler, Ideen, MDs und Typos
+  * in getrennte Files sortiert.
+  * \param[in] str Spielermeldung
+  * \param[in] myname Art der Spielermeldung (DETAILS, BUG, TYPO, MD)
+  * @see md(string), idea(string), bug(string), typo(string)
+  */
+void smart_log(string myname, string str)
+{
+  string obnam;
+  object obj;
+
+  string *tmp = explode(str, ":");
+  if (sizeof(tmp) > 1) {
+    obnam = lower_case(trim(tmp[0]));
+    obj = present(obnam, environment()) || present(obnam);
+    if (!obj) {
+      obj = environment(this_object());
+    }
+    else // nur hier Teil vor dem : wegschneiden
+      str = trim(implode(tmp[1..],":"));
+  }
+  else {
+    obj = QueryProp(P_REFERENCE_OBJECT);
+    if (!obj || !present(obj))
+      obj = environment(this_interactive());
+  }
+
+  mapping err = ([ F_PROG: "unbekannt",
+           F_LINE: 0,
+           F_MSG: str,
+           F_OBJ: obj
+         ]);
+
+  string desc="etwas unbekanntes";
+  switch(myname) {
+    case "BUGS":
+      desc="einen Fehler";
+      err[F_TYPE]=T_REPORTED_ERR;
+      break;
+    case "DETAILS":
+      desc="ein fehlendes Detail";
+      err[F_TYPE]=T_REPORTED_MD;
+      break;
+    case "IDEA":
+      desc="eine Idee";
+      err[F_TYPE]=T_REPORTED_IDEA;
+      break;
+    case "TYPO":
+      desc="einen Typo";
+      err[F_TYPE]=T_REPORTED_TYPO;
+      break;
+  }
+
+  // Eintragung in die Fehler-DB
+  string hashkey = (string)ERRORD->LogReportedError(err);
+
+  // ggf. will das Objekte mit noch irgendwas anfangen.
+  obj->SmartLog(0, myname, str, strftime("%d. %b %Y"));
+
+  tell_object(this_object(), break_string( sprintf(
+    "Du hast an %s erfolgreich %s abgesetzt.\n"
+    "Die ID der abgesetzten Meldung lautet: %s\n",
+    (obj->IsRoom() ? "diesem Raum" : obj->name(WEM,1)),desc,
+    hashkey||"N/A"),78,BS_LEAVE_MY_LFS));
+}
+
+/** Speichert den Spieler und loggt ihn aus (Spielerkommando 'ende').
+  * Der Spieler wird vollstaendig ausgeloggt, d.h. das Spielerobjekt
+  * zerstoert.
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see disconnect()
+  */
+int quit()
+{
+  int arg;
+  SetProp(P_LAST_QUIT,time());
+  catch(RemoveChannels();publish);
+  if(!QueryGuest())
+  {
+    save_me(0);
+    tell_object(ME,"Speichere "+QueryProp(P_NAME)+".\n");
+  }
+
+  if (interactive(ME))
+    call_notify_player_change(0);
+
+  remove_living_name();
+  // EVT_LIB_LOGOUT wird in remove() getriggert.
+  if(catch(remove();publish)) destruct(ME);
+  return 1;
+}
+
+/** Wrapper im quit() herum, verhindert 'ende', falls Spieler kaempft.
+  * \return 0 oder Rueckgabewert von quit()
+  * @see quit()
+  */
+static int new_quit() {
+  notify_fail("Du bist in Gedanken noch bei Deinem letzten Kampf.\n"+
+              "Warte noch etwas bevor Du das Spiel verlaesst,\n"+
+              "damit Du so nicht in RL weitermachst...\n");
+  if (time()-Query(P_LAST_COMBAT_TIME)<120 && !IS_LEARNING(ME))
+    return 0;
+  return quit();
+}
+
+/** Gibt die Infos ueber den Char an den Spieler aus (Spielerkommando 'info').
+  * \param[in] arg Wenn arg=="short", wird eine Kurzuebersicht ausgegeben.
+  * \return 1
+  * @see short_score()
+  */
+static int score(string arg) {
+  string tmp, gender;
+  int i,sz,val;
+  mixed ind;
+  object *enem1, *enem2, *inv;
+
+  if (QueryProp(P_GHOST)) {
+    write("Im ewigen Leben gibt es keine Punkte.\n");
+    return 1;
+  }
+
+  int plev = LEPMASTER->QueryLevel();
+ 
+  switch(tmp = QueryProp(P_GENDER)) {
+  case MALE: gender = "maennlich"; break;
+  case FEMALE: gender = "weiblich"; break;
+  case NEUTER: gender = "neutral"; break;
+  default: gender = lower_case(tmp);
+  }
+
+  ind = m_indices(QueryProp(P_ATTRIBUTES));
+  tmp = "";
+  foreach(string index: ind) {
+    string aname;
+    switch (index) {
+      case "int": aname = "Intelligenz"; break;
+      case "con": aname = "Ausdauer"; break;
+      case "dex": aname = "Geschicklichkeit"; break;
+      case "str": aname = "Kraft"; break;
+      default:
+        if(stringp(index)) aname = capitalize(index);
+        else aname = "Unbekannt";
+    }
+    aname = sprintf("%-18'.'s %2.2d", aname+" ", QueryRealAttribute(index));
+    if((val = QueryAttributeOffset(index)))
+      aname += sprintf(" (%s%d)", (val>=0?"+":""), val);
+    tmp += aname + "\n";
+  }
+
+  printf("- %-'-'68s\n",
+         TeamPrefix()+capitalize(implode(explode(short()||"","\n"),""))+" ");
+  if(arg!="short") {
+    printf("Rasse ............ %-' '18s Abenteuer ........ %d %s\n",
+           QueryProp(P_RACE), QueryProp(P_QP),
+           (val = QM->QueryTotalQP()) == QueryProp(P_QP) ? "" : "("+val+")");
+    printf("Geschlecht ....... %-' '18s Groesse .......... %d cm\n",
+           gender, QueryProp(P_SIZE));
+    printf("Stufe ............ %-3.3d %-' '14s Gewicht .......... %d kg\n",
+           QueryProp(P_LEVEL), (QueryProp(P_LEVEL) < plev ? "("+plev+")" : ""),
+           QueryProp(P_WEIGHT) / 1000);
+    printf("Gilde ............ %-' '18s Gildenstufe ...... %d\n",
+         capitalize(QueryProp(P_GUILD)), QueryProp(P_GUILD_LEVEL));
+  }
+  printf("Erfahrung ........ %-' '18s Charakter ........ %-s\n\n",
+         QueryProp(P_XP)+ " Punkte", al_to_title(QueryProp(P_ALIGN)));
+  printf("%#-76.2s\n\n", tmp);
+  printf("Gesundheit ....... %-3.3d %-' '14s Gift ............. %s\n",
+         QueryProp(P_HP),
+         (QueryProp(P_HP) == (val = QueryProp(P_MAX_HP)) ? "" : "("+val+")"),
+         ((val = QueryProp(P_POISON)) ?
+          (val < 4 ? "leicht" : "gefaehrlich") : "gesund"));
+  printf("Konzentration .... %-3.3d %-' '14s Vorsicht ......... %s\n",
+         QueryProp(P_SP),
+         (QueryProp(P_SP) == (val = QueryProp(P_MAX_SP)) ? "" : "("+val+")"),
+         ((ind = QueryProp(P_WIMPY)) ? ""+ind : "mutig"));
+  printf("Todesfolgen....... %-' '18s %s\n",
+         ((val = death_suffering()) ? ""+((val+9)/10) : "kein Malus"),
+         (QueryProp(P_WIMPY) && ind=QueryProp(P_WIMPY_DIRECTION))
+         ? sprintf("Fluchtrichtung ... %O", ind) : "");
+  printf("%s",
+         (time()-Query(P_LAST_COMBAT_TIME)<120 && !IS_LEARNING(ME)) ?
+         "Spiel verlassen .. nicht moeglich\n" : ""
+         );
+
+  if(arg!="short") {
+    write(break_string(Forschung(), 70));
+    if(ind=QueryProp(P_AWAY))
+      printf("Du bist nicht ansprechbar: %O\n",ind);
+  }
+
+  if(sizeof(enem1=((mixed)QueryEnemies())[0])) {
+    enem2=({});
+    inv=all_inventory(environment(ME));
+    foreach(object en: enem1) {
+      if (member(inv,en)==-1) // Ist unser Feind und ist nicht hier
+        enem2+=({en});
+    }
+    if(sizeof(enem2))
+    {
+      write(break_string(
+            "Du verfolgst " + CountUp(map_objects(enem2, "name", WEN))+".",
+            78));
+    }
+  }
+  if(arg!="short") show_age();
+  printf("%-'-'70s\n", "");
+  return 1;
+}
+
+/** Gibt eine kuerzere Info ueber den Char aus (Spielerkommando punkte|score).
+    Ruft score("short").
+  * \param[in] arg UNUSED
+  * \return 1 bei Erfolg, 0 sonst.
+  * @see score(string), very_short_score()
+  */
+static int short_score(string arg) {
+  return score("short");
+}
+
+/** Gibt eine Miniinfo ueber LP / KP aus (Spielerkommando: kurzinfo)
+  * \return 1
+  * @see score(string), short_score(string)
+  */
+static int very_short_score(string arg) {
+  int    lp,mlp,xlp,kp,mkp,xkp;
+  string bar;
+
+  lp=QueryProp(P_HP); mlp=QueryProp(P_MAX_HP);
+  kp=QueryProp(P_SP); mkp=QueryProp(P_MAX_SP);
+  if (mlp)
+    xlp=(lp*40/mlp);
+  if (mkp)
+    xkp=(kp*40/mkp);
+  bar="  .    .    .    .    .    .    .    .  ";
+  if (QueryProp(P_NO_ASCII_ART) || arg == "-k")
+    printf("Gesundheit: %3.3d (%3.3d), Konzentration: %3.3d (%3.3d)\n",
+           lp, mlp, kp, mkp);
+  else
+    printf("Gesundheit:    0 |%'#'40.40s| %3.3d%s\n"+
+           "Konzentration: 0 |%'#'40.40s| %3.3d%s\n",
+           (xlp<0?bar:bar[xlp..]),lp,(lp==mlp?"":sprintf(" (%d)",mlp)),
+           (xkp<0?bar:bar[xkp..]),kp,(kp==mkp?"":sprintf(" (%d)",mkp))
+           );
+  return 1;
+}
+
+/** Gibt eine Manpage/Hilfeseite an den Spieler aus.
+    Beruecksichtigt hierbei die Synonymliste aus dir/.synonym, um die richtige
+    Manpage auszugeben.
+  * \param[in] dir Verzeichnis der gewuenschten Manpage
+  * \param[in] page Name der gewuenschten Manpage
+  * \return String der gewuenschten Manpage
+  */
+static string getmanpage(string dir, string page)
+{
+  string text, *syn;
+  int i;
+
+  if (dir[<1] != '/')
+    dir += "/";
+
+  if ((text=read_file(dir+page)) && sizeof(text))
+    return text;
+
+  if (text = read_file(dir+".synonym")) {
+    syn = regexplode(text, "([ \t][ \t]*|\n)");
+    if ((i=member(syn, page))!=-1)
+      return read_file(dir+syn[i+2]);
+  }
+  return 0;
+}
+
+/** Gibt eine Hilfeseite an den Spieler aus (Spielerkommando hilfe|man).
+  * Die Hilfeseite wird in div. Verzeichnissen gesucht (je nach Gilde,
+  * Magier-/Spielerstatus).
+  * \param[in] str Name der gewuenschten Hilfeseite.
+  * \return 1
+  */
+static int help(string str) {
+  string verb, rest, text, gilde;
+  mixed found;
+
+  found=0;
+  text = "";
+  if (str) {
+    str = implode( explode(str, ".." ), "");
+
+    if ( sscanf( str, "gilde %s %s", gilde, rest)==2)
+      str=rest;
+    else
+      gilde=QueryProp(P_GUILD);
+    if (!gilde) gilde="abenteurer";
+
+    if ( sscanf( str, "%s %s",verb,rest )==2 ) str = verb;
+
+    if ((IS_LEARNER(PL)) ) {
+      if (rest = getmanpage("/doc/wiz/",str)) {
+        found = 1;
+        text += rest;
+      }
+      else if (rest = getmanpage("/doc/mcmd/", str)) {
+        found = 1;
+        text += rest;
+      }
+    }
+
+    if ((IS_SEER(PL)) /*&& !found*/ ) {
+      if (rest = getmanpage("/doc/scmd/",str)) {
+        if (found)
+          text += "\n--------------------\n";
+        found = 1;
+        text += rest;
+      }
+    }
+
+    if (rest = getmanpage("/doc/g."+gilde+"/",str)) {
+      if (found)
+        text += "\n--------------------\n";
+      found = 1;
+      text += rest;
+    } else {
+      if (rest = getmanpage("/doc/help/",str)) {
+        if (found)
+          text += "\n--------------------\n";
+        found = 1;
+        text += rest;
+      }
+      else if (rest = getmanpage("/doc/pcmd/",str)) {
+        if (found)
+          text += "\n--------------------\n";
+        found = 1;
+        text += rest;
+      }
+      else if (rest = getmanpage("/doc/REGELN/",str)) {
+        if (found)
+          text += "\n--------------------\n";
+        found = 1;
+        text += rest;
+      }
+    }
+
+    if (!found)
+      text = "Dazu ist keine Hilfe verfuegbar.\n";
+
+    More(text,0);
+    return 1;
+  }
+  if (IS_LEARNER(PL))
+    text = read_file("/doc/hilfe.magier");
+  else if (IS_SEER(PL))
+    text = read_file("/doc/hilfe.seher");
+
+  More(text + read_file("/doc/hilfe.spieler"), 0);
+  return 1;
+}
+
+/** Ermittelt angebene Optionen fuer das Spielerkommando 'wer'.
+  * \param[in] str vom Spieler spezifizierter String von Filteroptionen: -k,
+  * -v, -a, -s (+ Langformen).
+  * \return Array von Spieleroptionen als veroderte Int-Flags und Rest der
+  * Spielereingabe ohne die Optionen.
+  */
+static mixed filter_who_options(string str)
+{
+  string* opt, *ans;
+  int i,len,res;
+
+  opt = explode(str," "); len=sizeof(opt);
+  ans = ({});
+  res = 0;
+  for(i=0;i<len;i++)
+    switch(opt[i]){
+      case "-k":
+      case "-kurz":
+      res |= WHO_SHORT; break;
+      case "-v":
+      case "-vertikal":
+      res |= WHO_VERTICAL; break;
+      case "-alphabetisch":
+      case "-a":
+      case "-alpha":
+      res |= WHO_ALPHA; break;
+      case "-s":
+      case "-spieler":
+      res |= WHO_PLAYER_VIEW; break;
+      default:
+      return ({ res, implode(opt[i..]," ") });
+    }
+  return ({ res, 0 });
+
+}
+
+/** Spielerkommando 'wer', fragt /obj/werliste ab.
+  * \param[in] str Spielereingabe mit Optionen fuer wer.
+  * \return 1
+  */
+static int who(string str) {
+  int i,shrt;
+  string ret;
+  mixed ans;
+
+  if ((str=_unparsed_args())&&str[0..0]!="-") {
+    ans = filter_who_options(str);
+    shrt = ans[0];
+    str = ans[1];
+    if (!shrt) {
+      if (ret=INETD->_send_udp(str,
+                              ([ REQUEST: "who", SENDER: getuid(ME) ]), 1 ))
+        write(ret);
+      else
+        write("Anfrage abgeschickt.\n");
+      return 1;
+    }
+  }
+  if (str) i=(member(str,'o')>0); else i=0;
+  if (sizeof(str)>1 && str[0] == '-') str = str[1..1];
+  More(implode( "/obj/werliste"->QueryWhoListe(
+    IS_LEARNER(ME) && QueryProp(P_WANTS_TO_LEARN),shrt,0,str,i),"\n"),0);
+  return 1;
+}
+
+/** Spielerkommando 'kwer', fragt /obj/werliste ab.
+  * \param[in] str Spielereingabe mit Optionen fuer wer.
+  * \return 1
+  */
+static int kwho(string str)
+{
+  int shrt;
+  mixed res;
+
+  if(str) {
+    res = filter_who_options(str);
+    shrt = res[0];
+    str = res[1];
+  }
+  More(implode( "/obj/werliste"->QueryWhoListe(
+      IS_LEARNER(ME) && QueryProp(P_WANTS_TO_LEARN), shrt|WHO_SHORT ,0,str),
+    "\n")+"\n\n",0);
+  return 1;
+}
+
+/** Spielerkommando 'kkwer', gibt eine einfache Liste der Anwesenden aus.
+    Filtert unsichtbare Spieler aus, falls SPielerobjekt kein Magier ist.
+  * \param[in] str Spielereingabe mit Optionen fuer wer.
+  * \return 1
+  */
+static varargs int kkwho(string str) {
+  object *obs;
+  string *namen;
+
+  obs=filter_users(str);
+  namen=({});
+  if (IS_LEARNER(this_player())) {
+    foreach(object ob: obs) {
+      if (environment(ob))
+        namen+=({capitalize(geteuid(ob))});
+    }
+  } 
+  else {
+    foreach(object ob: obs) {
+      if (!ob->QueryProp(P_INVIS) && environment(ob))
+        namen+=({capitalize(geteuid(ob))});
+    }
+  }
+  if (sizeof(namen))
+    write(break_string(CountUp(sort_array(namen,#'>),", ", ", ")+".",75));
+  else
+    write("Keine passenden Spieler gefunden.\n");
+  
+  return 1;
+}
+
+/** Spielerkommando 'toete'.
+  * Prueft auf Geist, Gast, toten HC-Spieler, Waffe/freie Hand. Versucht einen
+  * Gegner zu finden und ruft dann Kill(string).
+  * \param[in] str Spielereingabe
+  * \return 1 oder 0, falls kein potentieller Gegner bei 'toete alle' gefunden
+  * wird.
+  */
+static int kill(string str) {
+  object eob,wob;
+
+  if (QueryProp(P_GHOST))
+  {
+    write("Das kannst Du in Deinem immateriellen Zustand nicht.\n");
+    return 1;
+  }
+  
+  if(hc_play>1)
+  {
+    write("DAS HAST DU HINTER DIR.\n");
+    return 1;
+  }
+  
+  if (QueryGuest())
+  {
+    write("Du bist doch nur Gast hier.\n");
+    return 1;
+  }
+  if (!str || str == "") {
+    write("WEN willst Du toeten?\n");
+    return 1;
+  }
+  if( !QueryProp(P_WEAPON) && QueryProp(P_FREE_HANDS)==0 ) {
+    write(
+      "Dazu solltest Du eine Waffe gezueckt oder eine Hand frei haben.\n");
+    return 1;
+  }
+  str=lower_case(str);
+  if (str=="alle") {
+    object livs;
+    livs=filter(all_inventory(environment(PL)),
+        function int (object ob) {
+            if (living(ob) && !query_once_interactive(ob)
+                  && !ob->QueryProp(P_INVIS)
+                  && !ob->QueryProp(P_NO_GLOBAL_ATTACK)
+                  && !ob->QueryProp(P_FRIEND))
+            {
+                Kill(ob);
+                return 1;
+            }
+            return 0;
+        } );
+    // wenn Gegner gefunden, raus, ansonsten kommt die Fehlermeldung unten.
+    if (sizeof(livs)) return 1;
+  }
+  else {
+    int i=1;
+    while(objectp(eob = present(str,i++,environment(PL)))) {
+      if (living(eob) && !eob->QueryProp(P_INVIS))
+        break;
+      else
+        eob=0;
+    }
+  }
+  if (!objectp(eob)) {
+    // per write und return 1 ist hier mal ok, weil dies Kommando im Spieler
+    // eh zuletzt in der Kommandokette ausgefuehrt wird und per return 0 eh
+    // kein anderes mehr zum Zug kommt.
+    write("Du siehst hier kein derartiges Wesen!\n");
+    return 1;
+  }
+  else if (eob == PL) {
+    write("Selbstmord ist keine Loesung!\n");
+    return 1;
+  }
+
+  /* Kill him */
+  Kill(eob);
+  return 1;
+}
+
+/** Spielerkommando 'stop'.
+  * Loescht die Gegnerliste, sofern man nicht InFight() ist.
+  * \param[in] str Spielereingabe, wird ignoriert.
+  * \return 1
+  */
+static int stop( string str )
+{
+    if ( InFight() ){
+        write( "Das geht nicht mitten im Kampf.\n" );
+        return 1;
+    }
+
+    if ( !str ){
+        StopHuntingMode();
+        write( "Ok.\n" );
+        return 1;
+    }
+
+    if ( !StopHuntID(str) )
+        write( "So jemanden verfolgst Du nicht!\n" );
+
+    return 1;
+}
+
+/** Spielerkommando fuers emoten ':'.
+  * \param[in] str Spielereingabe
+  * \param[in] genitiv Genetivflag
+  * \return 1 oder 0, falls Spieler nicht emoten kann (kein CAN_EMOTE Flag in
+  * P_CAN_FLAGS).
+  */
+int emote(string str,int genitiv)
+{
+  string *commands,message,verb;
+  object living;
+  int i,size;
+
+  if (!(Query(P_CAN_FLAGS)&CAN_EMOTE)) return 0;
+  if (query_verb()[0]==';') genitiv=1;
+  if (query_verb()[0]==':'||query_verb()[0]==';')
+    verb=query_verb()[1..]+" ";
+  else
+    verb="";
+  str=this_player()->_unparsed_args();
+  commands=explode(verb+(str||""),"#");
+  message=break_string((IS_SEER(ME) ? "" : ">")
+                       +capitalize(genitiv ? name(WESSEN) :
+                                   name())
+                       +" "+commands[0],78);
+  size=sizeof(commands);
+  if(size>=3)
+  {
+    living=find_living(lower_case(commands[1]));
+    if(!living || environment(living)!=environment() ||
+       (living->QueryProp(P_INVIS)) && !IS_LEARNER(ME))
+    {
+      write(capitalize(commands[1])+" sehe ich hier nicht!\n");
+      return 1;
+    }
+    if(living!=this_object())
+      tell_object(living,break_string((IS_SEER(this_player()) ? "" : ">")
+                                    +capitalize(genitiv ?
+                                                this_player()->name(WESSEN) :
+                                                this_player()->name())
+                                    +" "+commands[2],78));
+  }
+  if(size>=4)
+    write(break_string(commands[3],78));
+  else
+    write(message);
+  tell_room(environment(),message,({this_object(),living}));
+  return 1;
+}
+
+/** Spielerkommando fuers remoten 'r:'.
+  * \param[in] str Spielereingabe
+  * \param[in] flag Genetivflag
+  * \return 1 oder 0, falls Spieler nicht emoten kann (kein CAN_REMOTE Flag in
+  * P_CAN_FLAGS).
+  */
+static int remote(string str, int flag)
+{
+  int m;
+  string tmp, dest;
+  string *exstr;
+  object destpl;
+
+  if ( !(Query(P_CAN_FLAGS) & CAN_REMOTE) )
+      return 0;
+
+  if ( !(str=_unparsed_args()) ||
+       sizeof( (exstr=explode(str," ")) - ({""}) ) <= 1 ){
+      write("Was willst Du zu wem `emoten`?\n");
+      return 1;
+  }
+
+  dest = lower_case(exstr[0]);
+
+  if( !(destpl=find_player( dest ) ) ||
+      (destpl->QueryProp(P_INVIS) && !IS_LEARNER(ME)) ){
+    write("Einen solchen Spieler gibt es derzeit nicht.\n");
+    return 1;
+  }
+
+  tmp = implode( exstr[1..], " " );
+
+  tmp = regreplace( tmp, "(^|[^#])#($|[^#])", "\\1aus der Ferne\\2", 1 );
+  tmp = regreplace( tmp, "(^|[^\\^])\\^($|[^\\^])", "\\1in der Ferne\\2", 1 );
+  tmp = regreplace( tmp, "##", "#", 1 );
+  tmp = regreplace( tmp, "\\^\\^", "^", 1 );
+
+  if ( strstr( tmp, "aus der Ferne" ) == -1
+       && strstr( tmp, "in der Ferne" ) == -1 )
+      tmp += " aus der Ferne";
+
+  if ( QueryProp(P_INVIS) && IS_LEARNER(destpl) ){
+      str = "(" + capitalize(getuid(ME));
+      if ( flag )
+          str += member( "sxz", str[<1] ) == -1 ? "s" : "'";
+      str += ")";
+  }
+  else
+      str = (flag ? capitalize(name(WESSEN)) : capitalize(name(WER)));
+
+  str += " " + tmp + (member( ".?!", tmp[<1] ) == -1 ? "." : "") + "\n";
+
+  m = destpl->ReceiveMsg(str, MT_COMM|MT_FAR, MA_EMOTE,0, ME);
+  switch(m)
+  {
+    case MSG_DELIVERED:
+      _recv(destpl, capitalize(destpl->name()) + "->" + str, MSGFLAG_REMOTE);
+      break;
+    case MSG_BUFFERED:
+      write( capitalize(destpl->name(WER) + " ist gerade beschaeftigt.\n") );
+      break;
+    case MSG_IGNORED:
+      write( capitalize(destpl->name(WER) + " ignoriert Dich.\n") );
+      break;
+    case MSG_VERB_IGN:
+    case MSG_MUD_IGN:
+      write( capitalize(destpl->name(WER) + " ignoriert Deine Meldung.\n") );
+      break;
+    default:
+      write( capitalize(destpl->name(WER) + " kann Dich gerade nicht "
+            "wahrnehmen.\n") );
+  }
+  return 1;
+}
+
+/** Spielerkommando fuers emoten im Genitiv ';'.
+  * Ruft emote(string,int) auf.
+  */
+static int gemote(string str)
+{
+  return emote(str, 1);
+}
+
+/** Spielerkommando fuers remoten im Genitiv 'r;'.
+  * Ruft remote(string, int) auf.
+  */
+static int gremote(string str)
+{
+  return remote(str, 1);
+}
+
+static void load_auto_objects(mapping map_ldfied);
+
+private void InitPlayer();
+private void InitPlayer2();
+private void InitPlayer3();
+
+/** Gibt eine Zufallszahl um P_AVERAGE_SIZE herum zurueck.
+  * \return Zufaellige Groesse.
+  */
+private int RandomSize()
+{
+  return (100+random(13)-random(13)+random(13)-random(13))*
+    (QueryProp(P_AVERAGE_SIZE)||170)/100;
+}
+
+/** Setzt bestimmte Props im Spieler, falls diese nicht gesetzt sind oder
+  * loescht obsolete Props. Repariert bestimmte Datenstrukturen im Spieler.
+  * Wird von start_player() nach Laden des Savefiles gerufen.
+  * Momentan wird z.B. die Groesse gesetzt, falls sie bisher 0 ist und die
+  * Skills des Spielers initialisiert bzw. repariert oder auf die aktuellste
+  * Version des Skillsystems
+  * Ruft ggf. InitSkills() und FixSkills().
+  * @param[in] newflag Gibt an, ob es ein neuerstellter Spieler ist.
+  * @sa start_player(), InitSkills(), FixSkills()
+  */
+private void updates_after_restore(int newflag) {
+ 
+  // Seher duerfen die Fluchtrichtung uebermitteln lassen.
+  // Eigentlich koennte es Merlin machen. Dummerweise gibt es ja auch alte
+  // Seher und dann kann es gleiche fuer alle hier gemacht werden. (Ob der
+  // Code jemals rauskann?)
+  //TODO: Irgendwann alle Seher korrigieren und Code nach Merlin schieben...
+  if (IS_SEER(ME))
+    SetProp(P_CAN_FLAGS,QueryProp(P_CAN_FLAGS) | CAN_REPORT_WIMPY_DIR);
+
+  // ggf. Invis-Eigenschaft aus dem Loginobjekt abrufen (Invislogin), koennte
+  // ja anders als aus Savefile sein. Gesetztes P_INVIS aus diesem aber
+  // beibehalten.
+  if (IS_LEARNER(ME) && !QueryProp(P_INVIS)
+//      && load_name(previous_object()) == "/secure/login"
+      )
+  {
+    SetProp(P_INVIS, previous_object()->query_invis());
+    if (QueryProp(P_INVIS))
+      tell_object(ME, "DU BIST UNSICHTBAR!\n" );
+  }
+  "*"::updates_after_restore(newflag);
+
+  attributes::UpdateAttributes();
+
+  int size=Query(P_SIZE);
+  if (!size) size=RandomSize();
+  while(size==QueryProp(P_AVERAGE_SIZE))
+    size=RandomSize();
+  Set(P_SIZE,size);
+
+  // Prop wird nicht mehr genutzt. TODO: irgendwann entfernen.
+  Set(P_SECOND_LIST, SAVE, F_MODE_AD);
+  Set(P_SECOND_LIST, 0, F_VALUE);
+}
+
+
+/** Setzt den HC-Modus.
+  */
+varargs nomask void set_hc_play(string str,int val)
+{
+   string str1;
+
+    str1 = explode( object_name(previous_object()), "#" )[0];
+    
+    if ( str1 != "/secure/login" &&
+         previous_object()!=this_object() &&
+         extern_call() &&
+         (!geteuid(ME) || geteuid(ME) != getuid(ME) ||
+          capitalize(geteuid(ME)) != str ||
+          geteuid(ME) != geteuid(previous_object())) ){
+        write( "DIESER VERSUCH WAR ILLEGAL !!\n" );
+        return;
+    }
+    
+    hc_play=val;
+}
+
+/** gibt den HC-Modus zurueck.
+  */
+nomask int query_hc_play()
+{
+  return hc_play;
+}
+
+/** Initialisiert und aktiviert das Spielerobjekt.
+  * Kann nur von /secure/login oder /secure/master gerufen werden.
+  * Startet Telnet Negotiation, laedt Savefile, setzt einige Props. 
+  * Ruft updates_after_restore(), um div. Daten zu aktualisieren/reparieren.
+  * Ruft create() aus /std/player/potion.c.
+  * Bei neuem Spieler wird der entsprechende Event ausgeloest die Attribute
+  * auf die Startwerte gesetzt.
+  * Prueft Zweitiemarkierungen.
+  * Ruft InitPlayer().
+  * @param[in] str Name des Charakters, der geladen werden soll.
+  * @param[in] ip textuelle Repraesentation der IP-Adresse des Spielers.
+  * @return 1 bei Erfolg, 0 sonst.
+  * @todo Div. Reparaturen/Updates nach updates_after_restore() auslagern.
+  */
+varargs nomask int start_player( string str, string ip )
+{
+    mixed second;
+    int newflag;  /* could player be restored? */
+    string str1;
+
+    call_out( "disconnect", 600 );
+
+    str1 = explode( object_name(previous_object()), "#" )[0];
+
+    if ( str1 != "/secure/login" &&
+         str1 != "/secure/master" &&
+         (!geteuid(ME) || geteuid(ME) != getuid(ME) ||
+          capitalize(geteuid(ME)) != str ||
+          geteuid(ME) != geteuid(previous_object())) ){
+        write( "DIESER VERSUCH WAR ILLEGAL !!\n" );
+        destruct(ME);
+        return 0;
+    }
+
+    /* try to restore player. If it doesn't exist, set the new flag */
+    newflag = !restore_object( "/" + SAVEPATH + lower_case(str)[0..0] + "/"
+                               +lower_case(str) );
+
+    updates_after_restore(newflag);
+
+   if ( query_once_interactive(ME) )
+    {
+      // Telnet-Negotiations durchfuehren, aber nur die grundlegenden aus
+      // telnetneg. Alle anderen sollten erst spaeter, nach vollstaendiger
+      // Initialisierung gemacht werden.
+        telnetneg::startup_telnet_negs();
+        modify_prompt();
+        Set( P_LAST_LOGIN, time() );
+    }
+
+    Set( P_WANTS_TO_LEARN, 1 ); // 1 sollte der default sein !!!
+    Set( P_WANTS_TO_LEARN, PROTECTED, F_MODE_AS );
+    // Eingefuegt 18.11.99, kann nach einem Jahr wieder raus:
+    Set( P_TESTPLAYER, PROTECTED, F_MODE_AS );
+
+    if ( IS_LEARNER(ME) )
+        SetProp( P_CAN_FLAGS, QueryProp(P_CAN_FLAGS)|CAN_REMOTE );
+
+    Set( P_NAME, str );
+    Set( P_NAME, SECURED, F_MODE_AS );
+
+    if ( !QueryProp(P_NEEDED_QP) )
+        SetProp( P_NEEDED_QP, REQ_QP );
+
+    Set( P_NEEDED_QP, NOSETMETHOD, F_SET_METHOD );
+    Set( P_NEEDED_QP, SAVE|SECURED, F_MODE_AS );
+
+    /* autosave the player after 500 heartbeats */
+    time_to_save = age + 500;
+    potion::create(); /* DO IT HERE AFTER THE RESTORE !! */
+
+    AddId( getuid() );
+    SetProp( P_AC, 0 );
+    SetProp( P_WEAPON, 0 );
+
+    /* Set some things which wont be set when all is OK */
+    SetProp( P_MAX_HP, (QueryAttribute(A_CON) * 8 + 42 ));
+    SetProp( P_MAX_SP, (QueryAttribute(A_INT) * 8 + 42 ));
+
+    catch( bb = "/secure/bbmaster"->query_bb() );
+
+    /* If this is a new character, we call the adventurers guild to get
+     * our first title !
+     */
+    if ( newflag ) {
+        if ( QueryGuest())
+            SetProp( P_TITLE, "ueberkommt das "MUDNAME" ..." );
+
+        Set( P_LEVEL, -1 );
+        SetProp( P_ATTRIBUTES, ([ A_STR:1, A_CON:1, A_INT:1, A_DEX:1 ]) );
+        SetProp( P_HP, QueryProp(P_MAX_HP) );
+
+        // Event ausloesen
+        EVENTD->TriggerEvent(EVT_LIB_PLAYER_CREATION, ([
+              E_OBJECT: ME,
+              E_PLNAME: getuid(ME) ]) );
+    }
+    
+    InitPlayer();
+
+    // Padreic 01.02.1999
+    if ( !IS_LEARNER(ME) && second = QueryProp(P_SECOND) ) {
+        if ( stringp(second) && lower_case(second)[0..3] == "von " ) {
+            second = lower_case(second[4..]);
+            SetProp( P_SECOND, second );
+        }
+
+        if ( !stringp(second ) ||
+             file_size( "/save/" + second[0..0] + "/" + second + ".o" ) <= 0 ){
+            if ( stringp(second) &&
+                 file_size( "/save/" + lower_case(second[0..0]) + "/" +
+                            lower_case(second) + ".o" ) >0 ){
+                SetProp( P_SECOND, lower_case(second) );
+                log_file( "WRONG_SECOND",
+                          sprintf( "%s: %s: P_SECOND = %O -> Automatisch "
+                                   "korrigiert,\n",
+                                   dtime(time()), object_name(), second ) );
+            }
+            else {
+                tell_object( ME,
+                             "*\n*\n* Deine Zweitiemarkierung ist ungueltig, "
+                             "bitte aendere diese und sprich im\n* Zweifel "
+                             "bitte einen Erzmagier an.\n*\n*\n" );
+
+                log_file( "WRONG_SECOND",
+                          sprintf( "%s: %s: P_SECOND = %O\n",
+                                   dtime(time()), object_name(), second ) );
+                // ein bisschen deutlicher auffordern.. Padreic 08.04.1999
+                move( "/d/gebirge/room/zwafflad", M_GO );
+            }
+        }
+    }
+    return(0);
+}
+
+/** Letzte Phase der Spielerinitialisierung beim Laden des Charakters.
+  * Ruft enable_commands(), aktiviert den Heartbeat und aktiviert die
+  * Kommandos aus den geerbten command.c, put_and_get.c, team.c, soul.c,
+  * guide.c, setzt den Living Name.
+  * Registriert UseSpell() als Catchall-Kommando.
+  * Laesst den Spieler ggf. einen Level per /std/gilde aufsteigen, ruft
+  * call_notify_player_change(), loest Login-Event aus.
+  * Gibt Willkommenstexte, News und neue Mails aus.
+  * Findet den Startraum und bewegt den Spieler dorthin.
+  * Ruft FinalSetup() (aus den Rassenshells).
+  * Begrenzt Geldmenge im Spieler (wegen Bankzweities) nach Tragkraft und
+  * erstattet Geld bei Reboot.
+  * Ruft set_is_wizard().
+  * Startet Clonen der Autoloader.
+  * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
+  */
+private void InitPlayer4()
+{
+    int num, str, ski;
+    string err, called_from_ip;
+    mixed start_place;
+    object mon;
+
+    enable_commands();
+    set_heart_beat(1);
+    command::initialize();
+    add_put_and_get_commands();
+    add_team_commands();
+    add_soul_commands();
+    add_guide_commands();
+    add_action( "UseSpell", "", 1 );
+    set_living_name( getuid() );
+    while ( remove_call_out("disconnect") != -1 )
+        ;
+
+    if ( QueryProp(P_LEVEL) == -1 )
+    {
+        catch( "/std/gilde"->try_player_advance(this_object()) ;publish );
+    }
+
+    if ( interactive(ME) )
+        call_notify_player_change(1);
+
+    if ( interactive(this_object()) ) {
+        cat( "/etc/NEWS" );
+
+        NewbieIntroMsg();
+
+        if ( QueryProp(P_INVIS) && !IS_WIZARD(ME) )
+            SetProp( P_INVIS, 0 );
+
+        catch( num = "secure/mailer"->FingerMail(getuid());publish );
+
+        if ( num )
+            write( "Du hast " + num + " neue" +
+                   (num == 1 ? "n Brief" : " Briefe")+" im Postamt liegen.\n" );
+
+        catch( RegisterChannels();publish );
+
+        if ( (called_from_ip = Query(P_CALLED_FROM_IP)) &&
+             query_ip_number(ME) != called_from_ip ){
+            string tmp;
+
+            if ( stringp(tmp = query_ip_name(called_from_ip)) &&
+                 tmp != called_from_ip )
+                tmp = " [" + tmp + "]";
+            else
+                tmp = "";
+
+            write( "Das letzte Mal kamst Du von " + called_from_ip
+                   + tmp + ".\n" );
+        }
+
+        Set( P_CALLED_FROM_IP, query_ip_number(ME) );
+    }
+
+    if ( !stringp(default_home) || default_home == "" )
+        default_home = "/gilden/abenteurer";
+
+    if ( IS_SEER(ME) && !IS_LEARNER(ME) )
+        catch( start_place = HAUSVERWALTER->FindeHaus(getuid(ME));publish );
+    // wenn der Spieler noch ganz frisch ist und noch wenig  Stufenpunkte
+    // gekriegt hat und das Tutorial noch nicht gemacht hat, startet er im
+    // Tutorial.
+    else if (QueryProp(P_LEP) <= 120
+             && QM->HasMiniQuest(this_object(),
+               "/d/anfaenger/ennox/tutorial/npcs/verkaeufer") != 1)
+        start_place = "/room/welcome/"+ getuid(this_object());
+    else
+        start_place = 0;
+
+    if ( !start_place )
+        start_place = QueryProp(P_START_HOME);
+
+    if( objectp(start_place) || (stringp(start_place) && start_place != "" ) ){
+        if ((err = catch(move( start_place, M_GO|M_SILENT|M_NO_SHOW );publish)) 
+            || !environment() )
+            err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
+    }
+    else
+        err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
+
+    if ( err )
+        catch(move( "/gilden/abenteurer", M_GO|M_SILENT|M_NO_SHOW );publish);
+
+    // Die Shell muss FinalSetup() nicht implementieren. Daher Callother
+    catch( ME->FinalSetup();publish );
+
+    // Login-event ausloesen
+    EVENTD->TriggerEvent(EVT_LIB_LOGIN, ([
+          E_OBJECT: ME,
+          E_PLNAME: getuid(ME),
+          E_ENVIRONMENT: environment() ]) );
+
+    // erst jetzt GMCP freigeben und zu verhandeln.
+    gmcp::startup_telnet_negs();
+
+    // Schonmal sichern, falls ein Bug (Evalcost...) dazwischen kommen sollte.
+    autoload_rest = autoload;
+
+    // Um Geld-Xties mit illegal viel Geld zu vermeiden, kommt ein Check:
+    if ( !IS_LEARNER(ME) && !Query(P_TESTPLAYER) &&
+         mon = present( "\ngeld", ME ) ){
+        // maximale Kraft, die der Spieler haette haben koennen
+        str = QueryAttribute(A_STR) + 4;
+        if ( Query(P_FROG) )
+            str += 30;
+        if ( str < 1 )
+            str = QueryRealAttribute(A_STR) + 4;
+        if ( str > 30 )
+            str = 30;
+
+        // Trageskill beachten
+        ski = to_int(UseSkill( SK_CARRY, ([SI_SKILLARG : str ]) ));
+        if ( !intp(ski) )
+            ski = 0;
+
+        // Wieviel konnte der Spieler insgesamt maximal tragen?
+        num = 9200 + str * 800 + ski;
+        if ( num < 3000 )
+            num = 3000;
+
+        // Verdoppeln fuer einen guten Container und nochmal 20% draufschlagen
+        // zur Sicherheit. Das Ganze danach *4, um die maximale Anzahl Muenzen
+        // zu erhalten.
+        num = (int) (num * 8.8);
+
+        // Geld, das zuviel ist, auf den Maximalwert kuerzen.
+        // Zur Sicherheit wird mitgeloggt. Falls ein Spieler sich (zu recht)
+        // beschwert, kann man immer noch wieder die Summe korrigieren ;-)
+        if ( (str = mon->QueryProp(P_AMOUNT)) > num ){
+            mon->SetProp( P_AMOUNT, num );
+            log_file( "ZUVIEL_GELD", sprintf( "%s: %s hatte %d Muenzen bei "
+                                              "sich. Korrigiert auf %d.\n ",
+                                              ctime(time())[4..],
+                                              capitalize(getuid(ME)),
+                                              str, num ) );
+        }
+    }
+    int entschaedigung = QueryProp(P_CARRIED_VALUE);
+    if ( entschaedigung > 0 )
+    {
+        write( "Du findest " + entschaedigung +
+               " Muenzen, die Du beim letzten Mal verloren hast.\n" );
+
+        if ( MayAddWeight( entschaedigung / 4 ) ){
+            write( "Weil Du nicht mehr soviel tragen kannst, spendest Du den "+
+                   "Rest der Zentralbank.\n" );
+
+            num = (QueryProp(P_MAX_WEIGHT) - query_weight_contents()) * 4;
+            entschaedigung = (num < 0) ? 0 : num;
+        }
+
+        AddMoney( entschaedigung );
+        SetProp(P_CARRIED_VALUE,0);
+    }
+
+    if ( !QueryProp(P_INVIS) )
+        say( capitalize(name(WER)) + " betritt diese Welt.\n" );
+    else
+        write( "DU BIST UNSICHTBAR!\n\n" );
+#if __EFUN_DEFINED__(set_is_wizard)
+    if ( IS_WIZARD(getuid(ME)) )
+        set_is_wizard( ME, 1 );
+    else
+        set_is_wizard( ME, 0 );
+#endif
+    if ( query_once_interactive(ME) )
+        ListAwaited();
+
+    // Autoloader werden ganz zum Schluss geclont, da das bis zum bitteren
+    // (Evalcost-)Ende geschieht und danach u.U. keine Rechenzeit fuer
+    // andere Aktionen mehr ueber ist
+    load_auto_objects( autoload );
+}
+
+/** Setzt die Spielerinitialisierung nach start_player() fort.
+  * Prueft den Wert der vom Spieler getragenen Sachen (fuer Entschaedigung
+  * nach Reboot).
+  * Setzt div. Properties des "Spielerkoerpers", die eigentlich gespeichert
+  * werden, nach einem Reboot zurueck.
+  * Fragt ggf. nach eMail-Adresse und uebergibt per input_to an
+  * InitPlayer2().
+  * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
+*/
+private void InitPlayer()
+{
+    string mailaddr;
+
+    // wenn es einen Crash gab, sollen Spieler nicht noch extra bestraft werden
+    if ( file_time( "/save/" + getuid()[0..0] + "/" + getuid() + ".o" )
+         < last_reboot_time() ){
+        SetProp( P_FOOD, 0 );
+        SetProp( P_DRINK, 0 );
+        SetProp( P_ALCOHOL, 0 );
+        SetProp( P_BLIND, 0 );
+        SetProp( P_DEAF, 0 );
+        SetProp( P_POISON, 0 );
+        SetProp( P_GHOST, 0 );
+        SetProp( P_FROG, 0 );
+        SetProp( P_HP, QueryProp(P_MAX_HP) );
+        SetProp( P_SP, QueryProp(P_MAX_SP) );
+    }
+
+    if ( QueryGuest() )
+        Set( P_MAILADDR, "none" );
+    else if ( !(mailaddr = Query(P_MAILADDR)) || mailaddr == "" ) {
+        write(break_string(
+          "Eine gueltige EMail-Adresse erleichtert es erheblich, Dir "
+          "ein neues Passwort setzen zu lassen, falls Du einmal Dein "
+          "Passwort vergisst.",78)); 
+        input_to( "getmailaddr",INPUT_PROMPT,
+            "Gib bitte Deine EMail-Adresse an: " );
+        return;
+    }
+    InitPlayer2();
+}
+
+/** liest eMail-Adresse vom Spieler ein und speichert sie.
+  * Uebergibt anschliessend an InitPlayer2()
+  * @param[in] maddr Spielereingabe der Emailadresse.
+  * @sa InitPlayer2().
+  */
+static void getmailaddr( string maddr )
+{
+    maddr = check_email(maddr);
+
+    if ( !stringp(maddr)) {
+      write("Deine Eingabe scheint keine gueltige EMail-Adresse gewesen "
+          "zu sein.\n");
+      input_to( "getmailaddr", INPUT_PROMPT,
+          "Gib bitte Deine EMail-Adresse an: " );
+      return;
+    }
+    Set( P_MAILADDR, maddr );
+    InitPlayer2();
+}
+
+
+/** Prueft Geschlecht des Spielers und fragt ggf. beim Spieler nach.
+  * Uebergibt an InitPlayer3() oder get_gender().
+  * @sa InitPlayer3(), get_gender().
+  */
+private void InitPlayer2()
+{
+    if( member(({ MALE, FEMALE }), QueryProp(P_GENDER) ) == -1 ) {
+        input_to( "getgender", INPUT_PROMPT,
+            "Bist Du maennlich oder weiblich: ");
+        return;
+    }
+
+    InitPlayer3();
+}
+
+/** Liest Spielerantwort auf die Frage nach dem Geschlecht des Chars ein.
+  * Wird gerufen von input_to().
+  * Uebergibt an InitPlayer3().
+  * @sa InitPlayer3().
+  */
+static void getgender( string gender_string )
+{
+    gender_string = lower_case( gender_string );
+
+    if ( sizeof(gender_string)==1 && gender_string[0] == 'm' ){
+        write( "Willkommen, mein Herr!\n" );
+        SetProp( P_GENDER, MALE );
+    }
+    else if ( sizeof(gender_string)==1 && gender_string[0] == 'w' ){    
+        write( "Willkommen, gnae' Frau!\n" );
+        SetProp( P_GENDER, FEMALE );    
+    }    
+    else {
+        write( "Wie? Was? Verstehe ich nicht!\n" );    
+        input_to( "getgender", INPUT_PROMPT,
+            "Bist Du maennlich oder weiblich? (tippe m oder w): ");    
+        return;
+    }
+
+    InitPlayer3();
+}
+
+
+/** Prueft Terminaltyp des Spielers und fragt ggf. beim Spieler nach.
+  * Uebergibt an InitPlayer4() oder gettty().
+  * @sa InitPlayer4(), gettty().
+  */
+private void InitPlayer3()
+{
+    if ( !QueryProp(P_TTY) || QueryProp(P_TTY) == "none" )
+    {
+        write( "Waehle einen Terminaltyp (kann spaeter mit <stty> geaendert "
+               "werden)\n");
+        input_to( "gettty", INPUT_PROMPT, "vt100, ansi, dumb (Standard: dumb): " );
+        return;
+    }
+    InitPlayer4();
+}
+
+/** Liest Spielerantwort auf die Frage nach dem Terminaltyp ein.
+  * Wird gerufen von input_to().
+  * Uebergibt an InitPlayer4().
+  * @sa InitPlayer4().
+  */
+static void gettty( string ttystr )
+{
+    if ( !ttystr || ttystr == "" )
+        ttystr = "dumb";
+
+    ttystr = lower_case(ttystr);
+
+    if ( ttystr == "vt100" ){
+        write( "Dies sollte " + ANSI_BOLD + "fett" + ANSI_NORMAL + " sein.\n" );
+        SetProp( P_TTY, ttystr );
+    }
+    else
+        if ( ttystr == "ansi" ){
+            write( "Dies sollte " + ANSI_RED + "rot" + ANSI_NORMAL +
+                   " sein.\n" );
+            SetProp( P_TTY, "ansi" );
+        }
+        else if ( ttystr == "dumb" ){
+            write( "Ohje, oede! Besorg Dir ein besseres Terminal!\n" );
+            SetProp( P_TTY, "dumb" );
+        }
+        else {
+            write( "Dieser Terminaltyp wird nicht unterstuetzt. Nimm bitte "
+                   "einen aus:\nvt100, ansi or dumb (Standard ist dumb).\n" );
+            input_to( "gettty", INPUT_PROMPT,
+                "vt100, ansi or dumb (Standard ist dumb): ");
+            return;
+        }
+
+    InitPlayer4();
+}
+
+
+/** Liefert die UID des Charakters zurueck, also den Charakternamen.
+  * @return UID des Objekts (Charaktername).
+  */
+nomask string query_real_name() {
+  /* ACHTUNG !! DIES LFUN DARF NICHT ENTFERNT WERDEN !!! */
+  /* Sie wird vom Gamedriver (zB bei F_ED) aufgerufen !! */
+  /* Ich bin da zwar nicht so ueberzeugt von, dass der Driver die heutzutage
+   * noch ruft, aber die halbe Mudlib ruft sie. ;-) (Zesstra, 27.4.08)
+   */
+  return getuid();
+}
+
+/*
+ * the wizard command review: show player moving messages
+ */
+int review() {
+  string *msg;
+  write(short());
+  write("Deine Bewegungen werden wie folgt gemeldet:\n"+
+        "mout:  "+name(WER)+" "+(msg=explode(QueryProp(P_MSGOUT),"#"))[0]
+       +" <Richtung>"+(sizeof(msg)>1 ? msg[1] : "")+".\n"+
+        "min:   "+name(WER)+" "+QueryProp(P_MSGIN)+".\n"+
+        "mmout: "+name(WER)+" "+QueryProp(P_MMSGOUT)+".\n"+
+        "mmin:  "+name(WER)+" "+QueryProp(P_MMSGIN)+".\n"+
+        (IS_LEARNER(ME) ?
+         "cmsg:  "+name(WER)+" "+QueryProp(P_CLONE_MSG)+".\n"+
+         "dmsg:  <Irgendetwas> "+QueryProp(P_DESTRUCT_MSG)+".\n"
+         : "")+
+        "Wenn Du jemanden angreifst, sieht das so aus:\n"+
+        name(WER)+" greift Dich"+QueryProp(P_HANDS)[0]+" an.\n");
+  return 1;
+}
+
+/*
+ * set player moving messages
+ */
+
+static int setmin(string str)
+{
+  SetProp(P_MSGIN, _unparsed_args()||"kommt an");
+  write("Ok.\n");
+  return 1;
+}
+
+static int setmout(string str)
+{
+  string *msg;
+
+  if(sizeof(msg=explode((_unparsed_args()||"geht"),"#"))>2)
+  {
+    write("Du darfst nur einmal '#' fuer die Richtung angeben.\n");
+    return 1;
+  }
+  if(sizeof(msg)>1)
+  {
+    if (msg[0]!="" && msg[0][<1]==' ') msg[0]=msg[0][0..<2];
+    SetProp(P_MSGOUT, msg[0]+"#"+msg[1]);
+  }
+  else
+    SetProp(P_MSGOUT, _unparsed_args()||"geht");
+  write("Ok.\n");
+  return 1;
+}
+
+static int setmmin(string str)
+{
+  SetProp(P_MMSGIN, _unparsed_args()||"erscheint");
+  write("Ok.\n");
+  return 1;
+}
+
+static int setmmout(string str)
+{
+  SetProp(P_MMSGOUT, _unparsed_args()||"verschwindet");
+  write("Ok.\n");
+  return 1;
+}
+
+static int setcmsg(string str)
+{
+  SetProp(P_CLONE_MSG, _unparsed_args()||"zaubert etwas aus "
+          + QueryPossPronoun(MALE,WEM) + " Aermel hervor");
+  write("Ok.\n");
+  return 1;
+}
+
+static int setdmsg(string str)
+{
+  SetProp(P_DESTRUCT_MSG, _unparsed_args()||"wird von " + name(WER,1)
+          + " zerstaeubt");
+  write("Ok.\n");
+  return 1;
+}
+
+static int set_title(string str)
+{
+  string bonus;
+  
+  if(!IS_SEER(this_object()) && !FAO_HAS_TITLE_GIFT(this_object()) )
+  {
+    return 0;
+  }
+  
+  bonus = "";
+  if (!(str=_unparsed_args()))
+    str = "";
+  else if( str[0..2]=="\\b," || str[0..2]=="\\b'" )
+  {
+    bonus = "\b"; // ein backspace fuer ein (hoch)komma ist ok! :-)
+    str = str[2..];
+  }
+  if(str=="0")  // damit der Gildentitel zum Vorschein kommen kann
+    SetProp(P_TITLE, 0);
+  else
+    SetProp(P_TITLE, bonus+str);
+  write("Ok.\n");
+  return 1;
+}
+
+static int extra_input(string str, string look)
+{
+  if (str=="**")
+  {
+    if (look=="")
+      SetProp(P_EXTRA_LOOK,0);
+    else
+      SetProp(P_EXTRA_LOOK,look);
+    write("Ok.\n");
+    return 1;
+  }
+  input_to("extra_input",INPUT_PROMPT, "]" ,look+str+"\n");
+  return 1;
+}
+
+static int extralook(mixed str)
+{
+  if( str=="?" )
+  {
+    write( "Dein Extralook ist : "+QueryProp(P_EXTRA_LOOK) + "\n");
+    return 1;
+  }
+  write("Bitte gib Deinen Extra-Look ein. Beenden mit **:\n");
+  input_to("extra_input",INPUT_PROMPT,"]","");
+  return 1;
+}
+
+static void calculate_value()
+{
+  int i, carried_value, value;
+
+  carried_value=0;
+  foreach(object ob: deep_inventory(ME)) {
+    if (!ob->QueryProp(P_AUTOLOADOBJ))
+      carried_value+=((value=(int)ob->QueryProp(P_VALUE)) > 1000 ? 1000 : value);
+  }
+  SetProp(P_CARRIED_VALUE, carried_value);
+}
+
+/* Called by command 'save' */
+int save_character() {
+  save_me(1);
+  write("Ok.\n");
+  return 1;
+}
+
+void save_me(mixed value_items)
+{
+  // Gaeste werden nicht gespeichert
+  if( getuid()[0..3]=="gast" )
+    return;
+ 
+  // Autoloader identifizieren und speichern
+  autoload=([]);
+  foreach(object ob: deep_inventory(ME)) {
+    int val = ob->QueryProp( P_AUTOLOADOBJ );
+    if (val && clonep(ob))
+    {
+      string obname=load_name(ob);
+      if (obname == GELD)
+        autoload[obname] += val;
+      else
+        autoload += ([obname:val]);
+    }
+  }
+  // An noch nicht geclonte Autoloader denken!
+  autoload += autoload_rest;
+  
+  // Im Bedarfsfall Wert des Inventory bestimmen
+  if (value_items)
+    calculate_value();
+  else
+    SetProp(P_CARRIED_VALUE, 0);
+
+  // Logout-Zeit speichern
+  if(query_once_interactive(ME) && !QueryProp(P_INVIS))
+    Set(P_LAST_LOGOUT,time());
+
+  // Funktion zur Bestimmung des Gildenrating aufrufen
+  string gilde=GUILD_DIR+QueryProp(P_GUILD);
+  if (find_object(gilde) || file_size(gilde+".c")>-1)
+    catch(call_other(gilde,"GuildRating",this_object());publish);
+
+  // Speichern des Spielers
+  save_object("/"+SAVEPATH+getuid()[0..0]+"/" + getuid());
+}
+
+static varargs void log_autoload( string file, string reason, mixed data, string error )
+{
+  if (member(autoload_error,file)!=-1) return;
+  log_file(SHELLLOG("NO_AUTO_FILE"),sprintf("%s: %s: %s\nreason: cannot %s file\ndata: %O\n%s\n",
+           ctime()[4..15],capitalize(getuid()),file,reason,data,
+           (error?"Fehlermeldung: "+error+"\n":"")));
+  autoload_error+=({file});
+}
+
+// tics, die fuer autoloader reichen sollten:
+#define SAFE_FOR_AUTOLOADER __MAX_EVAL_COST__/4
+
+private void load_auto_object( string file, mixed data )
+{
+    object ob;
+    string error;
+   
+    if( get_eval_cost() < SAFE_FOR_AUTOLOADER ) return;
+    m_delete( autoload_rest, file );
+    autoload_error-=({file});
+
+    if ( file == "/obj/money" )
+      file = "/items/money";
+    if ( file == "/obj/seercard" )
+      file = "/items/seercard";
+    
+    ob = find_object(file);
+    
+    if (!ob)
+    {
+        if (file_size(file+".c")<0&&
+           file_size(implode(explode(file,"/")[0..<2],"/")+
+                     "/virtual_compiler.c")<0)
+        {
+           log_autoload(file,"find",data,0);
+           return;
+        }
+        if (error = catch(load_object(file); publish))
+        {
+           log_autoload(file,"load",data,error);
+           return;
+        }
+    }
+    if ( error = catch(ob = clone_object(file); publish) )
+    {
+        log_autoload(file,"clone",data, error);
+        return;
+    }
+
+    if ( error = catch(ob->SetProp( P_AUTOLOADOBJ, data ); publish) )
+    {
+        log_autoload(file,"SetProp",data, error);
+        ob->remove(1);
+        if (ob) destruct(ob);
+        return;
+    }
+
+    if ( error = catch(ob->move( ME, M_NOCHECK );publish) ) {
+        log_autoload(file,"move",data, error);
+        ob->remove(1);
+        if(ob) destruct(ob);
+        return;
+    }
+}
+
+static void load_auto_objects( mapping map_ldfied )
+{
+    if ( (!mappingp(map_ldfied) || !sizeof(map_ldfied)) && !sizeof(autoload_rest) )
+        return;
+
+    // Mit Netztoten Spielern rechnen manche Autoloader nicht. Also
+    // das Clonen unterbrechen und in Reconnect() wieder anwerfen.
+    if ( !interactive() )
+        return;
+
+    // Kleiner Hack: autoload_rest ist eine globale Variable, die beim
+    // Clonen der einzelnen Autoloader direkt veraendert wird.
+    // So lange das Mapping noch Eintraege hat, muessen wir noch fehlende
+    // Autoloader clonen.
+    if ( !sizeof(autoload_rest) )
+        autoload_rest = map_ldfied;
+
+    // Schon hier einen call_out() zum "Nach"clonen von noch nicht geclonten
+    // Autoloadern starten, da spaeter u.U. keine Rechenzeit mehr dafuer da ist.
+    while ( remove_call_out("load_auto_objects") != -1 )
+        /* do nothing */;
+    
+    // Mit Parameter '0' aufrufen, da das globale Mapping benutzt wird.
+    // Verzoegerung 0 in rekursiven Callouts ist bloed, also 1s Delay
+    call_out( "load_auto_objects", 2, 0 );
+    
+    // Mit catch() gegen die Evalcost-Falle!
+    // Mit Absicht das walk_mapping() aus der "alten" Version erhalten und
+    // nicht durch eine (einfachere) Schleife inkl. get_eval_cost() ersetzt,
+    // da eine Schleife gegenueber der walk_mapping()-Loesung den Aufbau
+    // der previous_object()-Kette veraendern wuerde; darauf testen aber
+    // manche Objekte.
+    catch( walk_mapping( autoload_rest, #'load_auto_object/*'*/ ) );
+}
+
+/*
+ * al_to_title: Make the numeric alignment value into a string
+ */
+static string al_to_title(int a)
+{
+  if (a >= KILL_NEUTRAL_ALIGNMENT * 100)
+    return "heilig";
+  if (a > KILL_NEUTRAL_ALIGNMENT * 20)
+    return "gut";
+  if (a > KILL_NEUTRAL_ALIGNMENT * 4)
+    return "nett";
+  if (a > - KILL_NEUTRAL_ALIGNMENT * 4)
+    return "neutral";
+  if (a > - KILL_NEUTRAL_ALIGNMENT * 20)
+    return "frech";
+  if (a > - KILL_NEUTRAL_ALIGNMENT * 100)
+    return "boese";
+  return "satanisch";
+}
+
+static int toggle_whimpy_dir(string str) {
+  SetProp(P_WIMPY_DIRECTION,str=_unparsed_args()||str);
+  if (str)
+    printf("Ok, Fluchtrichtung %O.\n",str);
+  else
+    printf("Ok, bevorzugte Fluchtrichtung deaktiviert.\n");
+  return 1;
+}
+
+static int toggle_whimpy(string str)
+{
+  int i;
+
+  if(!str || str=="" || (sscanf(str,"%d",i)<0))
+  {
+    write("vorsicht <hp>, 0<=hp<"+QueryProp(P_MAX_HP)+"\n");
+    return 1;
+  }
+  if(i>=QueryProp(P_MAX_HP) || i<0)
+  {
+    write("Der Wert ist nicht erlaubt.\n");
+    return 1;
+  }
+  if(!i) write("Prinz Eisenherz-Modus.\n");
+  else write("Vorsicht-Modus ("+i+")\n");
+  SetProp(P_WIMPY,i);
+  return 1;
+}
+
+/** Bestimmt, ob das Spielerobjekt beschattet werden darf.
+  * Beschatten ist nur fuer Objekte erlaubt, die in /std/player/shadows
+  * abgelegt sind.
+  * Ausnahme: Testspieler mit gesetztem P_ALLOWED_SHADOW
+  * @param[in] obj Objekt, was beschatten moechte.
+  * @return 0, wenn Beschatten erlaubt, 1 sonst.
+  */
+varargs nomask int query_prevent_shadow(object obj)
+{
+  string what, allowed_shadow;
+  int    dummy;
+
+//  if ( Query(P_TESTPLAYER) )
+//      return 0;
+  
+  if (obj){
+    what=object_name(obj);
+    if (what && what != "" &&
+        sscanf(what,"/std/player/shadows/%s#%d",what,dummy)==2)
+      return 0;
+    
+    // Einem Testspieler kann man P_ALLOWED_SHADOW auf einen zu testenden
+    // Shadow setzen.
+    if ( Query(P_TESTPLAYER) && 
+         stringp(what) && 
+         stringp(allowed_shadow=Query(P_ALLOWED_SHADOW)) &&
+         strstr(what, allowed_shadow)==0)
+            return 0;
+  }
+  return 1;
+}
+
+static int uhrzeit()
+{
+  write(dtime(time()+QueryProp(P_TIMEZONE)*3600)+".\n");
+  return 1;
+}
+
+protected string SetDefaultHome(string str)
+{
+  return default_home=str;
+}
+
+string QueryDefaultHome()
+{
+  return default_home;
+}
+
+protected string SetDefaultPrayRoom(string str)
+{
+  if(hc_play>1)
+  {
+    default_pray_room="/room/nirvana";
+  }
+  else
+    default_pray_room=str;
+  
+  return default_pray_room;
+}
+
+string QueryPrayRoom()
+{
+  if(hc_play>1)
+  {
+    return "/room/nirvana";
+  } 
+  string room = QueryProp(P_PRAY_ROOM);
+  if (stringp(room))
+    return room;
+  else if (default_pray_room)
+    return default_pray_room;
+  // hoffentlich ist das wenigstens gesetzt. 
+  return default_home;
+}
+
+void _restart_beat()
+{
+  tell_object(ME,
+      "Der GameDriver teilt Dir mit: Dein Herzschlag hat wieder eingesetzt.\n");
+  set_heart_beat(1);
+}
+
+static int weg(string str)
+{
+  if (!(str=_unparsed_args()))
+  {
+    printf("Du bist nicht%s als abwesend gekennzeichnet.\n",
+           QueryProp(P_AWAY) ? " mehr" : "");
+    SetProp(P_AWAY, 0);
+    return 1;
+  }
+  write("Du bist jetzt als abwesend gekennzeichnet.\n");
+  SetProp(P_AWAY, str);
+  return 1;
+}
+
+/* Ein Befehl zum anschauen der Wegmeldung anderer Spieler */
+static int wegmeldung(string player)
+{
+
+  object player_ob;
+  string weg;
+
+  if ( !player || player=="" || 
+       player==lowerstring(this_player()->QueryProp(P_NAME)))
+  {
+    weg=this_player()->QueryProp(P_AWAY);
+    write ("Du bist "+(weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
+    if (weg)
+      write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
+    return 1;  
+  }
+
+  // Welcher Spieler ist gemeint?
+  player_ob=find_player(player);
+
+  // Spieler nicht da oder Invis und Anfrager is kein Magier
+  if (!player_ob || 
+      (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player()))) 
+  {
+    write(capitalize(player)+" ist gerade nicht im Spiel.\n");
+    return 1;
+  }
+
+  weg=player_ob->QueryProp(P_AWAY);
+
+        // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
+  write (player_ob->QueryProp(P_NAME)+" ist "+
+         (weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
+
+  if (weg)
+    write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
+
+  return 1;
+}
+
+static string timediff(int time)
+{
+  string ret;
+
+  ret="";
+  if(time>=86400) {
+    ret+=time/86400+"d ";
+    time%=86400;
+  }
+  if(time<36000) ret+="0";
+  ret+=time/3600+":";
+  time%=3600;
+  if(time<600) ret+="0";
+  ret+=time/60+":";
+  time%=60;
+  if(time<10) ret+="0";
+  ret+=time+"";
+  return ret;
+}
+
+
+/* Ein Befehl zum anschauen der Idlezeit anderer Spieler */
+static int idlezeit(string player)
+{
+
+  object player_ob;
+  int idle;
+
+  if ( !player || player=="" || 
+       player==lowerstring(this_player()->QueryProp(P_NAME)))
+  {
+    write ("Du bist selber natuerlich gerade nicht idle.\n");
+    return 1;  
+  }
+
+  // Welcher Spieler ist gemeint?
+  player_ob=find_player(player);
+
+  // Spieler nicht da oder Invis und Anfrager is kein Magier
+  if (!player_ob || 
+      (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player()))) 
+  {
+    write(capitalize(player)+" ist gerade nicht im Spiel.\n");
+    return 1;
+  }
+
+  idle=query_idle(player_ob);
+
+        // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
+  write (player_ob->QueryProp(P_NAME)+" ist "+
+         (idle>=60?timediff(idle):"nicht")+" passiv.\n");
+
+  return 1;
+}
+
+
+/** Belebt einen netztoten Spieler wieder.
+  * Reaktiviert Heartbeats, bewegt den Spieler zurueck an den Ort, der eim
+  * Einschlafen zum Aufwachen ermittelt wurde (im einfachsten Fall der Raum,
+  * wo man eingeschlafen ist).
+  */
+static void ndead_revive()
+{
+    string fname;
+    int ret;
+
+    set_heart_beat(1);
+    ndead_next_check = NETDEAD_CHECK_TIME;
+    ndead_currently = 0;
+    ndead_lasttime = 0;
+
+    if ( !objectp(ndead_location) && 
+         stringp(ndead_l_filename) && sizeof(ndead_l_filename)) {
+        
+        if ( member( ndead_l_filename, '#' ) == -1 ){
+            catch(load_object( ndead_l_filename); publish);
+            ndead_location = find_object(ndead_l_filename);
+        }
+        else {
+            ndead_location = find_object(ndead_l_filename);
+            if ( !ndead_location && env_ndead_info ){
+                fname = explode( ndead_l_filename, "#" )[0];
+                catch(ndead_location = 
+                    (load_object(fname)->SetProp(P_NETDEAD_INFO, env_ndead_info));
+                    publish);
+                if ( !objectp(ndead_location) ){
+                    catch(load_object( ndead_location);publish);
+                    ndead_location = find_object(ndead_location);
+                }
+            }
+        }
+    }
+
+    if ( !objectp(ndead_location)
+        || catch(ret = move( ndead_location, M_GO|M_SILENT );publish) 
+        || ret != 1 ) {
+        move( "gilden/abenteurer", M_GO|M_SILENT );
+        ndead_location = environment();
+    }
+
+    //  ndead_location=0;
+    ndead_l_filename = 0;
+    env_ndead_info = 0;
+}
+
+/** Bewegt einen netztoten Spieler in den Netztotenraum
+  * Gerufen von heartbeat().
+  * Zerstoert Gaeste, verlaesst ggf. das Team, ermittelt, ob der Spieler beim
+  * Aufwachen in das alte Environment bewegt werden soll oder in einen anderen
+  * Raum, hierzu wird im Environment P_NETDEAD_INFO abgefragt.
+  * Deaktiviert die Kommandos per disable_commands().
+  */
+static void ndead_move_me() {
+  object team;
+  mixed amem;
+
+  set_heart_beat(0);
+  stop_heart_beats();
+  if (QueryGuest()) {
+    quit();
+    if (ME)
+      remove();
+    if (ME)
+      destruct(ME);
+    return;
+  }
+  ndead_next_check=NETDEAD_CHECK_TIME;
+  ndead_currently=1;
+  ndead_lasttime=0;
+  ndead_location=environment();
+  if (objectp(ndead_location))
+    ndead_l_filename=object_name(ndead_location);
+  
+  if (objectp(environment())
+      && sizeof(explode(object_name(environment()),"#")) > 1)
+    env_ndead_info=environment()->QueryProp(P_NETDEAD_INFO);
+  else
+    env_ndead_info=0;
+  
+  if ( objectp(team=Query(P_TEAM)) )
+      // Der Test auf assoziierte Teammitglieder (== FolgeNPCs)
+      // verhindert, dass Spieler nach "schlafe ein" aus dem
+      // Team ausgetragen werden. -- 29.01.2002 Tiamak
+      //      && !objectp(amem=Query(P_TEAM_ASSOC_MEMBERS))
+      //      && !(pointerp(amem) && sizeof(amem)))
+    team->RemoveMember(ME);
+  
+  disable_commands();
+  move(NETDEAD_ROOM,M_GO|M_NO_ATTACK|M_NOCHECK,"ins Reich der Netztoten");
+}
+
+/** Ist dieser Character ein Gast?
+  * @return 1, wenn Gast, 0 sonst.
+  */
+int QueryGuest()
+{
+  string dummy;
+  return sscanf(getuid(),"gast%d",dummy);
+}
+
+/** Spielerkommando 'schlafe ein'.
+  * Ruft remove_interactive() bei Spielern, bei Magiern wird quit() gerufen,
+  * um das Magierobjekt zu zerstoeren.
+  * @sa quit()
+  */
+int disconnect(string str)
+{ 
+  string verb;
+  string desc = break_string(
+      "\"schlafe ein\" beendet Deine Verbindung mit "MUDNAME". Du behaeltst "
+     "Deine Sachen.\nFalls "MUDNAME" jedoch abstuerzt oder neu gestartet "
+     "wird, waehrend Du weg bist, verlierst Du die meisten allerdings "
+     "(genauso, als wenn Du Deine Verbindung mit \"ende\" beendet haettest). "
+     "In diesem Fall bekommst Du dann eine kleine Entschaedigung."
+     ,78,0,BS_LEAVE_MY_LFS);
+
+  verb=query_verb();
+  if (!verb)
+    verb="AUTOCALL";
+  if (verb[0..5]=="schlaf" && str!="ein")
+  {
+    notify_fail(desc);
+    return 0;
+  }
+  if (IS_LEARNER(this_object()))
+    return quit();
+  
+  tell_object(this_object(), desc);
+
+  if (clonep(environment()) && !environment()->QueryProp(P_NETDEAD_INFO))
+    tell_object(this_object(),break_string(
+        "\nACHTUNG: Wenn Du hier laengere Zeit schlaefst, "
+        "kommst Du vermutlich nicht an diesen Ort zurueck!",78));
+
+  say(capitalize(name(WER))+" hat gerade die Verbindung zu "MUDNAME" gekappt.\n");
+  remove_interactive(ME);
+  call_out(#'clear_tell_history,4);
+  return 1;
+}
+
+static int finger (string str)
+{
+  string ret;
+  mixed xname;
+
+  if (!str || str==""
+      || sizeof(explode(str," ")-({"-n","-p","-s","-v","-a"}))>1)
+  {
+    write("finger <spielername> oder finger <spielername@mudname>\n"+
+      "Bitte nur den reinen Spielernamen verwenden, keine Namensvorsaetze oder Titel\n");
+    return 1;
+  }
+  xname=explode(str,"@");
+  if(sizeof(xname)==2)
+  {
+    if (xname[0]=="-n " || xname[0]=="-p " || xname[0]=="-s ")  {
+      write("finger <spielername>@<mudname> - der Spielername fehlt.\n");
+      return 1;
+    }
+    if (ret=INETD->_send_udp(xname[1],([
+                   REQUEST: "finger",
+                   SENDER: getuid(ME),
+                   DATA: (explode(xname[0]," ")-({"-n","-p","-s"}))[0]
+                   ]), 1))
+            write(ret);
+        else
+      write("Anfrage abgeschickt.\n");
+    return 1;
+  }
+  "/p/daemon/finger"->finger_single(str,1);
+  return 1;
+}
+
+string lalign(string str, int wid)
+{
+  return (str+"                                                   "+
+      "                                                   ")[0..wid-1];
+}
+
+#define MUDS_BAR "\
+-------------------------------------------------------------------------------"
+
+private void format(mixed mud, mixed hosts, string output)
+{
+  output += lalign(hosts[mud][HOST_NAME], 20) + "  " +
+        (hosts[mud][HOST_STATUS] ?
+           hosts[mud][HOST_STATUS] > 0 ?
+             "UP       " + ctime(hosts[mud][HOST_STATUS])[4..15] :
+             "DOWN     " + ctime(-hosts[mud][HOST_STATUS])[4..15]
+         : "UNKNOWN  Never accessed.") + "\n";
+}
+
+static int muds() {
+  mapping hosts;
+  int i;
+  mixed muds, output;
+
+  output = lalign("Mudname", 20) + "  Status   Last access";
+  output += "\n" + MUDS_BAR[0..sizeof(output)] + "\n";
+  muds = sort_array(m_indices(hosts = INETD->query("hosts")),#'>);
+  map(muds, #'format, hosts, &output);
+  More(output);
+  return 1;
+}
+
+// **** local property methods
+static int _set_level(int i)
+{
+  if (!intp(i)) return -1;
+  if (i<1) return -1;
+  Set(P_LEVEL, i);
+  GMCP_Char( ([P_LEVEL: i]) );
+  return i;
+}
+
+static int _set_invis(int a)
+{
+  return Set(P_INVIS, intp(a) ? a : !Query(P_INVIS));
+}
+
+/* sets the terminal type */
+/* note: support vt100 (b/w), ansi (color), dumb (none) */
+static string _set_tty(string str) {
+  if(str != "dumb" && str != "vt100" && str != "ansi")
+    return Query(P_TTY);
+  return Set(P_TTY, str);
+}
+
+static int stty(string str)
+{
+  if(str!="dumb"&&str!="vt100"&&str!="ansi"&&str!="reset")
+  {
+    write("Kommando: stty dumb|vt100|ansi oder reset\n");
+  }
+  if(str == "reset") {
+      printf("Dieser Text sollte lesbar sein!\n");
+      return 1;
+  }
+
+  write("TTY steht jetzt auf "+SetProp(P_TTY,str)+".\n");
+  if(str == "ansi" || str == "vt100") {
+      printf("Terminal Test:\n");
+      printf("VT100: fett unterstrichen "+
+             "blinkend invers\n");
+      if(str == "ansi") {
+          printf("ANSI Farben und VT100 Attribute:\n");
+          foreach(int fg: 30 .. 37) {
+              foreach(int bg: 40 .. 47) {
+                  printf("[%d;%dm@", fg, bg);
+                  printf("[%d;%dm@", fg, bg);
+                  printf("[%d;%dm@", fg, bg);
+                  printf("[%d;%dm@", fg, bg);
+              }
+              printf("\n");
+          }
+          printf("Sollte dieser Text hier nicht richtig lesbar\nsein,"+
+                 "benutze das Kommando stty reset!\n");
+      }
+
+  }
+  return 1;
+}
+
+int set_ascii_art(string str)
+{
+  if (str!="ein"&&str!="aus")
+  {
+     printf("Du moechtest 'Grafik' "+(QueryProp(P_NO_ASCII_ART)?"NICHT ":"")+
+                     "sehen.\n");
+  }
+
+  if (str=="ein") {
+    SetProp(P_NO_ASCII_ART, 0);
+    printf("Zukuenftig moechtest Du 'Grafik' sehen.\n");
+  }
+
+  if (str=="aus") {
+    SetProp(P_NO_ASCII_ART, 1);
+    printf("Zukuenftig moechtest Du KEINE 'Grafik' mehr sehen.\n");
+  }
+
+
+  return 1;
+}
+
+int _set_shell_version(int arg)
+{
+  if (!intp(arg))
+    return -1;
+  Set(P_SHELL_VERSION,({QueryProp(P_RACE),arg}));
+  return 1;
+}
+
+int _query_shell_version()
+{   mixed sv;
+
+    if (!(sv=Query(P_SHELL_VERSION)) || !pointerp(sv) || sizeof(sv)!=2 ||
+        sv[0]!=QueryProp(P_RACE) || !intp(sv[1]))
+      return 0;
+    return sv[1];
+}
+
+// XxXxXxXxXx
+
+int more(string str)
+{
+  if(!str)
+  {
+    notify_fail("Usage: more <file>\n");
+    return 0;
+  }
+  if (file_size(str) <= 0) {
+    notify_fail(str+": No such file\n");
+    return 0;
+  }
+  More(str, 1);
+  return 1;
+}
+
+static int set_visualbell(string str)
+{
+  if(!str)
+  {
+    write("Derzeitige Einstellung fuer Tonausgabe: "+
+         (QueryProp(P_VISUALBELL)?"AUS":"EIN")+".\n");
+    return 1;
+  }
+  if (str=="ein")
+  {
+    if(!QueryProp(P_VISUALBELL))
+      write("Die Tonausgabe stand schon auf EIN.\n");
+    else
+      {
+  SetProp(P_VISUALBELL,0);
+        write("OK, Tonausgabe auf EIN gestellt.\n");
+      }
+  }
+  else
+    if (str=="aus")
+    {
+      if(QueryProp(P_VISUALBELL))
+        write("Die Tonausgabe stand schon auf AUS.\n");
+      else
+        {
+          SetProp(P_VISUALBELL,1);
+    write("OK, Tonausgabe auf AUS gestellt.\n");
+  }
+    }
+    else
+      write("Syntax: ton [ein|aus]\n");
+  return 1;
+}
+
+static int set_screensize(string str)
+{
+  int size;
+
+  if (str && (str[0..2] == "abs" || str[0..2]=="rel")) {
+    size = QueryProp(P_MORE_FLAGS);
+    if (str[0..2] == "abs") {
+      size |= E_ABS;
+      write("Es wird beim Prompt die Zeilenzahl des Textes angegeben.\n");
+    }
+    else {
+      size &= ~E_ABS;
+      write("Es wird beim Prompt der prozentuale Anteil des Textes angegeben.\n");
+    }
+    SetProp(P_MORE_FLAGS, size);
+    return 1;
+  }
+
+  if ( str && (str=="auto" || sscanf( str, "auto %d", size )) ){
+      if ( size > 0 ){
+          write("Ungueltiger Wert! "
+                "In Verbindung mit 'auto' sind nur negative Werte erlaubt.\n");
+          return 1;
+      }
+
+      SetProp( P_SCREENSIZE, size-1 );
+
+      write("Ok, Deine Zeilenzahl wird nun automatisch ermittelt (derzeit "+
+            QueryProp(P_SCREENSIZE)+").\n"+
+            break_string("Bitte beachte, dass dies nur einwandfrei "
+                         "funktioniert, wenn Dein Client Telnetnegotiations "
+                         "unterstuetzt (siehe auch \"hilfe telnegs\").") );
+    return 1;
+  }
+
+  if ( !str || str=="" || !sscanf( str, "%d", size ) || size < 0 || size > 100){
+      write(break_string(
+            sprintf("Mit dem Befehl 'zeilen <groesse>' kannst Du einstellen, "
+            "wieviele Zeilen bei mehrseitigen Texten auf einmal ausgegeben "
+            "werden. Die angegebene Groesse muss zwischen 0 und 100 liegen. "
+            "Bei Groesse 0 wird einfach alles ausgegeben (ohne Pause). Mit "
+            "der Einstellung 'auto' wird die Groesse automatisch ueber "
+            "die Telnetnegotiations ermittelt (siehe auch 'hilfe telnegs'). "
+            "Um nach einer Seite Text noch etwas Platz zu haben, kann man z.B. "
+            "'zeilen auto -3' einstellen.\n"
+            "Die Voreinstellung ist 20 Zeilen.\n"
+            "Mit 'zeilen abs[olut]' und 'zeilen rel[ativ]' kannst Du fest"
+            "legen, ob im Prompt bei langen Texten die aktuelle Zeilennummer "
+            "oder eine prozentuale Angabe ausgegeben wird.\n"
+            "Deine aktuelle Einstellung ist %d%s Zeilen (%s).",
+            QueryProp(P_SCREENSIZE),
+            Query(P_SCREENSIZE) < 0 ? " 'automatische'" : "",
+            QueryProp(P_MORE_FLAGS) & E_ABS ? "absolut" : "relativ"),78,0,1));
+    return 1;
+  }
+
+  SetProp( P_SCREENSIZE, size );
+
+  printf( "Okay, Deine Zeilenzahl steht nun auf %d.\n", size );
+  return 1;
+}
+
+static int _query_screensize()
+{
+    int sz,rows;
+
+    if ( (sz=Query(P_SCREENSIZE)) >= 0 )
+        return sz;
+
+    if ( !rows=QueryProp(P_TTY_ROWS) )
+        return 0;
+
+    return (rows+=sz) >= 5 ? rows : 5;
+}
+
+static int presay(string str)
+{
+  if (!str=_unparsed_args())
+    write("Dein Presay ist jetzt geloescht.\n");
+  else
+    printf("Dein Presay lautet jetzt: \"%s\".\n",str=capitalize(str));
+  SetProp(P_PRESAY,str);
+  return 1;
+}
+
+static int sethands(string str)
+{
+  mixed *hands;
+
+  if (!(str=_unparsed_args()))
+  {
+    write("sethands <message>\n");
+    return 1;
+  }
+  if (str=="0")
+      hands=RaceDefault(P_HANDS);
+  if (!hands || !pointerp(hands))
+      hands=Query(P_HANDS);
+  hands[0]=" "+str;
+  Set(P_HANDS,hands);
+  write("Ok.\n");
+  return 1;
+}
+
+static int inform(string str)
+{
+  switch (str) {
+    case "on":
+    case "ein":
+    case "an":
+      if (Query(P_INFORMME))
+    write("Das hattest Du schon so eingestellt.\n");
+      else
+      {
+    write("Kuenftig wirst Du informiert, wenn jemand das "MUDNAME" verlaesst/betritt.\n");
+    Set(P_INFORMME,1);
+      }
+      return 1;
+    case "aus":
+    case "off":
+      if (!Query(P_INFORMME))
+    write("Das hattest Du schon so eingestellt.\n");
+      else
+      {
+    write("Ok.\n");
+    Set(P_INFORMME,0);
+      }
+      return 1;
+    case 0:
+      write("Inform-Mode ist "+(Query(P_INFORMME)?"an":"aus")+"geschaltet.\n");
+      return 1;
+    }
+  write("inform an oder inform aus, bitte.\n");
+  return 1;
+}
+
+void delayed_write(mixed *what)
+{
+  if (!pointerp(what)||!sizeof(what)||!pointerp(what[0]))
+    return;
+  tell_object(ME,what[0][0]);
+  if (sizeof(what)>1&&sizeof(what[0])>1)
+    call_out("delayed_write",what[0][1],what[1..]);
+}
+
+void notify_player_change(string who, int rein, int invis)
+{
+  string *list,name;
+  mixed mlist;
+
+  if (invis) name="("+who+")";
+    else name=who;
+
+  if (Query(P_INFORMME))
+  {
+      if (rein)
+        tell_object(ME,name+" ist gerade ins "MUDNAME" gekommen.\n");
+      else
+        tell_object(ME,name+" hat gerade das "MUDNAME" verlassen.\n");
+  }
+
+  if(Query(P_WAITFOR_FLAGS) & (0x01))return ;
+
+  if(pointerp(list=Query(P_WAITFOR)) && sizeof(list) && member(list,who)!=-1)
+  {
+    if (!QueryProp(P_VISUALBELL))
+        name+=sprintf("%c",7); // Char fuer Pieps an den String anhaengen.
+    // Moechte der Spieler keine ASCII-Grafik sehen, wird diese Meldung ohne
+    // Leerzeichen formatiert, so dass sie von Screenreadern vorgelesen wird.
+    // Anderenfalls wuerde sie einzeln buchstabiert.
+    if ( QueryProp(P_NO_ASCII_ART) )
+    {
+      delayed_write( ({ ({ sprintf("%s IST JETZT %sDA !!!\n", 
+                           name, (rein?"":"NICHT MEHR ")) }) }) );
+    }
+    else 
+    {
+      delayed_write( ({ ({ sprintf("%s   I S T   J E T Z T   %sD A !!!\n",
+                           name, (rein?"":"N I C H T   M E H R   ")) }) }) );
+    }
+  }
+
+  if (rein && (sizeof(mlist=QueryProp(P_WAITFOR_REASON))) &&
+     (mappingp(mlist)) && (mlist[who]))
+        Show_WaitFor_Reason(who,invis);
+}
+
+static int erwarte(string str)
+{
+  string *list,*str1;
+  mixed mlist;
+  int i;
+
+  if (!mappingp(mlist=QueryProp(P_WAITFOR_REASON)))
+     mlist=([]);
+  if (!pointerp(list=Query(P_WAITFOR)))
+     list=({});
+
+  if (!str || str=="-u")
+  {
+     if(Query(P_WAITFOR_FLAGS)&0x01)
+       write("Du hast 'erwarte' temporaer deaktiviert.\n");
+     write("Du erwartest jetzt");
+     if (!sizeof(list))
+        write(" niemanden mehr.\n");
+     else
+     {
+        write(":\n");
+        if (!str) list=sort_array(list,#'>);
+        More(break_string(CountUp(list),78));
+     }
+     return 1;
+  }
+  if(str=="aus"){
+    Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)|0x01);
+    write("Erwarte ist jetzt deaktiviert.\n");
+    return 1;
+  }
+  if(str=="an" || str=="ein"){
+    Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)&0xFE);
+    write("Erwarte ist jetzt aktiv.\n");
+    return 1;
+  }
+
+  str1=explode(_unparsed_args()||""," ");
+  if (sizeof(str1)==1)
+  {
+     if (str1[0]!="wegen")
+     {
+        str=capitalize(lower_case(str));
+        if (member(list,str)!=-1)
+        {
+           SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,str));
+           list-=({str});
+           write(str+" aus der Liste entfernt.\n");
+        } else
+        {
+           if (sizeof(list)>1000)
+           {
+             write("Du erwartest schon genuegend!\n");
+             return 1;
+           }
+           list+=({str});
+           write(str+" an die Liste angehaengt.\n");
+        }
+        Set(P_WAITFOR,list);
+     }
+     else
+     {
+        if (sizeof(mlist) && sizeof(list=m_indices(mlist)))
+        {
+           write("Du erwartest aus einem bestimmten Grund:\n");
+           write(break_string(CountUp(sort_array(list,#'>))+".",78));
+        }
+        else write("Du erwartest niemanden aus einem bestimmten Grund.\n");
+     }
+     return 1;
+  }
+  notify_fail("Falsche Syntax, siehe 'hilfe erwarte'!\n");
+  if (str1[1]!="wegen") return 0;
+  if (sizeof(str1)==2)
+     Show_WaitFor_Reason(capitalize(lower_case(str1[0])),0);
+  else {
+     string s=capitalize(lower_case(str1[0]));
+     if (sizeof(str1)==3 && (str1[2]=="nichts" || str1[2]=="loeschen"))
+        if (!mlist[s])
+           write("Du hast "+s+" aus keinem bestimmten Grund erwartet!\n");
+        else
+        {
+           SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,s));
+           write("Du erwartest "+s+" aus keinem bestimmten Grund mehr!\n");
+        }
+     else
+     {
+        if (IS_ARCH(ME)) i=80; else if (IS_LEARNER(ME)) i=40;
+        else if (IS_SEER(ME)) i=20; else i=10;
+        if (!mlist[s] && sizeof(mlist)>=i)
+           write("Sorry, aber Du erwartest schon genuegend Leute!\n");
+        else
+        {
+           SetProp(P_WAITFOR_REASON,mlist+([s:implode(str1[2..]," ")]));
+           Show_WaitFor_Reason(s,0);
+        }
+     }
+  }
+  return 1;
+}
+
+static int uhrmeldung(string str)
+{
+  if (!(str=_unparsed_args()))
+  {
+    str=QueryProp(P_CLOCKMSG);
+    if (!str)
+    {
+      write("Du hast die Standard-Uhrmeldung.\n");
+      return 1;
+    }
+        if( !stringp(str) ) str = sprintf("%O\n",str);
+    printf("Deine Uhrmeldung ist:\n%s\n",str[0..<2]);
+    return 1;
+  }
+  if (str=="0")
+  {
+    SetProp(P_CLOCKMSG,0);
+    write("Ok, Du hast jetzt wieder die Standard-Meldung.\n");
+    return 1;
+  }
+  if (sizeof(explode(str,"%d"))>2)
+  {
+    write("Fehler, es darf nur ein %d in der Meldung vorkommen.\n");
+    return 1;
+  }
+  /* Mehrere %-Parameter verursachen das Abschalten der Uhr zur vollen Stunde.
+   */
+  if (sizeof(explode(str,"%"))>2)
+  {
+    write("Fehler: Zuviele %-Parameter in der Meldung.\n");
+    return 1;
+  }
+  /* Nur ein %-Parameter, aber der falsche: nicht sinnvoll. */
+  else
+  {
+    int i = strstr(str,"%",0);
+    if ( i>-1 && ( i==sizeof(str)-1 || str[i+1]!='d'))
+    {
+      write("Fehler: Falscher %-Parameter in der Meldung.\n");
+      return 1;
+    }
+  }
+  str+="\n";
+  SetProp(P_CLOCKMSG,str);
+  write("Ok.\n");
+  return 1;
+}
+
+static int zeitzone(string str)
+{
+  int zt;
+  if(!str || str==""){
+    if(!(zt=QueryProp(P_TIMEZONE)))
+      write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
+            "eingestellt.\n");
+    else if(zt>0)
+      printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
+             "eingestellt.\n",zt);
+    else
+      printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
+             "Berlin eingestellt.\n",-zt);
+    return 1;
+  }
+  if(sscanf(str,"utc %d",zt)==1)  zt=(zt-1)%24;
+  else zt=to_int(str)%24;
+
+  SetProp(P_TIMEZONE,zt);
+
+  if(!zt)
+    write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
+          "eingestellt.\n");
+  else if(zt>0)
+    printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
+           "eingestellt.\n",zt);
+  else
+    printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
+           "Berlin eingestellt.\n",-zt);
+  return 1;
+}
+
+static int emailanzeige(string str){
+  notify_fail("Syntax: emailanzeige [alle|freunde|niemand]\n");
+  if(!str || str==""){
+    if(!(str=QueryProp(P_SHOWEMAIL)))str="Niemandem";
+    else if(str=="alle")str="allen";
+    else if(str=="freunde")str="Deinen Freunden";
+    else if(str=="niemand")str="niemandem";
+    else{
+      SetProp(P_SHOWEMAIL,0);
+      str="Niemandem";
+    }
+    write("Deine Email wird "+str+" angezeigt.\n");
+    return 1;
+  }
+  else if(member(({"alle","freunde","niemand"}),str)==-1)return 0;
+
+  SetProp(P_SHOWEMAIL,str);
+
+  if(str=="alle")str="allen";
+  else if(str=="freunde")str="Deinen Freunden";
+  else str="niemandem";
+  write("Deine Email wird "+str+" angezeigt.\n");
+  return 1;
+}
+
+static int zaubertraenke()
+{
+  More("/room/orakel"->TipListe());
+  return 1;
+}
+
+varargs static int angriffsmeldung(string arg) {
+  if (arg=="ein" || arg=="an")
+    SetProp(P_SHOW_ATTACK_MSG,1);
+  else if (arg=="aus")
+    SetProp(P_SHOW_ATTACK_MSG,0);
+  if (QueryProp(P_SHOW_ATTACK_MSG))
+    write("Du siehst saemtliche Angriffsmeldungen von Dir.\n");
+  else
+    write("Du siehst nur neue Angriffsmeldungen von Dir.\n");
+  return 1;
+}
+
+static mixed _query_localcmds()
+{
+  return ({({"zeilen","set_screensize",0,0}),
+           ({"email","set_email",0,0}),
+           ({"url","set_homepage",0,0}),
+           ({"icq","set_icq",0,0}),
+           ({"messenger", "set_messenger", 0, 0}), 
+           ({"ort","set_location",0,0}),
+           ({"punkte","short_score",0,0}),
+           ({"score","short_score",0,0}),
+           ({"info","score",0,0}),
+           ({"kurzinfo","very_short_score",0,0}),
+           ({"quit","new_quit",0,0}),
+           ({"ende","new_quit",0,0}),
+           ({"disconnect","disconnect",0,0}),
+           ({"schlaf","disconnect",1,0}),
+           ({"speichern","save_character",0,0}),
+           ({"save","save_character",0,0}),
+           ({"toete","kill",0,0}),
+           ({"angriffsmeldung","angriffsmeldung",0,0}),
+           ({"passw","change_password",1,0}),
+           ({"hilfe","help",1,0}),
+           ({"selbstloeschung","self_delete",0,0}),
+           ({"spielpause","spielpause",0,0}),
+           ({"spieldauer","spieldauer",0,0}),
+           ({"idee","idea",0,0}),
+           ({"typo","typo",0,0}),
+           ({"bug","bug",0,0}),
+           ({"fehler","fehlerhilfe",0,0}),
+           ({"md","md",0,0}),
+           ({"detail","md",0,0}),
+           ({"vorsicht","toggle_whimpy",0,0}),
+           ({"stop","stop",0,0}),
+           ({"kwho","kwho",0,0}),
+           ({"kwer","kwho",0,0}),
+           ({"kkwer","kkwho",0,0}),
+           ({"kkwho","kkwho",0,0}),
+           ({"who","who",0,0}),
+           ({"wer","who",0,0}),
+           ({"zeit","uhrzeit",0,0}),
+           ({"uhrzeit","uhrzeit",0,0}),
+           ({"weg","weg",0,0}),
+           ({"wegmeldung", "wegmeldung", 0, 0}),
+           ({"idlezeit", "idlezeit", 0, 0}),
+           ({"finger","finger",0,0}),
+           ({"muds","muds",0,0}),
+           ({"emote","emote",0,0}),
+           ({":","emote",1,0}),
+           ({";","emote",1,0}),
+           ({"remote","remote",0,SEER_LVL}),
+           ({"r:","remote",1,0}),
+           ({"r;","gremote",1,0}),
+           ({"titel","set_title",0,0}),
+           ({"review","review",0,SEER_LVL}),
+           ({"setmin","setmin",0,SEER_LVL}),
+           ({"setmout","setmout",0,SEER_LVL}),
+           ({"setmmin","setmmin",0,SEER_LVL}),
+           ({"setmmout","setmmout",0,SEER_LVL}),
+           ({"sethands","sethands",0,SEER_LVL}),
+           ({"presay","presay",0,SEER_LVL}),
+           ({"extralook","extralook",0,SEER_LVL}),
+           ({"fluchtrichtung","toggle_whimpy_dir",0,SEER_LVL}),
+           ({"inform","inform",0,0}),
+           ({"erwarte","erwarte",0,0}),
+           ({"stty","stty",0,0}),
+           ({"grafik", "set_ascii_art", 0, 0}), 
+           ({"uhrmeldung","uhrmeldung",0,0}),
+           ({"zeitzone","zeitzone",0,0}),
+           ({"behalte","behalte",0,0}),
+           ({"zweitiemarkierung","zweitiemarkierung",0,0}),
+           ({"emailanzeige","emailanzeige",0,0}),
+           ({"topliste","topliste",0,0}),
+           ({"ton","set_visualbell",0,0}),
+           ({"telnegs","show_telnegs",0,0}),
+           ({"spotte", "spotte", 0, 0}),
+           ({"reise","reise",0,0}),
+           ({"zaubertraenke","zaubertraenke",0,0}),
+           ({"telnet","telnet_cmd",0,0}),
+     })+
+     command::_query_localcmds()+
+     viewcmd::_query_localcmds()+
+     comm::_query_localcmds()+
+     skills::_query_localcmds()+
+     description::_query_localcmds();
+}
+
+static int _check_keep(object ob)
+{
+  return (ob->QueryProp(P_KEEP_ON_SELL))==geteuid(ME);
+}
+
+static mixed _set_testplayer(mixed arg) {
+  mixed res;
+  object setob;
+
+  setob=this_player();
+  if (!objectp(setob) || !query_once_interactive(setob))
+    setob=this_interactive();
+  if (!objectp(setob))
+    setob=previous_object();
+  if (setob && !IS_DEPUTY(setob)) {
+    arg=geteuid(setob);
+    if (!arg || arg=="NOBODY")
+      arg=getuid(setob);
+    arg=capitalize(arg);
+  }
+  res=Set(P_TESTPLAYER,arg);
+  Set(P_TESTPLAYER,PROTECTED,F_MODE_AS);
+  return res;
+}
+
+int zweitiemarkierung(string arg)
+{
+  if (!QueryProp(P_SECOND))
+    return _notify_fail("Aber Du bist doch gar kein Zweiti.\n"),0;
+  notify_fail("Syntax: zweitiemarkierung [unsichtbar|sichtbar|name]\n");
+  if (!arg)
+    return 0;
+  switch (arg)
+  {
+    case "unsichtbar" :
+      SetProp(P_SECOND_MARK,-1);
+      write("Jetzt sieht kein Spieler mehr, dass Du ein Zweiti bist.\n");
+      return 1;
+    case "sichtbar" :
+      SetProp(P_SECOND_MARK,0);
+      write("Jetzt sieht kein Spieler mehr, wessen Zweiti Du bist.\n");
+      return 1;
+    case "name" :
+      SetProp(P_SECOND_MARK,1);
+      write("Jetzt koennen alle sehen, wessen Zweiti Du bist.\n");
+      return 1;
+  }
+  return 0;
+}
+
+int topliste(string arg)
+{
+    if (!arg)
+    {
+        printf("Du hast Dich fuer die Topliste %s.\n",
+            (QueryProp(P_NO_TOPLIST) ? "gesperrt" : "freigegeben"));
+        return 1;
+    }
+    else if (member(({"j","ja","n","nein"}),arg)==-1)
+        return _notify_fail("Syntax: topliste [ja|nein]\n"),0;
+    if (arg[0]=='j')
+    {
+        SetProp(P_NO_TOPLIST,0);
+        write("Du kannst jetzt (theoretisch) in der Topliste auftauchen.\n");
+    }
+    else
+    {
+        SetProp(P_NO_TOPLIST,1);
+        write("Du wirst jetzt nicht in der Topliste auftauchen.\n");
+    }
+    Set(P_NO_TOPLIST,SAVE|PROTECTED,F_MODE_AS);
+    return 1;
+}
+
+int show_telnegs(string arg)
+{
+    if (!arg)
+    {
+        write("Du bekommst Aenderungen Deiner Fenstergroesse "+
+              (QueryProp(P_TTY_SHOW)?"":"nicht ")+"angezeigt.\n");
+        return 1;
+    }
+    if (member(({"ein","an","aus"}),arg)==-1)
+    {
+        write("Syntax: telnegs [ein|aus]\n");
+        return 1;
+    }
+    if (arg=="ein" || arg=="an")
+    {
+        write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"":"nun ")+
+              "Aenderungen Deiner Fenstergroesse angezeigt.\n");
+        Set(P_TTY_SHOW,1);
+        return 1;
+    }
+    write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"nun ":"")+
+          "Aenderungen Deiner Fenstergroesse nicht "+
+          (QueryProp(P_TTY_SHOW)?"mehr ":"")+"angezeigt.\n");
+    Set(P_TTY_SHOW,0);
+    return 1;
+}
+
+private int set_keep_alive(string str) {
+  if (str == "ein") {
+    telnet_tm_counter = 240 / __HEART_BEAT_INTERVAL__;
+    tell_object(this_object(), break_string(
+        "An Deinen Client werden jetzt alle 4 Minuten unsichtbare Daten "
+        "geschickt, um zu verhindern, dass Deine Verbindung zum "MUDNAME
+        " beendet wird.", 78));
+  }
+  else if (str == "aus") {
+    telnet_tm_counter = 0;
+    tell_object(this_object(),break_string(
+        "Du hast das Senden von unsichtbaren Daten (Keep-Alive-Pakete) an "
+        "Deinen Client ausgeschaltet.",78));
+  }
+  else {
+    if (!telnet_tm_counter)
+      tell_object(this_object(), break_string(
+        "An Deinen Client werden keine Keep-Alive-Pakete geschickt.",78));
+    else
+      tell_object(this_object(), break_string(
+        "An Deinen Client werden alle 4 Minuten " 
+        "unsichtbare Daten geschickt, damit Deine Verbindung "
+        "zum "MUDNAME" nicht beendet wird.",78));
+  }
+  return 1;
+}
+
+private int print_telnet_rttime() {
+  int rtt = QueryProp(P_TELNET_RTTIME);
+  if (rtt>0)
+    tell_object(ME, break_string(
+      "Die letzte gemessene 'round-trip' Zeit vom MG zu Deinem Client "
+      "und zurueck betrug " + rtt + " us.",78));
+  else
+    tell_object(ME, break_string(
+      "Bislang wurde die 'round-trip' Zeit vom MG zu Deinem Client "
+      "noch nicht gemessen oder Dein Client unterstuetzt dieses "
+      "nicht.",78));
+  return 1;
+}
+
+int telnet_cmd(string str) {
+  if (!str) return 0;
+  string *args = explode(str, " ");
+  string newargs;
+  if (sizeof(args) > 1)
+    newargs = implode(args[1..], " ");
+  else
+    newargs = "";
+
+  switch(args[0])
+  {
+    case "keepalive":
+      return set_keep_alive(newargs);
+    case "rttime":
+      return print_telnet_rttime();
+  }
+  return 0;
+}
+
+int spotte( string str )
+{
+    _notify_fail( "Hier ist nichts, was Du verspotten koenntest!\n" );
+    return 0;
+}
+
+int behalte(string str)
+{
+  object ob,*obs;
+  string s;
+
+  if (str)
+  {
+    if (str=="alles") {
+      filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, getuid());
+      write("Ok!\n");
+      return 1;
+    }
+    if (str=="nichts") {
+      filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, 0);
+      write("Ok!\n");
+      return 1;
+    }
+    if (!sizeof(obs=find_obs(str,PUT_GET_NONE)))
+    {
+      _notify_fail("Aber sowas hast Du nicht dabei!\n");
+      return 0;
+    }
+    else ob=obs[0];
+
+    if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
+        ob->SetProp(P_KEEP_ON_SELL,0);
+    else
+        ob->SetProp(P_KEEP_ON_SELL,geteuid(ME));
+
+    // erneut abfragen, da sich der Wert nicht geaendert haben muss
+    if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
+        write(break_string(sprintf("Ok, Du wirst %s jetzt bei 'verkaufe alles' "
+                                   "behalten.\n",ob->name(WEN)),78));
+    else
+        write(break_string(sprintf("Ok, Du wirst %s beim naechsten 'verkaufe "
+                                   "alles' mitverkaufen!\n",ob->name(WEN)),78));
+
+    return 1;
+  }
+  s=make_invlist(ME,filter(all_inventory(ME),#'_check_keep)); //'));
+  More(s);
+  return 1;
+}
+
+static int _query_lep()
+{
+  int val;
+  val = LEPMASTER->QueryLEP();
+  Set( P_LEP, val );
+  return val;
+}
+
+static mixed _set_fraternitasdonoarchmagorum(mixed arg)
+{
+  if (!intp(arg)) return -1;
+
+  if ((!previous_object(1)||object_name(previous_object(1))!=FAO_MASTER) && 
+      (!this_interactive() || !IS_ARCH(this_interactive())))
+    return -1;
+
+  if (!intp(arg)) return -1;
+
+  log_file("fao/P_FAO",sprintf("%s - %s P_FAO gesetzt auf %O\n",
+        dtime(time()),query_real_name(),arg) );
+  return Set(P_FAO,arg);
+}
+
+nomask void set_realip(string str)
+{
+  if(previous_object() && strstr(object_name(previous_object()),"/secure")==0)
+  {
+    realip=str;
+  }
+}
+
+nomask string query_realip()
+{
+  return realip ? realip : "";
+}
+
+mixed _query_netdead_env() {
+        return ndead_location || ndead_l_filename;
+}
diff --git a/std/player/channel.c b/std/player/channel.c
new file mode 100644
index 0000000..9f8512a
--- /dev/null
+++ b/std/player/channel.c
@@ -0,0 +1,579 @@
+// MorgenGrauen MUDlib
+//
+// channel.c -- channel client
+//
+// $Id: channel.c 9404 2015-12-13 00:21:44Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <util.h>
+#include <thing/properties.h>
+#include <living/comm.h>
+#include <player.h>
+#include <player/comm.h>
+#include <daemon.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+
+#include <wizlevels.h>
+#include <defines.h>
+#include <properties.h>
+#include <sys_debug.h>
+#include <regexp.h>
+
+#define P_SWAP_CHANNELS  "swap_channels"
+#define P_CHANNEL_SHORT  "short_channels"
+
+#define CHANNELCMDS      "[#@%$&()<>a-zA-Z0-9\\-]"
+
+#define DEFAULT_CHANNELS ({"Abenteuer", "Anfaenger","Grats","Tod", "ZT"})
+#define DEFAULT_SHORTCUTS \
+([                     \
+ "b":"Abenteuer",      \
+ "a":"Allgemein",      \
+ "B":"Beileid",        \
+ "q":"D-chat",         \
+ "G":"Grats",          \
+ "M":"Moerder",        \
+ "h":"Seher",          \
+ "T":"Tod",            \
+])
+
+#define WIZARD_SHORTCUTS \
+([                     \
+ "P":"D-code",         \
+ "D":"Debug",          \
+ "O":"Intercode",      \
+ "I":"Intermud",       \
+ "m":"Magier",         \
+])
+
+
+private nosave mapping shortcut;
+private nosave int c_status;
+
+void create()
+{
+  Set(P_CHANNELS, SAVE, F_MODE);
+  Set(P_CHANNELS, DEFAULT_CHANNELS);
+  Set(P_SWAP_CHANNELS, SAVE, F_MODE);
+  Set(P_STD_CHANNEL, "Allgemein");
+  Set(P_STD_CHANNEL, SAVE, F_MODE);
+  Set(P_CHANNEL_SHORT, SAVE, F_MODE);
+  Set(P_CHANNEL_SHORT, DEFAULT_SHORTCUTS
+      + (IS_LEARNER(this_object()) ? WIZARD_SHORTCUTS : ([])));
+}
+
+static mixed _query_localcmds()
+{
+  return ({({"-","ChannelParser", 1, 0}),
+           ({"ebene", "ChannelAdmin", 0, 0}),
+           ({"ebenen", "ChannelAdmin", 1, 0}),
+         });
+}
+
+mixed RegisterChannels()
+{
+  mixed err;
+  if(extern_call() &&
+     previous_object() != find_object(CHMASTER)) return;
+  c_status = 0;
+  shortcut = QueryProp(P_CHANNEL_SHORT);
+  SetProp(P_CHANNELS, map(QueryProp(P_CHANNELS) || ({}),
+	#'lower_case));
+  err = filter(QueryProp(P_CHANNELS),
+         symbol_function("join", CHMASTER),
+         this_object());
+  if(QueryProp(P_LEVEL) < 5) return err;
+  while(sizeof(err)) {
+    CHMASTER->new(err[0], this_object());
+    err[0..0] = ({});
+  }
+  return err;
+}
+
+mixed RemoveChannels()
+{
+  closure cl;
+  mixed err=({});
+  if(extern_call() &&
+     previous_object() != find_object(CHMASTER)) return;
+  if(!c_status) c_status = 1;
+  else return ({});
+  cl=symbol_function("leave", CHMASTER);
+  if (closurep(cl)) {
+      err = filter(QueryProp(P_CHANNELS), cl, this_object());
+      SetProp(P_CHANNELS, QueryProp(P_CHANNELS) - err);
+  }
+  return err;
+}
+
+varargs private string getName(mixed x, int fall) {
+  
+  mixed o = closurep(x) ? query_closure_object(x) : x;
+  if(stringp(o) && sizeof(o) && (x = find_object(o))) 
+    o = x;
+  
+  // Objekte
+  if (objectp(o)) {
+    // Magier sehen unsichtbare nicht nur als "Jemand"
+    if (o->QueryProp(P_INVIS) && IS_LEARNING(this_object()))
+      return "("+capitalize(getuid(o))+")";
+    // Froesche mit Namen versorgen.
+    if (o->QueryProp(P_FROG))
+      return "Frosch "+capitalize(getuid(o));
+    // Default (Unsichtbare als "Jemand" (s. Name()))
+    return o->Name(fall, 2)||"<Unbekannt>";
+  }
+  // Strings
+  else if (stringp(o) && sizeof(o)) {
+    if (o[0] == '/') {
+      // unsichtbare Objekte...
+      int p = strstr(o, "$");
+      if (p != -1) {
+	// Magier im Magiermodus kriegen den Realnamen, andere nicht.
+	if (IS_LEARNING(this_object()))
+	  return o[1..p-1];
+	else
+	  return o[p+1..];
+      }
+      else
+	// doch nicht unsichtbar
+	return (fall == WESSEN ? o+"s" : o);
+    }
+    else
+      // nicht unsichtbar
+      return (fall == WESSEN ? o+"s" : o);
+  }
+  // Fall-through
+  return "<Unbekannt>";
+}
+
+// <nonint> unterdrueckt die AUsgabe an den Spieler und liefert den Text
+// zurueck. Wird nur fuer die Ebenenhistory benutzt. 
+string ChannelMessage(mixed* msg, int nonint)
+{
+  string channel_message;
+  string channel=msg[0];
+  object sender=msg[1];
+  string message=msg[2];
+  int msg_type = msg[3];
+
+  if ( previous_object() != find_object(CHMASTER) &&
+       previous_object() != ME )
+      return 0;
+
+  string sender_name = getName(sender, msg_type == MSG_GEMOTE ? WESSEN : WER);
+  int prepend_indent_flag=QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0;
+  switch(msg_type) {
+  case MSG_EMPTY:
+    channel_message= message+"\n";
+    break;
+  case MSG_GEMOTE:
+  case MSG_EMOTE:
+    channel_message = break_string(sender_name + " "+ message+"]",
+                   78, sprintf("[%s:", channel),
+                   BS_INDENT_ONCE|prepend_indent_flag);
+    break;
+  case MSG_SAY:
+  default:
+    string presay=sprintf("[%s:%s] ", channel, sender_name);
+    channel_message = break_string(message, max(78,sizeof(presay)+10),
+                   presay, prepend_indent_flag);
+    break;
+  }
+  if(nonint)
+    return channel_message;
+
+  // Wenn GMCP sich um Uebertragung der Nachricht kuemmert, wird ReceiveMsg()
+  // nicht mehr aufgerufen.
+  if (GMCP_Channel(channel_message, channel, sender_name) != 1)
+  {
+    // Der Ebenenname muss in Kleinbuchstaben uebergeben werden, damit die
+    // Ignorierepruefung funktioniert. Die ignorierestrings sind naemlich alle
+    // kleingeschrieben.
+    ReceiveMsg(channel_message, 
+               MT_COMM|MT_FAR|MSG_DONT_STORE|MSG_DONT_WRAP,
+               MA_CHANNEL"." + lower_case(channel), 0, sender);
+  }
+  return 0;
+}
+
+private void createList(string n, mixed a, mixed m, mixed l)
+{
+  int pos; string sh, *mem;
+  if((pos = member(map(m_values(shortcut), #'lower_case/*'*/), n)) != -1)
+    sh = m_indices(shortcut)[pos];
+  else sh = "";
+  mem=map(a[I_MEMBER],#'getName/*'*/, WER);
+  mem-=({"<MasteR>"});
+  l += ({ sprintf("%-12.12'.'s %c[%-1.1s] %|12.12' 's (%-|3' 'd) %-42.42s\n",
+                  a[I_NAME], (member(m, n) != -1 ? '*' : ' '), sh,
+                  a[I_MASTER] ?
+                  getName(a[I_MASTER]) : getName(a[I_ACCESS]),
+                  sizeof(mem),
+            (closurep(a[I_INFO]) && objectp(query_closure_object(a[I_INFO]))) ?
+                  funcall(a[I_INFO]) || "- Keine Beschreibung -" :
+                  (stringp(a[I_INFO]) ? a[I_INFO] : "- Keine Beschreibung -")
+        ) });
+}
+
+private mixed getChannel(string ch)
+{
+  mixed ff;
+  if(!sizeof(ch)) ch = QueryProp(P_STD_CHANNEL);
+  if(shortcut && shortcut[ch]) ch = shortcut[ch];
+  return CHMASTER->find(ch, this_object());
+}
+
+#ifndef DEBUG
+#define DEBUG(x)        if (funcall(symbol_function('find_player),"zesstra"))\
+          tell_object(funcall(symbol_function('find_player),"zesstra"),\
+	              "MDBG: "+x+"\n")
+#endif
+int ChannelParser(string args)
+{
+  mixed ch, cmd, tmp;
+  int pos, type, err;
+  string txt;
+  cmd = query_verb();
+  args = _unparsed_args();
+  notify_fail("Benutzung: -<Ebene>[ ]['|:|;]<Text>\n"
+              "           -<Ebene>[+|-|?|!|*]\n"
+              "           -?\n");
+  if(!cmd && !args) return 0;
+  if(!args) args = "";
+  cmd = cmd[1..];
+  if(sizeof(cmd = regexplode(cmd,
+                             "^" CHANNELCMDS "*"
+                             "([+-]|\\!|\\?|\\*)*")) > 1)
+  {
+    //z.B. cmd= ({"","allgemein",":testet"})
+    if(sizeof(cmd[1]) > 1 &&
+       strstr("+-?!*", cmd[1][<1..<1]) > -1)
+      tmp = cmd[1][0..<2];
+    else
+      tmp = cmd[1];
+    if(cmd[1] != "?" && cmd[1] != "!")
+      if(pointerp(ch = getChannel(tmp)))
+      {
+        notify_fail("Diese Angabe war nicht eindeutig! "
+                    "Folgende Ebenen passen:\n"
+                    +implode(ch, ", ")+"\n");
+        return 0;
+      }
+      else if(!ch) return (notify_fail("Die Ebene '"+tmp
+                                       +"' gibt es nicht!\n"), 0);
+    //DEBUG(sprintf("ChanCmd: %O\n",cmd));
+    if (sizeof(cmd[1])) {
+      switch(cmd[1][<1]) {
+    case '+':
+      switch(CHMASTER->join(ch, this_object()))
+      {
+      case E_ACCESS_DENIED:
+        notify_fail("Du darfst an die Ebene '"+ch+"' nicht heran.\n");
+        return 0;
+      case E_ALREADY_JOINED:
+        notify_fail("Du hast diese Ebene schon betreten!\n");
+        return 0;
+      default: break;
+      }
+      write("Du betrittst die Ebene '"+ch+"'.\n");
+      if(member(QueryProp(P_CHANNELS), ch = lower_case(ch)) == -1)
+        SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ ch }));
+      return 1;
+    case '-':
+      switch(CHMASTER->leave(ch, this_object()))
+      {
+      case E_ACCESS_DENIED:
+        write("Du kannst die Ebene '"+ch+"' nicht verlassen.\n");
+        break;
+      case E_NOT_MEMBER:
+        write("Wie willst Du eine Ebene verlassen, welche Du nicht "
+              "betreten hast?\n");
+        break;
+      default:
+        write("Du verlaesst die Ebene '"+ch+"'.\n");
+        SetProp(P_CHANNELS, QueryProp(P_CHANNELS) - ({ lower_case(ch), ch }));
+        break;
+      }
+      return 1;
+    case '!':
+    case '?':
+    {
+      mapping l;
+      if(mappingp(l = CHMASTER->list(this_object())))
+        if(stringp(ch) && sizeof(ch) && pointerp(l[ch = lower_case(ch)]))
+        {
+          int c; object o; string n; string *m;
+          m=sort_array(map(l[ch][I_MEMBER],#'getName/*'*/, WER),#'>/*'*/);
+          m-=({"<MasteR>"});
+          write(l[ch][I_NAME]+", "+funcall(l[ch][I_INFO])+".\n");
+          write("Du siehst "+((c = sizeof(m)) > 0
+                              ? (c == 1 ? "ein Gesicht" : c+" Gesichter")
+                              : "niemanden")+" auf der Ebene '"
+                                +l[ch][I_NAME]+"':\n");
+          write(break_string(implode(m,", "), 78));
+          write((l[ch][I_MASTER] ?
+                 getName(l[ch][I_MASTER]) : getName(l[ch][I_ACCESS], WER))
+                +" hat das Sagen auf dieser Ebene.\n");
+        }
+        else
+        {
+          mixed list; list = ({});
+          if(cmd[1][<1] == '!')
+            l -= mkmapping(m_indices(l) - QueryProp(P_CHANNELS));
+          walk_mapping(l, #'createList/*'*/, QueryProp(P_CHANNELS), &list);
+          list = sort_array(list, #'>/*'*/);
+          txt = sprintf("%-12.12' 's  [A] %|12' 's (%-3' 's) %-42.42s\n",
+                        "Name", "Eigner", "Sp", "Beschreibung")
+              + "-------------------------------------------------------"
+              + "-----------------------\n"
+              + implode(list, "");
+          More(txt);
+        }
+      return 1;
+    }
+    case '*':
+    {
+      mixed hist; int amount;
+      if(!pointerp(hist = CHMASTER->history(ch, this_object())) 
+		      || !sizeof(hist))
+      {
+        write("Es ist keine Geschichte fuer '"+ch+"' verfuegbar.\n");
+        return 1;
+      }
+      
+      //(Zesstra) cmd hat offenbar immer 3 Elemente...
+      //bei -all* ({"","all*",""})
+      //bei -all*10 ({"","all*,"10"})
+      //also ist bei -all* amount immer == 0 und es funktioniert eher zufaellig.
+      /*if(sizeof(cmd) > 2) 
+        amount = to_int(cmd[2]);
+      else 
+        amount=sizeof(hist);*/
+      amount=to_int(cmd[2]);
+      if (amount <= 0 || amount >= sizeof(hist))
+        amount=sizeof(hist);
+
+      txt = "Folgendes ist auf '"+ch+"' passiert:\n"
+          + implode(map(hist[<amount..], #'ChannelMessage/*'*/, 1), "");
+      More(txt);
+      return 1;
+    }
+    default:
+      break;
+    }
+    }
+  }
+  if(sizeof(cmd = implode(cmd[2..], "")))
+     args = cmd + (sizeof(args) ? " " : "") + args;
+
+  // KOntrollchars ausfiltern.
+  args = regreplace(args,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
+  if(!sizeof(args)) return 0;
+
+  //Wenn cmd leer ist: MSG_SAY
+  if (!sizeof(cmd)) type=MSG_SAY;
+  else {
+    switch(cmd[0])
+    {
+    case ':' :
+      type = MSG_EMOTE;
+      args = args[1..];
+      break;
+    case ';' :
+      type = MSG_GEMOTE;
+      args = args[1..];
+      break;
+    case '\'':
+      args = args[1..];
+    default  : type = MSG_SAY; break;
+    }
+  }
+  if(!ch || !sizeof(ch)) ch = QueryProp(P_STD_CHANNEL);
+  if((err = CHMASTER->send(ch, this_object(), args, type)) < 0)
+    if(!(err = CHMASTER->join(ch, this_object())))
+    {
+      if(member(QueryProp(P_CHANNELS), ch = lower_case(ch)) == -1)
+        SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ ch }));
+      err = CHMASTER->send(ch, this_object(), args, type);
+    }
+
+  switch(err)
+  {
+  case E_ACCESS_DENIED:
+    notify_fail("Auf der Ebene '"+ch+"' darfst Du nichts sagen.\n");
+    return 0;
+  case E_NOT_MEMBER:
+    notify_fail("Du hast die Ebene '"+ch+"' nicht betreten!\n");
+    return 0;
+  }
+  return 1;
+}
+
+int ChannelAdmin(string args)
+{
+  string n, descr, sh, cn;
+  mixed pa, tmp;
+  args = _unparsed_args();
+  notify_fail("Benutzung: ebene <Abkuerzung>=<Ebene>\n"
+              "           ebene <Abkuerzung>=\n"
+              "           ebene abkuerzungen [standard]\n"
+              "           ebene standard <Ebene>\n"
+              "           ebene an|ein|aus\n"
+              +(QueryProp(P_LEVEL) >= 5 ?
+                "           ebene neu <Name> <Bezeichnung>\n"
+    "           ebene beschreibung <Name> <Beschreibung>\n" : "")
+        +(IS_ARCH(this_object()) ?
+          "           ebene kill <Name>\n"
+	  "           ebene clear <Name>\n": ""));
+  if(!args || !sizeof(args)) return 0;
+  if(sscanf(args, "kill %s", n) && IS_ARCH(this_object()))
+  {
+    if(!(cn = CHMASTER->find(n, this_object()))) cn = n;
+    switch(CHMASTER->remove(cn, this_object()))
+    {
+    case E_ACCESS_DENIED:
+     notify_fail("Die Ebene '"+cn+"' lies sich nicht entfernen!\n");
+     return 0;
+    }
+    write("Du entfernst die Ebene '"+cn+"'.\n");
+    return 1;
+  }
+  if(sscanf(args, "clear %s", n) && IS_ARCH(this_object()))
+  {
+    if(!(cn = CHMASTER->find(n, this_object()))) cn = n;
+    switch(CHMASTER->clear_history(cn, this_object()))
+    {
+    case E_ACCESS_DENIED:
+     notify_fail("Der Verlauf zur Ebene '"+cn+"' lies sich nicht entfernen!\n");
+     return 0;
+    }
+    write("Du entfernst den Verlauf zur Ebene '"+cn+"'.\n");
+    return 1;
+  }
+  if(sscanf(args, "neu %s %s", n, descr) == 2)
+  {
+    mixed x;
+    if(QueryProp(P_LEVEL) < 5)
+      return (notify_fail("Neue Ebenen zu erstellen ist dir verwehrt.\n"), 0);
+    if(!sizeof(regexp(({ n }), "^" CHANNELCMDS CHANNELCMDS "*")))
+      return (notify_fail("Der Name '"+n+"' ist nicht konform!\n"), 0);
+    if (sizeof(n) > 20 )
+      return(notify_fail("Der Name '"+n+"' ist zu lang.\n"), 0);
+    switch(x = CHMASTER->new(n, this_object(), descr))
+    {
+    case E_ACCESS_DENIED:
+      notify_fail("Diese Ebene darfst du nicht erschaffen!\n"); break;
+    default:
+      write("Du erschaffst die Ebene '"+n+"'.\n");
+      SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ lower_case(n) }));
+      return 1;
+    }
+  }
+  if(sscanf(args, "beschreibung %s %s", n, descr) == 2)
+  {
+    mixed ch;
+    cn = CHMASTER->find(n, this_object());
+    if(!cn || pointerp(cn))
+      return (notify_fail("Die Ebene '"+n+"' existiert nicht oder die Angabe "
+        "war nicht eindeutig.\n"), 0);
+    ch = CHMASTER->list(this_object());
+    if(ch[lower_case(cn)][I_MASTER] != this_object())
+      return (notify_fail("Du bist nicht berechtigt die Beschreibung der Ebene"
+        " '"+cn+"' zu aendern.\n"), 0);
+    ch[lower_case(cn)][I_INFO] = descr;
+    write("Die Ebene '"+cn+"' hat ab sofort die Beschreibung:\n"+descr+"\n");
+    return 1;
+  }
+  if(sscanf(args, "%s=%s", sh, n) == 2 && sizeof(n))
+  {
+    mapping sc;
+    if(pointerp(tmp = CHMASTER->find(n, this_object())) || !tmp)
+      return (notify_fail("Benutzung: ebene <Abkuerzung>=<Ebene>\n"
+                          +(pointerp(tmp) ? implode(tmp, ", ") + "\n" :
+                            "Ebene '"+n+"' nicht gefunden!\n")), 0);
+    sc = QueryProp(P_CHANNEL_SHORT);
+    if(!sc) sc = ([]);
+    sc[sh] = tmp;
+    SetProp(P_CHANNEL_SHORT, sc);
+    shortcut = QueryProp(P_CHANNEL_SHORT);
+    write("'"+sh+"' wird jetzt als Abkuerzung fuer '"+tmp+"' anerkannt.\n");
+    return 1;
+  }
+  if(sscanf(args, "%s=", sh))
+  {
+    SetProp(P_CHANNEL_SHORT, m_copy_delete(QueryProp(P_CHANNEL_SHORT) || ([]), sh));
+    shortcut = QueryProp(P_CHANNEL_SHORT);
+    write("Du loeschst die Abkuerzung '"+sh+"'.\n");
+    return 1;
+  }
+  if(args == "an" || args == "ein")
+  {
+    mixed excl;
+    if(pointerp(QueryProp(P_SWAP_CHANNELS)))
+      SetProp(P_CHANNELS, QueryProp(P_SWAP_CHANNELS));
+    else
+      SetProp(P_CHANNELS, m_indices(CHMASTER->list(this_object())));
+    excl = RegisterChannels();
+    write("Du schaltest folgende Ebenen ein:\n"
+          +break_string(implode(QueryProp(P_CHANNELS) - excl, ", "), 78));
+    SetProp(P_SWAP_CHANNELS, 0);
+    return 1;
+  }
+  if(args == "aus")
+  {
+    SetProp(P_SWAP_CHANNELS, QueryProp(P_CHANNELS));
+    RemoveChannels();
+    SetProp(P_CHANNELS, ({}));
+    write("Du stellst die Ebenen ab.\n");
+    return 1;
+  }
+  pa = old_explode(args, " ");
+  if(!strstr("abkuerzungen", pa[0]))
+  {
+    string txt; txt = "";
+    if(sizeof(pa) > 1 && !strstr("standard", pa[1]))
+    {
+      write("Die Standard Abkuerzungen werden gesetzt.\n");
+      SetProp(P_CHANNEL_SHORT, DEFAULT_SHORTCUTS
+              + (IS_LEARNER(this_object()) ? WIZARD_SHORTCUTS : ([])));
+    }
+    walk_mapping(QueryProp(P_CHANNEL_SHORT),
+                 lambda(({'i/*'*/, 'c, 'r}),
+                        ({#'+=, 'r/*'*/,
+                          ({#'sprintf/*'*/, "%5.5s = %s\n", 'i, 'c})})),
+                 &txt);
+    txt = sprintf("Folgende Abkuerzungen sind definiert:\n%-78#s\n",
+                  implode(sort_array(old_explode(txt, "\n"), #'>/*'*/), "\n"));
+    More(txt);
+    return 1;
+  }
+  if(!strstr("standard", pa[0]))
+    if(sizeof(pa) < 2)
+      return (notify_fail("Benutzung: ebene standard <Ebene>\n"
+                          +(QueryProp(P_STD_CHANNEL)
+                            ? "Momentan ist '"+QueryProp(P_STD_CHANNEL)
+                            +"' eingestellt.\n"
+                            : "Es ist keine Standardebene eingestellt.\n")),0);
+    else
+      if(pointerp(tmp = CHMASTER->find(pa[1], this_object())))
+        return (notify_fail("Das war keine eindeutige Angabe! "
+                            "Folgende Ebenen passen:\n"
+                            +break_string(implode(tmp, ", "), 78)), 0);
+      else
+        if(!tmp) return (notify_fail("Ebene '"+pa[1]+"' nicht gefunden!\n"),0);
+        else
+        {
+          write("'"+tmp+"' ist jetzt die Standardebene.\n");
+          SetProp(P_STD_CHANNEL, tmp);
+          return 1;
+        }
+  return(0);
+}
+
diff --git a/std/player/combat.c b/std/player/combat.c
new file mode 100644
index 0000000..abb5a1b
--- /dev/null
+++ b/std/player/combat.c
@@ -0,0 +1,217 @@
+// MorgenGrauen MUDlib
+//
+// player/combat.c -- combat statistics
+//
+// $Id: combat.c 9008 2015-01-06 17:20:17Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/combat";
+inherit "/std/player/pklog";
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <combat.h>
+#include <new_skills.h>
+
+#define ME this_object()
+#define STATMASTER "/p/service/rochus/guildstat/master"
+
+private nosave closure mod_def_stat;
+private nosave string *plAttacked = ({});
+
+protected void create() {
+  combat::create();
+  // P_NO_ATTACK ist nicht fuer Spieler gedacht. Ausnahme: Spieler ist
+  // Geist, dann setzt das Spielerobjekt aber selber.
+  Set(P_NO_ATTACK, SECURED|NOSETMETHOD, F_MODE_AS);
+  
+  Set(P_HELPER_NPC, PROTECTED, F_MODE);
+  SetProp(P_HELPER_NPC, ([]) );
+}
+
+// Maske fuer alle moeglichen Klassen von Helfer-NPC
+#define CLASS_MASK 0x1fffffff
+/** registriert den NPC als Helfer von diesem Spieler.
+  @param[in] npc object Helfer-NPC
+  @param[in] flags int Bitfeld von Flags
+  @return int 1, falls der Helfer-NPC registriert wurde und noch nicht
+  registriert war.
+  @attention Nutzt aus, dass QueryProp(P_HELPER_NPC) _keine_ Kopie des
+  Mappings in der Prop liefert.
+  */
+public int RegisterHelperNPC(object npc, int flags) {
+  if (!objectp(npc))
+    raise_error(sprintf( "Wrong argument 1 in RegisterHelperNPC(). "
+          "Expected <object>, got %.100O\n", npc));
+  if (!intp(flags) || flags < 1)
+     raise_error(sprintf( "Wrong argument 2 in RegisterHelperNPC(). "
+          "Expected positive <int>, got %O\n", flags));
+  
+  mapping helpers = QueryProp(P_HELPER_NPC);
+  
+  // schon registrierte sind witzlos.
+  if (member(helpers, npc))
+    return 0;
+  
+  // auf exklusive Helfer pruefen.
+  foreach(object helper, int fl: helpers) {
+    // flags identisch? Dann Klasse und Exklusivitaet identisch
+    if (fl == flags)
+      return 0;
+    // oder einer von beiden exklusiv und beide in der gleichen Klasse?
+    else if ( ((fl & EXCLUSIVE_HELPER) || (flags & EXCLUSIVE_HELPER))
+              && ((fl & CLASS_MASK) == (flags & CLASS_MASK)) )
+      return 0;
+  }
+  // scheint wohl ok zu sein. Registrieren und Prop im NPC setzen.
+  helpers += ([ npc: flags ]);
+  npc->SetProp(P_HELPER_NPC, ({ this_object(), flags }) );
+  // momentan unnoetig, da helpers keine Kopie ist.
+  // SetProp(P_HELPER_NPC, helpers);
+
+  return 1;
+}
+#undef CLASS_MASK
+
+/** de-registriert den NPC als Helfer von diesem Spieler.
+  @param[in] npc object Helfer-NPC
+  @return int 1, falls der Helfer-NPC registriert war und entfernt wurde.
+  @attention Nutzt aus, dass QueryProp(P_HELPER_NPC) _keine_ Kopie des
+  Mappings in der Prop liefert.
+ */
+public int UnregisterHelperNPC(object npc) {
+  if (!objectp(npc))
+    raise_error(sprintf("Wrong argument in UnregisterHelpernNPC(). "
+          "Expected <object>, got %.100O\n", npc));
+
+  mapping helpers = QueryProp(P_HELPER_NPC);
+  if (member(helpers, npc)) {
+    m_delete(helpers, npc);
+    // momentan unnoetig, da helpers keine Kopie ist.
+    // SetProp(P_HELPER_NPC, helpers);
+    npc->SetProp(P_HELPER_NPC, 0);
+    return 1;
+  }
+  return 0;
+}
+
+/** Feind eintragen.
+  * Traegt ob als Feind ein. Dies allerdings nur, wenn ob kein Spieler ist
+  * oder beide Spieler (dieses Objekt und ob) in der Schattenwelt sind oder
+  * beide Spieler Testspieler sind.
+   @param[in] ob potentieller Feind.
+   @return int 1, falls ob als _neuer_ Feind eingetragen wurde.
+   */
+public int InsertEnemy(object ob) {
+  // wenn ob ein Spieler ist und nicht sowohl ich als auch ob in der
+  // Schattenwelt sind, wird ob nicht als Feind eingetragen.
+  if (query_once_interactive(ob)
+      && (strstr(object_name(environment(ob)),"/d/schattenwelt/")!=0
+          || strstr(object_name(environment(ME)),"/d/schattenwelt/")!=0)
+      && (!QueryProp(P_TESTPLAYER) || !ob->QueryProp(P_TESTPLAYER))
+     )
+  {
+    return 0;
+  }
+  return ::InsertEnemy(ob);
+}
+
+/** Hat dieser Spieler den Spieler pl angegriffen?.
+  @param[in] pl object zu pruefender Spieler
+  @return int 1, falls dieser Spieler pl angegriffen hat.
+  @attention Nebeneffekt: bereinigt den internen Speicher von UIDs von
+  zerstoerten Spielern und solchen, die keine Feinde mehr sind.
+  */
+public int QueryPlAttacked(object pl) {
+  object ob;
+
+  if ( !objectp(pl) )
+    return 0;
+
+  foreach(string plname: plAttacked) {
+    if ( !( ob=(find_player(plname)||find_netdead(plname)) )
+        || ( !IsEnemy(ob) && !(ob->IsEnemy(ME)) ) )
+      plAttacked -= ({plname}); // ja, das geht. ;-)
+  }
+  return (member( plAttacked, getuid(pl) ) >= 0);
+}
+
+/** kill - Kampf starten.
+ * Fuegt ob der Feindesliste hinzu.
+ */
+public int Kill(object ob) {
+
+  if (!objectp(ob)) return 0;
+
+  // dies dient nur dazu, das plAttacked mapping zu bereinigen.
+  // TODO: besser machen. ;-)
+  if ( query_once_interactive(ob) && !IsEnemy(ob)) {
+    QueryPlAttacked(ME);
+    ob->QueryPlAttacked(ob); // aktualisieren ...
+  }
+
+  int res = combat::Kill(ob);
+
+  // falls ob nen Spieler ist, pruefen, ob es ein Spieler-Spieler-Angriff
+  // ist.
+  // Dabei ggf. loggen und Magier verstaendigen.
+  if (query_once_interactive(ob) && CheckPlayerAttack(ME, ob, 0))
+  {
+    if (res == -4) // feind wurde nicht eingetragen
+      tell_object(ME, "Ein goettlicher Befehl hindert Dich am Kampf.\n");
+    else
+      plAttacked += ({ getuid(ob) });
+  }
+
+  return res;
+}
+
+public int Defend(int dam, string|string* dam_type, int|mapping spell, object enemy) {
+  int delta_hp,res;
+
+  if (query_once_interactive(ME)
+      && !IS_LEARNER(ME)
+      && !objectp(get_type_info(mod_def_stat,2))) {
+    object ma;
+    if (!objectp(ma=find_object(STATMASTER)))
+      return ::Defend(dam,dam_type,spell,enemy);
+    // Statistik nur aufrufen falls Master geladen
+    mod_def_stat=symbol_function("ModifyDefendStat",ma);
+  }
+
+  if (closurep(mod_def_stat))
+    delta_hp=QueryProp(P_HP);
+
+  res=::Defend(dam,dam_type,spell,enemy);
+
+  if (closurep(mod_def_stat)) {
+    delta_hp-=QueryProp(P_HP);
+    if (delta_hp<0)
+      delta_hp=0;
+    funcall(mod_def_stat,
+            QueryProp(P_GUILD),
+            QueryProp(P_GUILD_LEVEL),
+            dam-10*delta_hp,
+            dam_type,
+            spell);
+  }
+
+  return res;
+}
+
+// Spieler koennen als Geist nicht kämpfen
+// TODO: pruefen, ob das Setzen und Loeschen der Prop in set_ghost() nicht
+// auch ausreichen wuerde. In dem Fall muesste man aber P_NO_ATTACK auch
+// speichern, da P_GHOST gespeichert wird...
+static mixed _query_no_attack()
+{
+    if ( QueryProp(P_GHOST) )
+        return 1;
+
+    return Query(P_NO_ATTACK);
+}
diff --git a/std/player/comm.c b/std/player/comm.c
new file mode 100644
index 0000000..f91f1de
--- /dev/null
+++ b/std/player/comm.c
@@ -0,0 +1,1922 @@
+// MorgenGrauen MUDlib
+//
+// player/comm.c-- basic player communiction commands
+//
+// $Id: comm.c 9576 2016-06-18 15:00:01Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+//#pragma range_check
+
+inherit "/std/living/comm";
+inherit "/std/player/channel";
+inherit "/std/player/comm_structs";
+
+#include <input_to.h>
+
+#define NEED_PROTOTYPES
+#include <player/quest.h>
+#include <player/gmcp.h>
+#include <living/description.h>
+#undef NEED_PROTOTYPES
+
+#include <sys_debug.h>
+
+#include <thing/properties.h>
+#include <player/comm.h>
+#include <player/base.h>
+
+#include <properties.h>
+#include <config.h>
+#include <ansi.h>
+#include <wizlevels.h>
+#include <language.h>
+#include <udp.h>
+#include <defines.h>
+#include <daemon.h>
+#include <strings.h>
+#include <regexp.h>
+#include <interactive_info.h>
+
+#define TELLHIST_DISABLED   0
+#define TELLHIST_NO_MESSAGE 1
+#define TELLHIST_ENABLED    2
+#define TELLHIST_LONGLIFE   3
+
+#define ECHO_COST 50
+#define ERWIDER_PARAM ","
+
+#define ZDEBUG(x)  if (find_player("zesstra"))\
+              efun::tell_object(find_player("zesstra"),"CommDBG: "+x+"\n")
+
+private int tell_history_enabled = TELLHIST_NO_MESSAGE;
+private nosave mapping tell_history=([]);
+private nosave string *commreceivers = ({});
+private nosave string last_comm_partner;
+private nosave int last_beep_time;
+
+// Statusreporte aktiviert? Binaere Flags (s. set_report())
+private int stat_reports;
+// interner Cache fuer die LP/KP/Gift-Werte fuer die Statusreport-Ausgaben
+// Eintraege (in dieser Reihenfolge): P_HP, P_SP, Giftstatus
+// Initialisierung erfolgt beim ersten Report nach Login
+private nosave mixed *report_cache;
+
+// Puffer fuer Kobold.
+private nosave struct msg_buffer_s kobold = (<msg_buffer_s>
+                                             buf: allocate(32),
+                                             index: -1,);
+#define MAX_KOBOLD_LIMIT 256
+
+varargs string name(int casus, int demonst);
+
+//local property prototypes
+static int _query_intermud();
+public int RemoveIgnore(string ign);
+public int AddIgnore(string ign);
+
+public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
+                              string msg_prefix, object origin);
+
+// erzeugt sortierte Liste an Kommunikationspartnern
+private string *sorted_commpartners(int reversed);
+
+private nosave string *buffer = ({});
+
+void create()
+{
+  ::create();
+  Set(P_EARMUFFS, 0);
+  Set(P_EARMUFFS, SAVE, F_MODE);
+  Set(P_EARMUFFS, SECURED, F_MODE);
+  Set(P_INTERMUD, SAVE, F_MODE);
+  Set(P_IGNORE, ([]), F_VALUE);
+  Set(P_IGNORE, SAVE, F_MODE);
+  Set(P_BUFFER, SAVE, F_MODE);
+  Set(P_MESSAGE_PREPEND, SAVE, F_MODE_AS);
+  Set(P_MESSAGE_BEEP, SAVE, F_MODE_AS);
+}
+
+void create_super()
+{
+  set_next_reset(-1);
+}
+
+// uebermittelt eine MT_NOTIFICATION an this_object(), welche nicht ignoriert
+// werden kann und auch nicht gespeichert wird.
+protected void _notify(string msg, string action) {
+  ReceiveMsg(msg,
+             MT_NOTIFICATION|MSG_DONT_BUFFER|MSG_DONT_IGNORE|MSG_DONT_STORE,
+             action, 0, this_object());
+}
+
+protected void updates_after_restore(int newflag) {
+  // Altes Ignoriere loeschen...
+  mixed ign = Query(P_IGNORE,F_VALUE);
+  if (!mappingp(ign))
+  {
+    if (pointerp(ign))
+      _notify(break_string(
+        "Deine Ignoriere-Einstellungen wurden soeben geloescht, "
+        "weil es eine Aktualisierung der Ignorierefunktion gab, "
+        "bei der eine Konversion der Daten leider nicht "
+        "moeglich war.",78), 0);
+
+    Set(P_IGNORE, ([]), F_VALUE);
+  }
+}
+
+static int set_report(string str) {
+  int canflags = QueryProp(P_CAN_FLAGS);
+
+  if(!str)
+  {
+    if (stat_reports) {
+    string *res=({});
+    if (stat_reports & DO_REPORT_HP)
+      res+=({"Lebenspunkte"});
+    if (stat_reports & DO_REPORT_SP)
+      res+=({"Konzentrationspunkte"});
+    if (stat_reports & DO_REPORT_POISON)
+      res+=({"Vergiftungen"});
+    if (stat_reports & DO_REPORT_WIMPY)
+      res+=({"Vorsicht"});
+
+    tell_object(ME,break_string(
+        "Dir werden jetzt Veraenderungen Deiner "
+        +CountUp(res) + " berichtet.",78));
+    }
+    else
+      tell_object(ME,
+        "Alle Statusreports sind ausgeschaltet.\n");
+
+    return 1;
+  }
+  else if (str == "aus") {
+    if (stat_reports & DO_REPORT_HP || stat_reports & DO_REPORT_WIMPY) {
+      string s="";
+      if (stat_reports & DO_REPORT_HP) {
+        str="ebenfalls ";
+        tell_object(ME, "Der Report wurde ausgeschaltet.\n");
+      }
+      if ( stat_reports & DO_REPORT_WIMPY ) {
+        tell_object(ME, "Der Vorsicht-Report wurde "+s+
+          "ausgeschaltet.\n");
+      }
+      stat_reports=0;
+    }
+    else {
+      tell_object(ME, "Der Report ist bereits ausgeschaltet.\n");
+    }
+    return 1;
+  }
+  else if (str == "ein") {
+    if ( stat_reports & DO_REPORT_HP ) {
+      tell_object(ME, "Der Report ist bereits eingeschaltet.\n");
+      return 1;
+    }
+    tell_object(ME, "Der Report wurde eingeschaltet.\n");
+    stat_reports |= DO_REPORT_HP;
+    if (!(canflags & CAN_REPORT_SP)) {
+      if (QueryQuest("Hilf den Gnarfen")==1) {
+        SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_SP);
+        stat_reports |= DO_REPORT_SP;
+      }
+      else {
+        tell_object(ME, break_string(
+          "Fuer den Statusreport Deiner Konzentration musst Du jedoch "
+          "zunaechst die Quest \"Hilf den Gnarfen\" bestehen.",78));
+      }
+    }
+    else {
+      stat_reports |= DO_REPORT_SP;
+    }
+    if (!(canflags & CAN_REPORT_POISON)) {
+      if (QueryQuest("Katzenjammer")==1) {
+        SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_POISON);
+        stat_reports |= DO_REPORT_POISON;
+      }
+      else {
+        tell_object(ME, break_string(
+          "Fuer den Statusreport Deiner Vergiftung musst Du jedoch "
+          "zunaechst die Quest \"Katzenjammer\" bestehen.",78));
+      }
+    }
+    else {
+      stat_reports |= DO_REPORT_POISON;
+    }
+    // Cache loeschen, damit beim naechsten Report-Event alle Daten neu
+    // eingetragen werden muessen. Muss beim Einschalten des Reports
+    // passieren, weil auch in der inaktiven Zeit weiterhin Aenderungen in
+    // status_report() eingehen, so dass der Cache zwar erst einmal leer ist,
+    // aber beim Wiedereinschalten nicht mehr ungueltig waere und somit
+    // veraltete Daten an den Spieler ausgegeben werden. Im unguenstigsten
+    // Fall wuerde das sogar dazu fuehren, dass die veralteten Daten lange
+    // Zeit nicht aktualisiert werden, wenn z.B. P_HP == P_MAX_HP, so dass
+    // kein P_HP-Event mehr eingeht.
+    report_cache=0;
+  }
+  else if (str == "vorsicht") {
+    if (!(canflags & CAN_REPORT_WIMPY)) {
+      if (QueryQuest("Schrat kann nicht einschlafen")==1) {
+        SetProp(P_CAN_FLAGS, canflags | CAN_REPORT_WIMPY);
+        tell_object(ME, "Der Vorsicht-Report wurde eingeschaltet.\n");
+        stat_reports |= DO_REPORT_WIMPY;
+      }
+      else {
+        tell_object(ME, break_string(
+          "Fuer den Statusreport Deiner Vorsicht musst Du "
+          "zunaechst die Quest \"Schrat kann nicht einschlafen\" "
+          "bestehen.",78));
+      }
+    }
+    else
+    {
+      stat_reports |= DO_REPORT_WIMPY;
+    }
+    // fuer Seher auch Bericht der Fluchtrichtung einschalten.
+    if ((stat_reports & DO_REPORT_WIMPY)
+        && !(stat_reports & DO_REPORT_WIMPY_DIR)
+        && ((canflags & CAN_REPORT_WIMPY) || IS_SEER(ME)))
+    {
+      stat_reports |= DO_REPORT_WIMPY_DIR;        
+    }
+  }
+  // sendet einmalig genau jetzt den konfigurierten report. Kann zum testen
+  // (von Triggern) oder beim Login benutzt werden, wenn man einen initialen
+  // Datenbestand erhalten will.
+  else if (str=="senden")
+  {
+    // Es wird Ausgabe von LP und Vorsicht getriggert, das sendet beide
+    // Zeilen.
+    status_report(DO_REPORT_HP, QueryProp(P_HP));
+    status_report(DO_REPORT_WIMPY, QueryProp(P_WIMPY));
+    return 1;
+  }
+  else 
+    return 0;
+  // nur aktuellen Zustand berichten
+  set_report(0);
+  return 1;
+}
+
+private string get_poison_desc(int p) {
+  string ret;
+  if ( intp(p) ) {
+    switch(p) {
+      case 0:    ret="keins";       break;
+      case 1..3: ret="leicht";      break;
+      case 4..8: ret="gefaehrlich"; break;
+      default:   ret="sehr ernst";  break;
+    }
+    return ret;
+  }
+  else return "(nicht verfuegbar)";
+}
+
+// sprintf()-Formatstrings fuer die Reportausgabe.
+#define REPORTLINE "LP: %3d, KP: %3s, Gift: %s.\n"
+#define REPORTLINE_WIMPY "Vorsicht: %d, Fluchtrichtung: %s.\n"
+// Defines zur Adressierung der Cache-Eintraege
+#define REP_HP     0
+#define REP_SP     1
+#define REP_POISON 2
+
+protected void status_report(int type, mixed val) {
+  // Wenn der Spieler GMCP hat und das sich um die Information kuemmert,
+  // erfolgt keine textuelle Ausgabe mehr. Daher return, wenn GMCP_Char()
+  // erfolg vermeldet hat.
+  int flags = QueryProp(P_CAN_FLAGS);
+  switch (type) {
+    case DO_REPORT_HP:
+      if (GMCP_Char( ([ P_HP: val ]) ) ) return;
+      break;
+    case DO_REPORT_SP:
+      if (!(flags & CAN_REPORT_SP)) return;
+      if (GMCP_Char( ([ P_SP: val ]) ) ) return;
+      break;
+    case DO_REPORT_POISON:
+       if (!(flags & CAN_REPORT_POISON)) return;
+       if (GMCP_Char( ([ P_POISON: val ]) ) ) return;
+      break;
+    case DO_REPORT_WIMPY:
+      if (!(flags & CAN_REPORT_WIMPY)) return;
+      if (GMCP_Char( ([ P_WIMPY: val ]) ) ) return;
+      break;
+    case DO_REPORT_WIMPY_DIR:
+      if (!(flags & CAN_REPORT_WIMPY_DIR)) return;
+      if (GMCP_Char( ([ P_WIMPY_DIRECTION: val ]) ) ) return;
+      break;
+  }
+
+  // konventionelle textuelle Ausgabe des Reports ab hier.
+  if (!(type & stat_reports))
+    return;
+
+  if ( !report_cache ) {
+    report_cache = ({
+      QueryProp(P_HP),
+      (stat_reports&DO_REPORT_SP) ? to_string(QueryProp(P_SP)) : "###",
+      (stat_reports&DO_REPORT_POISON) ?
+          get_poison_desc(QueryProp(P_POISON)) : "(nicht verfuegbar)"
+    });
+  }
+
+  switch(type) {
+      // LP berichten: Cache aktualisieren und Meldung ausgeben.
+      case DO_REPORT_HP:
+        report_cache[REP_HP]=val;
+        tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
+          report_cache[REP_SP], report_cache[REP_POISON]));
+        break;
+      // KP berichten: Wenn der Spieler den Report freigeschaltet hat,
+      // wird bei Aenderungen gemeldet. Wenn nicht, aendert sich nur der
+      // Cache-Eintrag. So wird verhindert, dass ein Spieler ueber KP-
+      // Veraenderungen auch dann informiert wuerde, wenn er den KP-Report
+      // gar nicht benutzen koennte.
+      case DO_REPORT_SP:
+        report_cache[REP_SP]=to_string(val);
+        tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
+          report_cache[REP_SP], report_cache[REP_POISON]));
+        break;
+      // Giftstatus berichten: Wenn der Giftreport freigeschaltet ist,
+      // Cache aktualisieren und berichten. Wenn nicht, aendert sich nur
+      // der Cache-Eintrag. Erlaeuterung hierzu s.o. beim KP-Report.
+      case DO_REPORT_POISON:
+        report_cache[REP_POISON] = get_poison_desc(val);
+        tell_object(ME, sprintf(REPORTLINE, report_cache[REP_HP],
+          report_cache[REP_SP], report_cache[REP_POISON]));
+        break;
+      // Vorsicht-Report: kann ohne weitere Abfragen ausgegeben werden, da
+      // alle noetigen Checks schon zu Beginn dieser Funktion erledigt wurden.
+      // Lediglich der Inhalt der Meldung muss abhaengig vom Seherstatus
+      // konfiguriert werden.
+      case DO_REPORT_WIMPY:
+        string res;
+        if (IS_SEER(ME)) {
+          // QueryProp() aus Kostengruenden im if(), damit die Aufruf-
+          // Haeufigkeit zumindest ein wenig reduziert wird.
+          string dir = QueryProp(P_WIMPY_DIRECTION)||"keine";
+          res = sprintf(REPORTLINE_WIMPY, val, dir);
+        }
+        else
+          res = sprintf(REPORTLINE_WIMPY, val, "(nicht verfuegbar)");
+        tell_object(ME, res);
+        break;
+      // Fluchtrichtungs-Report: wird nur bei Sehern ausgegeben, damit
+      // nicht auch Spieler eine VS-/FR-Meldung bekommen, wenn z.B. eine
+      // externe Manipulation der Fluchtrichtung stattfindet, sie aber den
+      // Report mangels Seherstatus gar nicht freigeschaltet haben.
+      case DO_REPORT_WIMPY_DIR:
+        if (IS_SEER(ME)) {
+          if (!val) val = "keine";
+          tell_object(ME,sprintf(REPORTLINE_WIMPY, QueryProp(P_WIMPY), val));
+        }
+        break;
+  }
+}
+
+#undef REPORTLINE
+#undef REPORTLINE_WIMPY
+#undef REP_HP
+#undef REP_SP
+#undef REP_POISON
+
+private string permutate(string msg)
+{
+  // Kontrollzeichen rausfiltern. *seufz*
+  msg = regreplace(msg,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
+  object ob=QueryProp(P_PERM_STRING);
+  if (!objectp(ob))
+    return msg;
+
+  return (string)ob->permutate_string(msg)||"";
+}
+
+// neue nachricht an den Kobold anhaengen
+// Rueckgabewerte: MSG_BUFFER_FULL oder MSG_BUFFERED
+private int add_to_kobold(string msg, int msg_type, string msg_action,
+                          string msg_prefix, object origin)
+{
+  // Nachricht soll im Kobold gespeichert werden.
+  // Kobold speichert Rohdaten und gibt spaeter das ganze auch wieder via
+  // ReceiveMsg() aus - dabei wird MSG_DONT_BUFFER | MSG_DONT_STORE gesetz,
+  // damit keine erneute Speicher in Kobold oder Komm-History erfolgt.
+
+  // wenn der Puffer zu klein ist, Groesse verdoppeln, wenn noch unterhalb
+  // des Limits.
+  if (kobold->index >= sizeof(kobold->buf)-1) {
+    if (sizeof(kobold->buf) < MAX_KOBOLD_LIMIT)
+      kobold->buf += allocate(sizeof(kobold->buf));
+    else
+      return MSG_BUFFER_FULL;
+  }
+  kobold->index = kobold->index +1;
+  // neue Nachricht an den Puffer anhaengen.
+  string sendername = query_once_interactive(origin) ?
+                      origin->query_real_name() :
+                      origin->name(WER) || "<Unbekannt>";
+  kobold->buf[kobold->index] = (<msg_s> msg: msg,
+      type : msg_type, action : msg_action, prefix : msg_prefix,
+      sendername : sendername);
+  return MSG_BUFFERED;
+}
+
+private void _flush_cache(int verbose) {
+  // nur mit genug Evalticks ausgeben.
+  if (get_eval_cost() < 100000) return;
+  if (kobold->index >= 0)
+  {
+    ReceiveMsg("Ein kleiner Kobold teilt Dir folgendes mit:",
+               MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
+               0, 0, this_object());
+    int prepend = QueryProp(P_MESSAGE_PREPEND);
+    foreach(int i: 0 .. kobold->index) // '0 ..' ist wichtig!
+    {
+      struct msg_s msg = kobold->buf[i];
+      // dies ist dient der Fehlerabsicherung, falls es nen Fehler (z.B. TLE)
+      // in der Schleife unten gab: dann ist index nicht auf -1 gesetzt
+      // worden, aber einige Nachrichten sind schon geloescht.
+      if (!structp(msg)) continue;
+      // Ausgabe via efun::tell_object(), weil die Arbeit von ReceiveMsg()
+      // schon getan wurde. Allerdings muessen wir uns noch um den UMbruch
+      // kuemmern.
+      if ((msg->type) & MSG_DONT_WRAP)
+        msg->msg = (msg->prefix ? msg->prefix : "") + msg->msg;
+      else
+      {
+        int bsflags = msg->type & MSG_ALL_BS_FLAGS;
+        if (prepend)
+          bsflags |= BS_PREPEND_INDENT;
+        msg->msg = break_string(msg->msg, 78, msg->prefix, bsflags);
+      }
+      efun::tell_object(this_object(), msg->msg);
+      kobold->buf[i]=0;
+    }
+    kobold->index=-1;
+  }
+  else if (verbose)
+  {
+    ReceiveMsg("Der kleine Kobold hat leider nichts Neues fuer Dich.",
+               MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_BUFFER,
+               0, 0, this_object());
+  }
+}
+
+varargs int cmd_kobold(string arg)
+{
+  switch(arg)
+  {
+    case "ein":
+      SetProp(P_BUFFER, 1);
+      printf("Der Kobold merkt sich jetzt alles!\n"); break;
+    case "aus":
+      SetProp(P_BUFFER, 0);
+      printf("Der Kobold wird Dich nicht stoeren!\n"); break;
+    default: if(arg) printf("Der Kobold sagt: kobold ein oder kobold aus\n");
+  }
+  _flush_cache(1);
+  return 1;
+}
+
+public int TestIgnoreSimple(string *arg)
+{   string *ignore;
+
+    if (!pointerp(arg) || !mappingp(ignore=Query(P_IGNORE,F_VALUE)))
+        return 0;
+    foreach(string s: arg)
+    {
+      if (member(ignore,s))
+        return 1;
+    }
+    return 0;
+}
+
+//TODO: deprecated - entfernen, wenn Message() entfernt wird.
+private int check_ignore(mixed ignore, string verb, string name)
+{
+  if (ignore == verb)
+    return 1;
+  ignore = explode(ignore, ".");
+  return ((sizeof(ignore) > 1) &&
+     (name == ignore[0] && member(ignore[1..], verb) != -1));
+}
+
+private int comm_beep() {
+  if (QueryProp(P_VISUALBELL)) return 0; // kein ton
+  int beep_interval=(int)QueryProp(P_MESSAGE_BEEP);
+  if (!beep_interval || ((time()-last_beep_time) < beep_interval)) return 0;
+  last_beep_time=time();
+  return 1;
+}
+
+private varargs void add_to_tell_history( string uid, int sent, int recv,
+                                 string message, string indent, int flags )
+{
+  /* tell_history ist ein Mapping mit UIDs der Gespraechspartner als Key.
+     Als Wert ist eine Strukur vom Typ chat_s eingetragen.
+     Strukturen chat_s und stored_msg_s sind in /std/player/comm_structs.c
+     definiert.
+     TODO fuer spaeter, gerade keine Zeit fuer:
+     Als Wert ist ein Array von chat_s enthalten, wobei das 0. Element das
+     jeweils juengste Gespraech mit diesem Gespraechspartner ist und alle
+     weiteren Elemente in der zeitlichen Reihenfolge kommen (also letztes
+     Element ist aeltestes Gespraech).
+     */
+
+  //TODO: Entfernen, wenn das nicht mehr passiert.
+  if (!stringp(uid))
+  {
+    ReceiveMsg(sprintf(
+      "\nadd_to_tell_history(): got bad uid argument %O."
+      "sent: %d, recv: %d, flags: %d, msg: %s", 
+      uid, sent, recv, flags, message),MT_DEBUG|MSG_BS_LEAVE_LFS,0,0,ME);              
+  }
+  
+  // letzten Gespraechspartner fuer erwidere.
+  if (!(flags & MSGFLAG_REMOTE))
+    last_comm_partner = uid;
+
+  // ist ein sortiertes Array von max. MAX_SAVED_CHATS Groesse, welches die
+  // Spieler enthaelt, denen man schon was mitgeteilt hat. Aktuellste am
+  // Anfang.
+  if (sent) {
+    if (!sizeof(commreceivers))
+      commreceivers = ({uid});
+    else if (commreceivers[0] != uid) {
+      // nur wenn der aktuelle Partner nicht am Anfang steht, muss man hier was
+      // tun. Comm-Partner an den Anfang stellen und ggf. alten Eintrag
+      // entfernen.
+      // TODO: Effizienter gestalten.
+      commreceivers = ({uid}) + (commreceivers-({uid}));
+      // ggf. kuerzen. (wenn !tell_history_enabled, wird es ggf. unten
+      // gemacht, denn die Hist muss min. alle UID enthalten, die auch in
+      // commreceivers drin sind.)
+      if (!tell_history_enabled && sizeof(commreceivers) > MAX_SAVED_CHATS)
+        commreceivers = commreceivers[0..MAX_SAVED_CHATS];
+    }
+  }
+
+  if (!tell_history_enabled)
+    return;
+
+  if (!indent && message[<1] == 10)
+      message = message[..<2];
+
+  struct chat_s chat;
+  // Gespraechspartner unbekannt?
+  if (!member(tell_history, uid)) {
+    // zuviele Gespraeche in Hist? >= ist Absicht weil ja gleich noch eins
+    // dazu kommt.
+    if (sizeof(tell_history) >= MAX_SAVED_CHATS) {
+      string deluid;
+      int zeit = __INT_MAX__;
+      foreach(string tuid, chat : tell_history) {
+        // aeltestes Gespraech suchen
+        if (zeit > chat->time_last_msg) {
+          deluid = tuid;
+          zeit = chat->time_last_msg;
+        }
+      }
+      // aeltestes Gespraech raus.
+      m_delete(tell_history, deluid);
+      if (member(commreceivers,deluid)>-1)
+        commreceivers-=({deluid});
+    }
+    // neues Gespraech anlegen
+    chat = (<chat_s> uid: uid, time_first_msg: time(), 
+               time_last_msg: time(),
+         sentcount: sent, recvcount: recv,
+         msgbuf: 0, ptr: 0 );
+    tell_history[uid] = chat;
+  }
+  else {
+    // Gespraechspartner bekannt, altes Gespraech weiterbenutzen
+    chat = tell_history[uid];
+    chat->time_last_msg = time();
+    chat->sentcount += sent;
+    chat->recvcount += recv;
+  }
+
+  if (tell_history_enabled < TELLHIST_ENABLED)
+    return;
+
+  // ggf. Array fuer Messages anlegen
+  if (!pointerp(chat->msgbuf))
+    chat->msgbuf = allocate(MAX_SAVED_MESSAGES);
+
+  // Message-Struktur ermitteln oder neu anlegen
+  struct stored_msg_s msg;
+  if (!structp(chat->msgbuf[chat->ptr])) {
+    // neue Struct ins Array schreiben
+    chat->msgbuf[chat->ptr] = msg = (<stored_msg_s>);
+  }
+  else {
+    // alte Struct ueberschreiben
+    msg = chat->msgbuf[chat->ptr];
+  }
+  // Index auf naechste Messagestruktur ermitteln
+  chat->ptr = (chat->ptr + 1) % MAX_SAVED_MESSAGES;
+  // Message speichern
+  msg->msg = message;
+  msg->prefix = indent;
+  msg->timestamp = time();
+}
+
+protected void clear_tell_history()
+{
+  /* Nach einem "schlafe ein" werden die gespeicherten Mitteilungen geloescht,
+     sofern der Spieler nichts abweichendes eingestellt hat. */
+
+#ifdef TELLHIST_LONGLIFE
+  if (tell_history_enabled == TELLHIST_LONGLIFE)
+    return;
+#endif
+
+  foreach (string uid, struct chat_s chat: tell_history)
+    if (pointerp(chat->msgbuf)) {
+      chat->msgbuf = 0;
+      chat->ptr = 0;
+    }
+}
+
+protected void reset(void)
+{
+  /* Wird 15 Minuten nach dem Verlust der Verbindung aufgerufen. Falls der
+     Spieler nicht inzwischen eine Verbindung wiederhergestellt hat, werden
+     wie bei einem "schlafe ein" die Mitteilungen geloescht. */
+
+  if (!interactive())
+    clear_tell_history();
+
+}
+
+// gerufen, wenn zielgerichtet mit jemandem kommuniziert wird _und_ das
+// Ergebnis des ReceiveMsg() geprueft werden und eine Meldung ausgegeben
+// werden soll.
+private void _send(object ob, string msg, int msg_type,
+                   string msg_action, string msg_prefix)
+{
+  int res = ob->ReceiveMsg(msg, msg_type, msg_action, msg_prefix, ME);
+  switch(res) {
+    case MSG_DELIVERED:
+      break;  // nix machen
+    case MSG_BUFFERED:
+      ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden."
+          "Die Mitteilung wurde von einem kleinen Kobold in Empfang "
+          "genommen. Er wird sie spaeter weiterleiten!",
+          MT_NOTIFICATION, msg_action, 0, this_object());
+      break;
+    case MSG_IGNORED:
+    case MSG_VERB_IGN:
+    case MSG_MUD_IGN:
+      ReceiveMsg(ob->Name(WER) + " hoert gar nicht zu, was Du sagst.",
+           MT_NOTIFICATION, msg_action, 0, this_object());
+      break;
+    case MSG_SENSE_BLOCK:
+      ReceiveMsg(ob->Name(WER) + " kann Dich leider nicht wahrnehmen.",
+          MT_NOTIFICATION, msg_action, 0, this_object());
+      break;
+    case MSG_BUFFER_FULL:
+      ReceiveMsg(ob->Name(WER) + " moechte gerade nicht gestoert werden."
+          "Die Mitteilung ging verloren, denn der Kobold kann sich "
+          "nichts mehr merken!", MT_NOTIFICATION, msg_action, 
+          0, this_object());
+      break;
+    default:
+      ReceiveMsg(ob->Name(WER) + " hat Deine Nachricht leider nicht "
+          "mitbekommen.", MT_NOTIFICATION, msg_action, 0, this_object());
+      break;
+  }
+}
+
+// Ausgabe an das Objekt selber und Aufzeichnung in der Kommhistory, falls
+// noetig. Wird bei _ausgehenden_ Nachrichten im eigenen Objekt gerufen, damit
+// die Nachricht ggf. in den Kommhistory erfasst wird.
+// TODO: entfernen, wenn alles Aufrufer ersetzt sind durch ReceiveMsg().
+protected varargs int _recv(object ob, string message, int flag, string indent)
+{
+  write(break_string(message, 78, indent,
+        QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0));
+  if ((flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE) &&
+      query_once_interactive(ob))
+  {
+    if (flag & MSGFLAG_WHISPER)
+      add_to_tell_history(getuid(ob), 1, 0,
+        "Du fluesterst " + ob->name(WEM) + " aus der Ferne etwas zu.", 0,
+        flag);
+    else
+      add_to_tell_history(getuid(ob), 1, 0, message, indent, flag);
+  }
+  return 1;
+}
+
+// <sender> sollte ein Objekt sein. In seltenen Faellen (z.B.
+// Fehlerbehandlung) ist es jedoch auch mal ein String.
+varargs int Message(string msg, int flag, string indent,
+                    string cname, mixed sender)
+{
+  object ti;
+  string verb, reply, *ignore, tin;
+  int em, te;
+  mixed deaf;
+
+  // Bei den Kanaelen 'Debug' und 'Entwicklung' kann man gezielt Bugs
+  // einzelner Magier ignorieren. Dazu wird der Kanalname zum 'verb',
+  // damit 'ignoriere name.debug' funktioniert.
+  if( flag == MSGFLAG_CHANNEL ){
+      if((msg[1..5] == "Debug" || msg[1..11] == "Entwicklung"
+            || msg[1..9]=="Warnungen"))
+      {
+        // Missbrauch der Variable 'ignore' als Zwischenspeicher
+        ignore = regexplode( msg, ":| |\\]" );
+        verb = lower_case(ignore[0][1..]);
+        tin = lower_case(ignore[2]);
+      }
+      else
+      {
+        if(cname)
+          verb=lower_case(cname);
+        else
+          verb=query_verb();
+        if( ti = this_interactive() )
+        {
+          tin = getuid(this_interactive());
+        }
+        else
+        {
+          //falls doch kein Objekt...
+          if (objectp(sender))
+            tin=lower_case(sender->name(RAW)||"<Unbekannt>");
+        }
+      }
+  }
+  else {
+    if( ti = this_interactive() )
+      tin = getuid(this_interactive());
+    verb = query_verb();
+  }
+
+  te = flag & (MSGFLAG_TELL | MSGFLAG_WHISPER);
+
+  // fuer "erwidere"
+  if (ti && (flag & MSGFLAG_TELL || flag & MSGFLAG_REMOTE)) {
+    if (!ti->QueryProp(P_INVIS)||IS_LEARNER(ME)) {
+      if (flag & MSGFLAG_WHISPER)
+        add_to_tell_history(getuid(ti), 0, 1,
+          capitalize((((IS_LEARNER(ti) && !ti->QueryProp(P_INVIS) &&
+            (ti->QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
+            ti->QueryProp(P_PRESAY) : "") + ti->name()) || "") +
+          " fluestert Dir aus der Ferne etwas zu.", 0, flag, 0);
+      else
+        add_to_tell_history(getuid(ti), 0, 1, msg, indent, flag, 0);
+    }
+  }
+  // Hoert der Spieler nicht?
+  em = (ti &&
+        (te || flag & MSGFLAG_SHOUT) &&
+        (QueryProp(P_EARMUFFS) &&
+          (query_wiz_level(ti) < QueryProp(P_EARMUFFS))));
+  ignore = (pointerp(ignore = QueryProp(P_IGNORE)) ? ignore : ({}));
+
+  // Werden der Sender oder das Verb ignoriert?
+  if(!ti && tin && flag == MSGFLAG_CHANNEL)
+  {
+     if((member(ignore, tin) != -1))
+     {
+       return MESSAGE_IGNORE_YOU;
+     }
+     if(verb &&  sizeof(filter(ignore, #'check_ignore, verb, tin)) )
+     {
+       return MESSAGE_IGNORE_YOU;
+     }
+  }
+  if (ti && (member(ignore, getuid(ti)) != -1)) {
+    if(te && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
+      efun::tell_object(ti, capitalize(name())+
+                      " hoert gar nicht zu, was Du sagst.\n");
+    return MESSAGE_IGNORE_YOU;
+  }
+  if(tin && verb &&
+     sizeof(filter(ignore, #'check_ignore/*'*/, verb, tin)))
+  {
+    if(ti && verb[0..2] != "ruf" && verb[0..3] != "mruf" &&
+       verb[0..3] != "echo" && verb[0] != '-' && !(flag & MSGFLAG_CHANNEL) )
+      efun::tell_object(ti, name()+" wehrt \""+verb+"\" ab.\n");
+    return MESSAGE_IGNORE_VERB;
+  }
+  if (flag & MSGFLAG_RTELL) {
+    int at;
+
+    verb = lower_case(old_explode(msg, " ")[0][1..]);
+    at = member(verb, '@');
+    /* verb wird hier eh missbraucht, also auch fuer ein intermud-erwidere*/
+    add_to_tell_history(verb, 0, 1, msg, indent, flag, 0);
+
+    if ((member(ignore, verb) >= 0) || (member(ignore,verb[0..at]) >= 0))
+      return MESSAGE_IGNORE_YOU;
+    else if (at > 0 && member(ignore, verb[at..]) >= 0)
+      return MESSAGE_IGNORE_MUD;
+  }
+
+  // Taubheit/Oropax
+  te |= (flag & MSGFLAG_SAY);
+
+  if (QueryProp(P_DEAF) && (flag & MSGFLAG_DEAFCHK) && !(flag & MSGFLAG_CHIST)) {
+    deaf = QueryProp(P_DEAF);
+    if (te)
+      reply = stringp(deaf) ?
+        capitalize(sprintf(deaf, name())) :
+        capitalize(name())+" ist momentan leider taub.\n";
+  }
+  else if (em)
+    reply = capitalize(name())+" hat Oropax in den Ohren.\n";
+
+  msg = break_string(msg, 78, indent,
+    (QueryProp(P_MESSAGE_PREPEND) ? BS_PREPEND_INDENT : 0) | BS_LEAVE_MY_LFS);
+
+  if(QueryProp(P_BUFFER) &&
+     (deaf ||
+      query_editing(this_object()) ||
+      query_input_pending(this_object())))
+  {
+    deaf = MESSAGE_DEAF;
+    if(flag & MSGFLAG_CACHE)
+    {
+      if(!stringp(reply))
+        reply = name()+" moechte gerade nicht gestoert werden.\n";
+
+      msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
+
+      int res =  add_to_kobold(msg, 0, 0, 0,
+                               objectp(sender) ? sender : ME);
+      if(res == MSG_BUFFERED)
+      {
+
+        reply += "Die Mitteilung wurde von einem kleinen Kobold in Empfang "+
+                 "genommen.\nEr wird sie spaeter weiterleiten!";
+        deaf = MESSAGE_CACHE;
+      }
+      else {
+        reply += "Die Mitteilung ging verloren, denn "+
+                 "der Kobold kann sich nichts mehr merken!";
+        deaf = MESSAGE_CACHE_FULL;
+      }
+      if(ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS)))
+        efun::tell_object(ti, reply+"\n");
+    }
+    return deaf;
+  }
+  else if((deaf || em) &&
+          ( (flag & MSGFLAG_RTELL) ||
+            (ti && (IS_LEARNER(ti)||!QueryProp(P_INVIS))))) {
+    if (te && ti)
+      efun::tell_object(ti, reply);
+    return MESSAGE_DEAF;
+  }
+
+  _flush_cache(0);
+  if(te && QueryProp(P_AWAY))
+    msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
+
+  if (flag & (MSGFLAG_SAY | MSGFLAG_TELL) && comm_beep()) {
+    msg=MESSAGE_BEEP+msg;
+  }
+  efun::tell_object(ME, msg);
+  return MESSAGE_OK;
+}
+
+static int ignoriere(string str)
+{
+  str = _unparsed_args(1);
+  mapping ignore=Query(P_IGNORE, F_VALUE);
+
+  if (!str)
+  {
+    string* ignarr = m_indices(ignore);
+    if (!sizeof(ignarr))
+        tell_object(ME, "Du ignorierst niemanden.\n");
+      else
+        ReceiveMsg("Du ignorierst:\n"
+                   + break_string(CountUp(map(sort_array(ignarr, #'> ),
+                                          #'capitalize ) 
+                                         ) + ".",78),
+                   MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE|MSG_DONT_WRAP,
+                   0,0,this_object());
+      return 1;
+  }
+  // trim spaces from args and convert to lower case.
+  str = lower_case(trim(str, TRIM_BOTH));
+
+  if (member(ignore, str))
+  {
+    RemoveIgnore(str);
+    tell_object(ME, sprintf("Du ignorierst %s nicht mehr.\n", capitalize(str)));
+  }
+  else if (sizeof(ignore)>100)
+  {
+   tell_object(ME, "Du ignorierst schon genuegend!\n");
+  }
+  else if (AddIgnore(str) == 1)
+  {
+    tell_object(ME,
+        sprintf("Du ignorierst jetzt %s.\n", capitalize(str)));
+  }
+  else
+  {
+    tell_object(ME,
+        sprintf("'%s' kannst Du nicht ignorieren.\n",str));
+  }
+  return 1;
+}
+
+
+static int _msg_beep(string str) {
+  int beep_interval;
+  notify_fail("Syntax: klingelton <1 bis 3600 Sekunden> oder klingelton aus\n");
+  if (stringp(str)) {
+    if (str=="aus")
+      SetProp(P_MESSAGE_BEEP,0);
+    else if ((beep_interval=to_int(str)) > 0 && beep_interval<=3600)
+      SetProp(P_MESSAGE_BEEP,beep_interval);
+    else return 0;
+  }
+
+  beep_interval=(int)QueryProp(P_MESSAGE_BEEP);
+  _notify("Ton bei Mitteilungen: "+
+        (beep_interval ? "aller "+beep_interval+" Sekunden." : "aus."),
+        query_verb());
+  return 1;
+}
+
+static int _msg_prepend(string str) {
+  int beep_interval;
+  notify_fail("Syntax: senderwiederholung ein/aus\n");
+  if (stringp(str)) {
+    if (str=="aus")  
+      SetProp(P_MESSAGE_PREPEND,1);
+    else if (str=="ein")  
+      SetProp(P_MESSAGE_PREPEND,0);
+    else return 0;
+  }
+
+  _notify("Senderwiederholung bei Mitteilungen: "+ 
+          ((int)QueryProp(P_MESSAGE_PREPEND) ?  "aus" : "ein")+".",
+          query_verb());
+
+  return 1;
+}
+
+static int _communicate(mixed str, int silent)
+{
+  string  verb;
+  string  myname;
+  string  msg;
+
+  if (!str || extern_call()) str=_unparsed_args()||"";
+  /* str=_unparsed_args()||""; */
+  verb = query_verb();
+  if(stringp(verb) && verb[0] == '\'') str = verb[1..] + " " + str;
+  if (str==""||str==" "||!str)
+  {
+    _notify("Was willst Du sagen?",MA_SAY);
+    return 1;
+  }
+  msg=permutate(str);
+
+  myname=(((QueryProp(P_INVIS)||!IS_LEARNER(ME))||
+     !(QueryProp(P_CAN_FLAGS)&CAN_PRESAY)?
+    "":QueryProp(P_PRESAY))+name())||"";
+
+  // an alles im Raum senden. (MT_LISTEN, weil dies gesprochene Kommunikation
+  // ist, keine MT_COMM)
+  send_room(environment(), msg, MT_LISTEN, MA_SAY,
+            capitalize(myname)+" sagt: ", ({this_object()}) );
+
+  if(!silent)
+  {
+    ReceiveMsg(msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
+               MA_SAY, "Du sagst: ", ME);
+  }
+  return 1;
+}
+
+static int _shout_to_all(mixed str)
+{
+  string pre, myname, realname, wizards_msg, players_msg;
+  string wizard_prefix, player_prefix;
+  int chars;
+
+  if (!(str=_unparsed_args()))
+  {
+    _notify("Was willst Du rufen?",MA_SHOUT);
+    return 1;
+  }
+  chars=sizeof(str)/2;
+  if (chars<4) chars=4;
+  pre = (!IS_LEARNER(ME) ||
+   QueryProp(P_INVIS) ||
+   !(QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ? "" : QueryProp(P_PRESAY);
+  realname = capitalize((pre + capitalize(getuid()))||"");
+  myname = capitalize(pre + name()||"");
+  if (QueryProp(P_INVIS))
+    realname = "("+realname+")";
+
+  wizards_msg = permutate(str);
+  wizard_prefix = myname+" ruft: ";
+
+  if(QueryProp(P_FROG)) {
+    players_msg = "Quaaak, quaaaaak, quuuuaaaaaaaaaaaaaaaaaaaak !!";
+    player_prefix = myname+" quakt: ";
+  }
+  else {
+    players_msg = wizards_msg;
+    player_prefix = wizard_prefix;
+  }
+
+  if(!IS_LEARNER(this_player()))
+  {
+    if(QueryProp(P_GHOST)) {
+        _notify("So ganz ohne Koerper bekommst Du keinen Ton heraus.",
+                MA_SHOUT);
+        return 1;
+    }
+    if (QueryProp(P_SP) <(chars+20))
+    {
+      _notify("Du musst erst wieder magische Kraefte sammeln.",
+              MA_SHOUT);
+      _notify("Tip: Benutz doch mal die Ebenen (Hilfe dazu mit 'hilfe "
+              "Ebenen').", MA_SHOUT);
+      return 1;
+    }
+    SetProp(P_SP, QueryProp(P_SP) - chars - 20);
+  }
+
+  ReceiveMsg(wizards_msg, MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
+             "rufe", "Du rufst: ", ME);
+
+  foreach ( object ob : users()-({this_object()}) )
+    if ( IS_LEARNER(ob) )
+      ob->ReceiveMsg(wizards_msg, MT_LISTEN|MT_FAR, MA_SHOUT, wizard_prefix,
+                    this_object());
+    else
+      ob->ReceiveMsg(players_msg, MT_LISTEN|MT_FAR, MA_SHOUT, player_prefix,
+                    this_object());
+
+  return 1;
+}
+
+varargs int _tell(string who, mixed msg)
+{
+  object    ob;
+  string    away,myname,ret;
+  mixed     ignore,it;
+  string    *xname;
+  int       i,visflag;
+
+  if (extern_call() && this_interactive()!=ME) return 1;
+  if (!who || !msg) {
+    _notify("Was willst Du mitteilen?",MA_TELL);
+    return 1;
+  }
+
+  if(who == ERWIDER_PARAM)
+  {
+    if (!last_comm_partner)
+    {
+      _notify_fail("Du hast aber noch keine Mitteilungen erhalten, auf die "
+          "Du was erwidern\nkoenntest.\n");
+      return 0;
+    }
+    who=last_comm_partner;
+  }
+
+  // teile .x mit teilt bisherigen Gespraechspartnern etwas mit.
+  if (who == ".")
+   who = ".1";
+
+  if ( sscanf(who, ".%d", i) == 1 ) {
+    if(i > 0 && i <= sizeof(commreceivers))
+      who = commreceivers[i-1];
+    else {
+      _notify_fail("So vielen Leuten hast Du noch nichts mitgeteilt!\n");
+      return 0;
+    }
+  }
+
+  xname = explode(who, "@");
+
+  if (sizeof(xname) == 2) 
+  {
+    if ( QueryProp(P_QP) )
+    {
+      if (ret=(string)INETD->_send_udp(xname[1],
+                                    ([ REQUEST:   "tell",
+                                       RECIPIENT: xname[0],
+                                       SENDER:    getuid(ME),
+                                       DATA:     msg ]), 1))
+      {
+        _notify(ret, MA_TELL);
+      }
+      else
+      {
+        write("Nachricht abgeschickt.\n");
+        add_to_tell_history(who, 1, 0, msg,
+          "Du teilst " + capitalize(who) + " mit: ", MSGFLAG_TELL, 1);
+      }
+    }
+    else
+      write("Du hast nicht genug Abenteuerpunkte, um Spielern in anderen \n"
+        "Muds etwas mitteilen zu koennen.\n");
+    return 1;
+  }
+
+  if (!ob=find_player(it = lower_case(who)))
+  {
+    it = match_living(it, 0);
+    if (!stringp(it))
+      switch(it) {
+      case -1:
+        _notify("Das war nicht eindeutig!",MA_TELL);
+        return 1;
+      case -2:
+        _notify("Kein solcher Spieler!",MA_TELL);
+        return 1;
+      }
+    ob = find_player(it) || find_living(it);
+    if(!ob) {
+      _notify("Kein solcher Spieler!",MA_TELL);
+      return 1;
+    }
+  }
+
+  if(QueryProp(P_INVIS)){
+    if(!IS_LEARNER(ob))
+      myname = name();
+    else
+      myname="("+
+             ((QueryProp(P_CAN_FLAGS) & CAN_PRESAY)?QueryProp(P_PRESAY):"")+
+             capitalize(getuid()) + ")";
+  }
+  else
+    myname=((IS_LEARNER(ME) && (QueryProp(P_CAN_FLAGS) & CAN_PRESAY)) ?
+              QueryProp(P_PRESAY):"") + name();
+  if (myname && sizeof(myname)) myname=capitalize(myname);
+  // erstmal an Empfaenger senden
+  _send(ob, permutate(msg), MT_COMM|MT_FAR, MA_TELL,
+        myname + " teilt Dir mit: ");
+
+  // dann evtl. noch an Absender ausgeben...
+  if (visflag = !ob->QueryProp(P_INVIS) || IS_LEARNER(this_player()))
+    _recv(ob, msg, MSGFLAG_TELL, "Du teilst " + capitalize(it) + " mit: ");
+  // oder irgendwas anderes an den Absender ausgeben...
+  if (!visflag && interactive(ob))
+      _notify("Kein solcher Spieler!",MA_TELL);
+  else if (away = (string)ob->QueryProp(P_AWAY))
+      ReceiveMsg( break_string( away, 78, capitalize(it)
+                           + " ist gerade nicht da: ", BS_INDENT_ONCE ),
+          MT_NOTIFICATION|MSG_DONT_WRAP|MSG_DONT_IGNORE,
+          MA_TELL, 0, this_object());
+  else if (interactive(ob) && (i=query_idle(ob))>=600)
+  { //ab 10 Mins
+      if (i<3600)
+        away=time2string("%m %M",i);
+      else
+        away=time2string("%h %H und %m %M",i);
+
+      _notify(sprintf("%s ist seit %s voellig untaetig.",
+              capitalize(it),away),
+              MA_TELL);
+    }
+
+  return 1;
+}
+
+static int _teile(string str)
+{
+  string who, message;
+  if (!(str=_unparsed_args())) return 0;
+  if (sscanf(str, "%s mit %s", who, message) == 2)
+    return _tell(who, message,1);
+  return 0;
+}
+static int _teile_mit_alias(string str)
+{
+  str = _unparsed_args(), TRIM_LEFT;
+  if (!str) return 0;
+  str = trim(str, TRIM_LEFT);
+  // Ziel muss min. 2 Buchstaben haben (.<nr>)
+  if (sizeof(str) < 4) return 0;
+  int pos = strstr(str, " ");
+  if (pos >= 2)
+    return _tell(str[..pos-1], str[pos+1..]);
+  return 0;
+}
+
+static int _erzaehle(string str)
+{
+  string who, message;
+
+  if (!(str=_unparsed_args())) return 0;
+  if (sscanf(str, "%s %s", who, message) == 2)
+    return _tell(who, message,1);
+  return 0;
+}
+
+static int _whisper(string str)
+{
+  object    ob;
+  string    who;
+  string    msg;
+  string    myname;
+
+  if (!(str=_unparsed_args()) ||
+       (sscanf(str, "%s zu %s", who, msg) != 2 &&
+        sscanf(str, "%s %s", who, msg) !=2 )) {
+    _notify("Was willst Du wem zufluestern?",MA_SAY);
+    return 1;
+  }
+  if (!(ob = present(who, environment(this_player()))) || !living(ob)) {
+    _notify(capitalize(who)+" ist nicht in diesem Raum.",MA_SAY);
+    return 1;
+  }
+
+  myname = capitalize((((IS_LEARNER(ME) &&
+       !QueryProp(P_INVIS) &&
+       (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
+      QueryProp(P_PRESAY) : "") + name()) || "");
+
+  _send(ob, permutate(msg), MT_LISTEN|MSG_DONT_STORE,
+        MSG_SAY, myname + " fluestert Dir zu: ");
+  send_room(environment(),
+            myname + " fluestert " + ob->name(WEM, 1) + " etwas zu.",
+            MT_LISTEN|MSG_DONT_STORE, MA_SAY, 0, ({this_object(),ob}));
+
+  _recv(ob, msg, MSGFLAG_WHISPER, "Du fluesterst " + ob->name(WEM) + " zu: ");
+
+
+  return 1;
+}
+
+static int _remote_whisper(string str)
+{
+  /* Wie 'teile mit', nur mit MSGFLAG_WHISPER. Dadurch wird der Inhalt der
+     Nachricht nicht in der tell_history verewigt. */
+
+  object    ob;
+  string    who, it;
+  string    msg;
+  string    myname;
+
+  if (!(str=_unparsed_args()) ||
+       (sscanf(str, "%s zu %s", who, msg) != 2 &&
+        sscanf(str, "%s %s", who, msg) !=2 )) {
+    _notify("Was willst Du wem aus der Ferne zufluestern?",MA_EMOTE);
+    return 1;
+  }
+
+  if (!ob=find_player(it = lower_case(who)))
+  {
+    it = match_living(it, 0);
+    if (!stringp(it))
+      switch(it){
+      case -1:
+        _notify("Das war nicht eindeutig!",MA_EMOTE);
+        return 1;
+      case -2:
+        _notify("Kein solcher Spieler!",MA_EMOTE);
+        return 1;
+      }
+    ob = find_player(it);
+    if(!ob) ob = find_living(it);
+    if(!ob){
+      _notify("Kein solcher Spieler!",MA_EMOTE);
+      return 1;
+    }
+  }
+  if (environment(ob) == environment()) {
+    _notify("Wenn jemand neben Dir steht, nimm fluester.",MA_EMOTE);
+    return 1;
+  }
+
+  myname = capitalize((((IS_LEARNER(ME) &&
+       !QueryProp(P_INVIS) &&
+       (QueryProp(P_CAN_FLAGS) & CAN_PRESAY))?
+      QueryProp(P_PRESAY) : "") + name()) || "");
+
+  // An Empfaenger senden.
+  _send(ob, permutate(msg), MT_COMM|MT_FAR|MSG_DONT_STORE, MA_EMOTE,
+         myname + " fluestert Dir aus der Ferne zu: ");
+
+  // wenn Empfaenger invis und wir kein Magier , ggf. fakefehler ausgeben.
+  if (ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())) {
+    _notify("Kein solcher Spieler!",MA_EMOTE);
+    return 1;
+  }
+  // sonst eigene Meldung via _recv() ausgeben.
+  else 
+    _recv(ob, msg, MSGFLAG_WHISPER | MSGFLAG_REMOTE,
+        "Du fluesterst " + ob->name(WEM) + " aus der Ferne zu: ");
+
+  return 1;
+}
+
+static int _converse(string arg)
+{
+  _notify("Mit '**' wird das Gespraech beendet.",MA_SAY);
+  if (stringp(arg) && strstr(arg, "-s") == 0)
+    input_to("_converse_more", INPUT_PROMPT, "]", 1);
+  else
+    input_to("_converse_more", INPUT_PROMPT, "]", 0);
+  return 1;
+}
+
+static int _converse_more(mixed str, int silent)
+{
+  if (str == "**") {
+    _notify("Ok.",MA_SAY);
+    return 0;
+  }
+
+  if(str != "")
+    _communicate(str, silent);
+
+  input_to("_converse_more", INPUT_PROMPT, "]", silent);
+  return 1;
+}
+
+private int is_learner(object o) { return IS_LEARNER(o); }
+
+static int _shout_to_wizards(mixed str)
+{
+  int     i, j;
+  string    myname;
+  object   *u;
+
+  str = _unparsed_args();
+  if (!str||!sizeof(str)) {
+    _notify("Was willst Du den Magiern zurufen?",MA_SHOUT);
+    return 1;
+  }
+  // Kontrollzeichen rausfiltern.
+  str = regreplace(str,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
+  myname = capitalize(getuid(this_object()));
+  if (!IS_LEARNER(this_object()))
+    _recv(0, str, MSGFLAG_MECHO, "Du teilst allen Magiern mit: ");
+
+  // mrufe ist nicht ignorierbar, da es nur fuer schwere Probleme gedacht ist.
+  filter(users(), #'is_learner)->ReceiveMsg(str,
+      MT_COMM|MT_FAR|MSG_DONT_IGNORE|MSG_DONT_STORE,
+      MA_SHOUT, myname+" an alle Magier: ", this_object());
+
+  return 1;
+}
+
+static int _echo(string str) {
+  if (!IS_SEER(ME) || (!IS_LEARNER(ME)
+        && !(QueryProp(P_CAN_FLAGS) & CAN_ECHO)))
+    return 0;
+
+  if (!(str=_unparsed_args())) {
+    _notify("Was moechtest Du 'echoen'?", 0);
+    return 1;
+  }
+
+  if (!IS_LEARNER(this_interactive()))
+  {
+    if (QueryProp(P_GHOST))
+    {
+      _notify_fail("Ohne Koerper fehlt Dir dazu die noetige magische Kraft.\n");
+      return 0;
+    }
+    if (QueryProp(P_SP)<ECHO_COST)
+    {
+      _notify_fail("Du musst erst wieder magische Kraefte sammeln.\n");
+      return 0;
+    }
+    SetProp(P_SP,QueryProp(P_SP)-ECHO_COST);
+    str=">\b"+str;
+    log_file("ARCH/ECHO_SEHER", sprintf("%s %s: %s\n", dtime(time()), getuid(),
+           str));
+  }
+  // An den Raum senden. Typ ist MT_COMM, aber das Echo soll weder in der
+  // Kommhistory noch im Kobold landen.
+  send_room(environment(ME), str, MT_COMM|MSG_DONT_STORE|MSG_DONT_BUFFER,
+            MA_UNKNOWN, 0, 0);
+  return 1;
+}
+
+// Dient als Verteidigung gegen Leute, die eher unbedacht reinschreiben, nicht
+// gegen Leute, die da absichtlich reinschreiben. Die werden geteert
+// und gefedert.
+static string *_set_ignore(mixed arg)
+{
+  raise_error("Direktes Setzen von P_IGNORE ist nicht erlaubt. "
+      "Benutze AddIgnore/RemoveIgnore()!\n");
+}
+// Kompatibiltaet zum alten Ignore: Array von Indices liefern. Aendert aber
+// nix dran, dass alle TestIgnore() & Co benutzen sollen.
+static string *_query_ignore() {
+  mixed ign=Query(P_IGNORE, F_VALUE);
+  if (mappingp(ign))
+    return m_indices(ign);
+  return ({});
+}
+
+public int AddIgnore(string ign) {
+  // Einige strings sind nicht erlaubt, z.B. konsekutive .
+  if (!sizeof(ign)
+      || regmatch(ign,"[.]{2,}",RE_PCRE)
+      || regmatch(ign," ",RE_PCRE)
+      || sizeof(explode(ign,"."))>3)
+    return 0;
+
+  mapping ignores=Query(P_IGNORE, F_VALUE);
+  ignores[ign]=time();
+  // kein Set() noetig.
+  return 1;
+}
+
+public int RemoveIgnore(string ign)
+{
+  mapping ignores=Query(P_IGNORE,F_VALUE);
+  m_delete(ignores,ign);
+  // Kein Set() noetig
+  return 1;
+}
+
+static int _query_intermud()
+{
+  mixed tmp;
+  return member(pointerp(tmp=Query(P_CHANNELS))?tmp:({}), "Intermud") > -1;
+}
+
+
+int erwidere(string str)
+{
+  str=_unparsed_args();
+  if (!str) return 0;
+  return _tell(ERWIDER_PARAM, str ,1);
+}
+
+static int tmhist(string str)
+{
+
+  if (str == "aus") {
+    tell_history_enabled = TELLHIST_DISABLED;
+    write("Ok, es wird nichts mehr gespeichert.\n");
+    if (sizeof(tell_history)) {
+      tell_history = ([]);
+      commreceivers = ({});
+      write("Deine Mitteilungsgeschichte wurde geloescht.\n");
+    }
+    return 1;
+  }
+
+  if (str == "namen") {
+    int flag;
+    tell_history_enabled = TELLHIST_NO_MESSAGE;
+    write("Ok, die Namen zukuenftiger Gespraechspartner werden gespeichert.\n");
+    foreach (string uid, struct chat_s chat: tell_history)
+      if (pointerp(chat->msgbuf)) {
+        chat->msgbuf = 0;
+        chat->ptr = 0;
+        flag = 1;
+      }
+    if (flag)
+      write("Der Inhalt Deiner Mitteilungen wurde geloescht.\n");
+    return 1;
+  }
+
+  if (str == "ein" || str == "an") {
+    tell_history_enabled = TELLHIST_ENABLED;
+    write("Ok, zukuenftige Mitteilungen werden gespeichert.\n");
+    return 1;
+  }
+
+#ifdef TELLHIST_LONGLIFE
+  if (str == "langlebig") {
+    tell_history_enabled = TELLHIST_LONGLIFE;
+    write("Ok, zukuenftige Mitteilungen werden jeweils bis zum naechsten "
+          "Ende/Crash/\nReboot gespeichert.\n");
+    return 1;
+  }
+#endif
+
+  if (str == "status") {
+    switch (tell_history_enabled) {
+      case TELLHIST_DISABLED:
+        write("Die Namen Deiner Gespraechspartner werden nicht gespeichert.\n");
+        break;
+      case TELLHIST_NO_MESSAGE:
+        write("Die Namen Deiner Gespraechspartner werden gespeichert.\n");
+        break;
+      case TELLHIST_ENABLED:
+        write("Deine Mitteilungen werden gespeichert.\n");
+        break;
+#ifdef TELLHIST_LONGLIFE
+      case TELLHIST_LONGLIFE:
+        write("Deine Mitteilungen werden jeweils bis zum naechsten Ende/"
+              "Crash/Reboot\ngespeichert.\n");
+        break;
+#endif
+    }
+    return 1;
+  }
+
+  if (tell_history_enabled == TELLHIST_DISABLED) {
+    _notify_fail("Deine Gespraechspartner werden nicht gespeichert.\n");
+    return 0;
+  }
+
+  if (!sizeof(tell_history)) {
+    _notify_fail("Du hast noch keinem etwas mitgeteilt "
+                 "und noch keine Mitteilungen erhalten.\n");
+    return 0;
+  }
+
+  if (str && sizeof(str)) {
+
+    if (tell_history_enabled < TELLHIST_ENABLED) {
+      _notify_fail("Der Inhalt Deiner Mitteilungen wird nicht gespeichert.\n");
+      return 0;
+    }
+
+    string uid;
+    if (member(tell_history, str)) {
+      // Name gewuenscht, da der String in der History vorkommt.
+      uid = str;
+    }
+    else {
+      // evtl. ne Zahl angegeben.
+      int i;
+      string *partners = sorted_commpartners(0);
+      if ((i = to_int(str) - 1) >= 0 && i < sizeof(partners))
+        uid = partners[i];
+      else {
+        notify_fail("Mit so vielen Leuten hast Du nicht gesprochen!\n");
+        return 0;
+      }
+    }
+
+    mixed *data = tell_history[uid]->msgbuf;
+    if (!data) {
+      _notify_fail(
+        "Der Inhalt dieser Mitteilung ist nicht (mehr) gespeichert.\n");
+      return 0;
+    }
+
+    int ptr = tell_history[uid]->ptr;
+
+    More(sprintf("%@s", map(data[ptr..MAX_SAVED_MESSAGES-1] +
+                              data[0..ptr-1],
+         function string (struct stored_msg_s msg) {
+             if (!structp(msg)) return "";
+               return break_string( msg->msg + " <"
+                 + strftime("%H:%M:%S",msg->timestamp) + ">", 78,
+                 msg->prefix || "", msg->prefix ? BS_LEAVE_MY_LFS : 0);
+         } ) ) );
+    return 1;
+  }
+
+  string history = "Folgende Gespraeche hast Du bereits gefuehrt:\n";
+  int i;
+  foreach (string uid : sorted_commpartners(0) ) {
+    int j;
+    struct chat_s chat = tell_history[uid];
+    history += sprintf("%2d.%-4s %s  %-11s  %d gesendet/%d empfangen\n", ++i,
+      ((j=member(commreceivers,uid))>-1 ? sprintf("/%2d.",j+1) : ""),
+      strftime("%a, %e.%m.%y",chat->time_last_msg),
+      capitalize(chat->uid), chat->sentcount, chat->recvcount);
+  }
+
+  More(history);
+
+  return 1;
+}
+
+static mixed _query_localcmds()
+{
+  return ({
+    ({"kobold", "cmd_kobold",0,0}),
+     ({"sag","_communicate",0,0}),
+     ({"sage","_communicate",0,0}),
+     ({"'","_communicate",1,0}),
+     ({"mruf","_shout_to_wizards",0,0}),
+     ({"mrufe","_shout_to_wizards",0,0}),
+     ({"ruf","_shout_to_all",0,0}),
+     ({"rufe","_shout_to_all",0,0}),
+     ({"erzaehl","_erzaehle",0,0}),
+     ({"erzaehle","_erzaehle",0,0}),
+     ({"teil","_teile",0,0}),
+     ({"teile","_teile",0,0}),
+     ({"tm","_teile_mit_alias",0,0}),
+     ({"fluester","_whisper",0,0}),
+     ({"fluestere","_whisper",0,0}),
+     ({"rfluester","_remote_whisper",0,0}),
+     ({"rfluestere","_remote_whisper",0,0}),
+     ({"gespraech","_converse",0,0}),
+     ({"echo","_echo",0,0}),
+     ({"ignorier","ignoriere",0,0}),
+     ({"ignoriere","ignoriere",0,0}),
+     ({"tmhist","tmhist",0,0}),
+     ({"erwider","erwidere",0,0}),
+     ({"erwidere","erwidere",0,0}),
+     ({"klingelton","_msg_beep",0,0}),
+     ({"senderwiederholung","_msg_prepend",0,0}),
+     ({"report","set_report",0,0}),
+    })+channel::_query_localcmds();
+}
+
+private string *sorted_commpartners(int reversed) {
+  return sort_array(m_indices(tell_history),
+      function int (string uid1, string uid2) {
+          if (reversed)
+            return tell_history[uid1]->time_last_msg >
+                   tell_history[uid2]->time_last_msg;
+          else
+            return tell_history[uid1]->time_last_msg <=
+                   tell_history[uid2]->time_last_msg;
+      } );
+}
+
+// Eigentlich nur in Magierobjekten gerufen. Gehoert aber thematisch hier
+// irgendwie hin.
+static void modify_prompt() {
+    string text = Query(P_PROMPT, F_VALUE);
+
+    if ( !stringp(text) || !sizeof(text) )
+        text = "> ";
+    else {
+        string path = Query(P_CURRENTDIR, F_VALUE);
+        if (stringp(path) && sizeof(path))
+          text = regreplace(text,"\\w",path,0); // Pfad einsetzen
+    }
+    configure_interactive(this_object(), IC_PROMPT, text);
+}
+
+// Prueft auf Ingoriereeintraege.
+// Rueckgabe: 0 (nicht ignoriert) oder MSG_IGNORED
+#ifdef __LPC_UNIONS__
+public int TestIgnore(string|string* srcnames)
+#else
+public int TestIgnore(mixed srcnames)
+#endif
+{
+  mapping ign = Query(P_IGNORE, F_VALUE);
+  if (stringp(srcnames))
+    srcnames = ({srcnames});
+
+  foreach(string srcname: srcnames)
+  {
+    // einfachster Fall, exakter Match
+    if (member(ign, srcname))
+      return MSG_IGNORED;
+    // ansonsten muss aufgetrennt werden.
+    if (strstr(srcname,".") > -1)
+    {
+      string *srcparts=explode(srcname,".");
+      switch(sizeof(srcparts))
+      {
+        // case 0 und 1 kann nicht passieren.
+        case 3:
+          // zu pruefen: [sender].aktion.qualifizierer.
+          // Der Fall, dass der Spieler dies _genau_ _so_ ignoriert hat, wird
+          // oben schon geprueft. im Spieler geprueft werden muss noch:
+          // spieler, .aktion, spieler.aktion und .aktion.qualifizierer
+          if ( (sizeof(srcparts[0]) && member(ign,srcparts[0])) // spieler
+              || member(ign, "."+srcparts[1])      // .aktion
+              || member(ign, srcparts[0]+"."+srcparts[1]) // [spieler].aktion
+              || member(ign, "."+srcparts[1]+"."+srcparts[2]) // .akt.qual
+             )
+          {
+            return MSG_IGNORED;
+          }
+          break;
+        case 2:
+          // zu pruefen: spieler.aktion
+          // Der Fall, dass der Spieler das _genau_ _so_ eingegeben hat, ist
+          // oben schon geprueft. Im Spieler zu pruefen ist noch:
+          // spieler und .aktion
+          if ((sizeof(srcparts[0]) && member(ign,srcparts[0]))
+              || member(ign, "."+srcparts[1]))
+          {
+            return MSG_IGNORED;
+          }
+          break;
+        default: // mehr als 3 Teile...
+          raise_error(sprintf("TestIgnoreExt(): too many qualifiers, only 1 "
+                "is supported. Got: %s\n",srcname));
+          break;
+      }
+    }
+  }
+  // Default: nicht ignorieren.
+  return 0;
+}
+
+#ifdef __LPC_UNIONS__
+public int TestIgnoreExt(string|string* srcnames)
+#else
+public int TestIgnoreExt(mixed srcnames)
+#endif
+{
+  return TestIgnore(srcnames);
+}
+
+// Prueft fuer ReceiveMsg() auf Ingoriereeintraege. Ignoriert aber nicht alle
+// Typen. 
+// Rueckgabe: 0 oder MSG_IGNORED | MSG_VERB_IGN | MSG_MUD_IGN
+private int check_ignores(string msg, int msg_type, string msg_action,
+                            string msg_prefix, object origin)
+{
+  // Einige Dinge lassen sich nicht ignorieren.
+  if (msg_type & (MT_NEWS|MT_NOTIFICATION))
+    return 0;
+  // alles andere geht zur zeit erstmal, wenn origin bekannt UND NICHT das
+  // eigene Objekt ist. Waer ggf. sonst doof. Ausserdem muss es natuerlich
+  // eine ignorierbare msg_action geben.
+  else if (stringp(msg_action) && origin && origin != ME)
+  {
+    string srcname =
+      (query_once_interactive(origin) ? origin->query_real_name()
+                                      : origin->name(WER) || "");
+    mapping ign = Query(P_IGNORE, F_VALUE);
+
+    if (member(ign, srcname))
+      return MSG_IGNORED;
+    // vielleicht wird irgendwas a la name.aktion ignoriert?
+    // dies ignoriert auch spieler.ebenen.<ebene> (s. msg_action bei
+    // Ebenenmeldungen)
+    if (member(ign, srcname+"."+msg_action))
+      return MSG_VERB_IGN;
+    // Oder die Aktion komplett? Dies ignoriert auch .ebenen.<ebene>, obwohl
+    // das reichlich sinnfrei ist.
+    if (member(ign, "."+msg_action))
+      return MSG_VERB_IGN;
+    // Spieler auf Ebenen ignoriert?
+    // msg_action ist hier nach diesem Muster: MA_CHANNEL.<ebene>
+    if (strstr(msg_action, MA_CHANNEL) == 0)
+    {
+      // spieler.ebenen? (spieler.ebenen.<ebene> oben schon geprueft)
+      if (member(ign, srcname + "."MA_CHANNEL))
+        return MSG_IGNORED;
+      // spieler.ebenen.ebenenname ist oben schon abgedeckt.
+      // .ebenen halte ich fuer sinnfrei, nicht geprueft.
+    }
+    // Spieler aus anderem mud? *seufz*
+    if (strstr(srcname,"@") > -1)
+    {
+      string *srcparts = explode(srcname,"@");
+      if (sizeof(srcparts)==2)
+      {
+        // spieler@?
+        if (member(ign, srcparts[0]+"@"))
+          return MSG_IGNORED;
+        // oder Mud per @mud?
+        if (member(ign, "@" + srcparts[1]))
+          return MSG_MUD_IGN;
+        // BTW: spieler@mud wurde schon ganz oben erfasst.
+      }
+    }
+  }
+  // Default: nicht ignorieren.
+  return 0;
+}
+
+// Wird die nachricht wahrgenommen? Die Pruefung erfolgt aufgrund von
+// msg_type. Zur wird MT_LOOK und MT_LISTEN beruecksichtigt (Pruefung auf
+// BLindheit/Taubheit).
+// Wichtig: enthaelt msg_action weder MT_LOOK noch MT_LISTEN, wird die
+// Nachricht wahrgenommen, da davon ausgegangen wird, dass sie mit den beiden
+// Sinn gar nix zu tun hat.
+// Rueckgabe: 0 oder MSG_SENSE_BLOCK
+private int check_senses(string msg, int msg_type, string msg_action,
+                              string msg_prefix, object origin)
+{
+  int senses = msg_type & (MT_LOOK|MT_LISTEN);
+  // Wenn von vorherein kein Sinn angesprochen, dann ist es eine nachricht,
+  // die von keinem der beiden wahrgenommen wird und sollte demnach nicht
+  // unterdrueckt werden.
+  if (!senses)
+    return 0;
+
+  if ((senses & MT_LOOK) && CannotSee(1))
+    senses &= ~MT_LOOK;  // Sinn loeschen
+
+  if ((senses & MT_LISTEN) && QueryProp(P_DEAF))
+    senses &= ~MT_LISTEN;
+
+  // wenn kein Sinn mehr ueber, wird die Nachricht nicht wahrgenommen.
+  if (!senses)
+    return MSG_SENSE_BLOCK;
+
+  return 0;
+}
+
+public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
+                              string msg_prefix, object origin)
+{
+  if (!msg) return MSG_FAILED;
+
+  // Flags und Typen spalten
+  int flags = msg_type & MSG_ALL_FLAGS;
+  int type = msg_type & ~flags;
+
+  // ggf. defaults ermitteln
+  origin ||= previous_object();
+  msg_action ||= comm_guess_action();
+  type ||= comm_guess_message_type(msg_action, origin);
+
+  // Debugmeldungen nur an Magier oder Testspieler mit P_WIZ_DEBUG
+  if (msg_type & MT_DEBUG)
+  {
+    if (!QueryProp(P_WIZ_DEBUG)
+        || (!IS_LEARNER(ME) && !QueryProp(P_TESTPLAYER)) )
+    return MSG_FAILED;
+  }
+
+  // Zuerst werden Sinne und P_IGNORE sowie ggf. sonstige Filter geprueft. In
+  // dem Fall ist direkt Ende, kein Kobold, keine Komm-History, keine
+  // Weiterbearbeitung.
+  // aber bestimmte Dinge lassen sich einfach nicht ignorieren.
+  if (!(flags & MSG_DONT_IGNORE))
+  {
+    // Sinne pruefen. (nur typen uebergeben, keine Flags)
+    int res=check_senses(msg, type, msg_action, msg_prefix, origin);
+    if (res) return res;
+
+    // Spieler-definiertes Ignoriere? (nur typen uebergeben, keine Flags)
+    res=check_ignores(msg, type, msg_action, msg_prefix, origin);
+    if (res) return res;
+  }
+
+  // Fuer MT_COMM gibt es ein paar Sonderdinge zu machen.
+  if ((type & MT_COMM))
+  {
+    // erstmal in der Komm-History ablegen, wenn gewuenscht.
+    if ((!(flags & MSG_DONT_STORE)))
+    {
+      string uid;
+      if (query_once_interactive(origin))
+        uid = origin->query_real_name();
+      else
+        uid = origin->name(WER) || "<unbekannt>";
+      add_to_tell_history(uid, 0, 1, msg, msg_prefix, 0);
+    }
+
+    // ggf. Uhrzeit bei abwesenden Spielern anhaengen, aber nicht bei
+    // Ebenenmeldungen. (Die haben ggf. schon.)
+    if (stringp(msg_action) && QueryProp(P_AWAY)
+        && strstr(msg_action, MA_CHANNEL) != 0)
+    {
+      // Uhrzeit anhaengen, aber ggf. muss ein \n abgeschnitten werden.
+      if (msg[<1] == '\n')
+        msg = msg[0..<2]+" [" + strftime("%H:%M",time()) + "]\n";
+      else
+        msg = msg + " [" + strftime("%H:%M",time()) + "]";
+    }
+    // Kobold erlaubt und gewuenscht? Kobold ist fuer die
+    // direkte Kommunikation mittels MT_COMM vorgesehen.
+    // Oropax von Magiern leitet inzwischen auch nur in Kobold um statt zu
+    // ignorieren.
+    // die if-Konstruktion ist so, weil ich das _flush_cache() im else
+    // brauche.
+    if (query_editing(this_object()) || query_input_pending(this_object())
+        || QueryProp(P_EARMUFFS))
+    {
+      if (!(flags & MSG_DONT_BUFFER)
+            && QueryProp(P_BUFFER))
+      {
+        // Nachricht soll im Kobold gespeichert werden.
+        return add_to_kobold(msg, msg_type, msg_action, msg_prefix, origin);
+      }
+    }
+    else
+    {
+      // wenn nicht in Editor/input_to, mal versuchen, den Kobold zu
+      // entleeren.
+      _flush_cache(0);
+    }
+
+    // ggf. Piepston anhaengen. NACH Koboldablage, die sollen erstmal keinen
+    // Pieps kriegen.
+    if (comm_beep())
+      msg=msg + MESSAGE_BEEP;
+  }
+
+  // Ausgabenachricht bauen und an den Spieler senden.
+  if (flags & MSG_DONT_WRAP)
+    msg = (msg_prefix ? msg_prefix : "") + msg;
+  else
+  {
+    int bsflags = flags & MSG_ALL_BS_FLAGS;
+    if (QueryProp(P_MESSAGE_PREPEND))
+      bsflags |= BS_PREPEND_INDENT;
+    msg = break_string(msg, 78, msg_prefix, bsflags);
+  }
+  efun::tell_object(ME, msg);
+
+  return MSG_DELIVERED;
+}
diff --git a/std/player/comm_structs.c b/std/player/comm_structs.c
new file mode 100644
index 0000000..5f268eb
--- /dev/null
+++ b/std/player/comm_structs.c
@@ -0,0 +1,41 @@
+// MorgenGrauen MUDlib
+//
+// player/comm.c-- basic player communiction commands
+//
+// $Id: comm.c 6918 2008-08-07 21:13:16Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+struct msg_s {
+  string msg;       // Inhalt der Nachricht
+  int type;         // Messagetyp fuer ReceiveMsg
+  string action;    // Messageaction fuer ReceiveMsg
+  string prefix;    // Einrueckung der Nachricht bei Darstellung (msg_prefix)
+  string sendername;// Ursprung der Nachricht
+};
+
+struct stored_msg_s (msg_s) {
+  int timestamp;    // Zeitstempel der Nachricht
+};
+
+struct msg_buffer_s {
+  //struct msg_s *buf;
+  mixed *buf;
+  int index;
+};
+
+struct chat_s {
+  string uid;           // UID des Gespraechspartners
+  int time_first_msg;   // Zeit der ersten Nachricht
+  int time_last_msg;    // Zeit der letzen Nachricht
+  int sentcount;        // Anzahl gesendeter Nachrichten
+  int recvcount;        // Anzahl empfangener Nachrichten
+  mixed msgbuf;         // Array von msg_s (Art Ringpuffer)
+  int ptr;              // Pointer auf die naechste zu ueberschreibende msg_s
+                        // in msgbuf
+};
+
diff --git a/std/player/command.c b/std/player/command.c
new file mode 100644
index 0000000..230445d
--- /dev/null
+++ b/std/player/command.c
@@ -0,0 +1,1074 @@
+// MorgenGrauen MUDlib
+//
+// player/commands.c -- alias, history and player command handling
+//
+// $Id: command.c 9576 2016-06-18 15:00:01Z Zesstra $
+#pragma strong_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <player/command.h>
+#include <player/comm.h>
+#include <thing/properties.h>
+#include <living/moving.h>
+#include <player.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <language.h>
+#include <new_skills.h>
+#include <config.h>
+#include <defines.h>
+#include <wizlevels.h>
+#include <logging.h>
+#include <strings.h>
+
+#define CBLOG(x)    log_file(SHELLLOG("DISABLECOMMANDS"),x,200000)
+
+#define HIST_SIZE 40
+#define EPMASTER "/secure/explorationmaster"
+
+private mapping aliases;
+private string *commands;
+private int hist_size, show_processing, histmin;
+private string default_notify_fail;
+private nosave string *history, *unparsed_args, unmodified;
+private nosave int hist_now;
+private nosave object last_command_env;
+private nosave int cmds_per_time, last_chg, max_commands, *cmd_types;
+// Datenstruktur: ({Setzer, Ablaufzeit, String/Closure})
+private nosave mixed disablecommands;
+private nosave object* syntaxdb;
+
+nomask void __set_bb(int flag);
+
+static varargs int __auswerten(string str, string intern);
+varargs int SoulComm(string str, string _verb);
+varargs mixed More(string str, int fflag, string returnto);
+static int _starts_with(string str, string start);
+static void reallocate_histbuf();
+
+private void AddHistory(string str)
+{
+  if (!stringp(str) || str=="" || str[0]=='&' || str[0]=='^' ||
+      str=="hist")
+    return;
+  if (!hist_size) return;
+  if (!pointerp(history) || sizeof(history)!=hist_size)
+    reallocate_histbuf();
+  if (sizeof(str)>=histmin && history[(hist_size+hist_now-1)%hist_size]!=str)
+    history[(hist_now++)%hist_size]=str;
+}
+
+static void create()
+{
+  last_chg=0;
+  histmin=hist_now=0;
+  Set(P_LOCALCMDS,({}));
+  Set(P_LOCALCMDS,PROTECTED,F_MODE_AS);
+  Set("_syntaxdb", SECURED|SAVE, F_MODE_AS);
+
+  show_processing=1;
+  unparsed_args=({0,0,0});
+  hist_size=HIST_SIZE;
+}
+
+static int replacedisplay(string str)
+{
+  if (!str || str=="" || !sscanf(str,"%d",show_processing))
+    printf("Unzulaessige Eingabe!\n%s 0|1|2\n",query_verb());
+    printf("Ersetzungsanzeige auf Level %d.\nLevel 0: Nichts anzeigen\n"+
+     "Level 1: Nur History-Ersetzungen anzeigen\n"+
+     "Level 2: History- und Alias-Ersetzungen anzeigen\n",show_processing);
+  if (show_processing>2&&!IS_WIZARD(ME)) show_processing=2;
+  return 1;
+}
+
+static int histmin(string str)
+{
+  int len;
+
+  if (!str||!sscanf(str,"%d",len)||len<0)
+  {
+    write("Benutzung: histmin ZAHL\nLegt die Mindestlaenge fest, die eine \
+Befehlszeile haben muss, um in den\nHistory-Puffer zu gelangen. Derzeit \
+eingestellt auf "+(string)histmin+" Zeichen.\n");
+    return 1;
+  }
+  histmin=len;
+  write("Mindestlaenge auf "+(string)len+" eingestellt.\n");
+  return 1;
+}
+
+static void reallocate_histbuf()
+{
+  int i;
+
+  history=allocate(hist_size);
+  hist_now=0;
+  for (i=0;i<hist_size;i++)
+    if (!stringp(history[i]))
+      history[i]="\n\n";
+}
+
+static int histlen(string str)
+{
+  int d;
+  if (!str||!sscanf(str,"%d",d)||d<0||d>40)
+  {
+    write("Benutzung: histlen ZAHL\nZAHL muss zwischen 0 und 40 liegen.\n");
+    printf("Deine History-Buffer-Laenge liegt bei %d Befehlen.\n",hist_size);
+    return 1;
+  }
+  hist_size=d;
+  printf("Deine History-Buffer-Laenge liegt jetzt bei %d Befehlen.\n",
+   hist_size);
+  reallocate_histbuf();
+  return 1;
+}
+
+static void initialize()
+{
+  if (!pointerp(history)||sizeof(history)!=hist_size)
+    reallocate_histbuf();
+  add_action("__auswerten","",1);
+    max_commands = EPMASTER->QueryCommands();
+    cmd_types = EPMASTER->QueryCmdTypes() || ({});
+
+    if ( !mappingp(aliases) )
+        aliases = ([]);
+
+    if ( !pointerp(commands) )
+        commands = ({});
+
+    if (QueryProp("_syntaxdb"))
+        syntaxdb = ({find_object("/secure/syntaxdb")});
+/*    else if (QueryProp(P_TESTPLAYER))
+    {
+      SetProp("_syntaxdb", 1);
+      call_out(#'_notify, 2,
+          "\nDa Du als Testspieler markiert bist, wurde bei Dir "
+          "die Syntaxsammlung eingeschaltet. Du kannst dies "
+          "wieder ausschalten. (hilfe syntaxsammlung) "
+          "Es waere schoen, wenn Du es beim Testen von "
+          "Gebieten einschaltest.", 0);
+    }*/
+}
+
+static mixed _set_default_notify_fail(string s)
+{
+  if (stringp(s)&&s!="")
+  {
+    if (s[<1]!='\n') s+="\n";
+    return default_notify_fail=s;
+  }
+  else if (!s||s=="")
+    return (default_notify_fail=0);
+}
+
+static mixed _query_default_notify_fail()
+{
+  return default_notify_fail;
+}
+
+static int set_errormessage(string s)
+{
+  if (!(s=_unparsed_args()))
+  {
+    _set_default_notify_fail(0);
+    write("Standard-Fehlermeldung auf \"Wie bitte?\" gesetzt.\n");
+  } else
+  {
+    write(break_string(sprintf("Standard-Fehlermeldung auf %s gesetzt.\n",
+             s),78));
+    _set_default_notify_fail(s);
+  }
+  return 1;
+}
+
+void reconnect()
+{
+  if (!mappingp(aliases)) aliases=([]);
+
+    if ( !pointerp(commands) )
+        commands = ({});
+
+    max_commands = EPMASTER->QueryCommands();
+    cmd_types = EPMASTER->QueryCmdTypes() || ({});
+}
+
+static int show_hist()
+{
+  int i;
+  string comm;
+
+  tell_object( ME, "Die History-Liste enthaelt folgende Kommandos:\n" );
+
+  for( i = 0; i < hist_size; i++ )
+      if ((comm=history[(hist_now+i)% hist_size])!= "\n\n")
+          tell_object( ME, " &"+(hist_now+i-hist_size)+"/-"+ (hist_size-i-1)
+      +"\t= "+comm+"\n");
+  return 1;
+}
+
+static string present_alias(mixed *ali)
+{
+  int j,k;
+  string s,s2;
+
+  for (s="",j=sizeof(ali)-1;j>=0;j--)
+    if (intp(ali[j]))
+      if ((k=ali[j])<0)
+  s="$"+(k==-1?"":(string)-k)+"*"+s;
+      else
+  s="$"+(string)k+s;
+    else
+      {
+         s2=implode(explode(ali[j],"\\"),"\\\\");
+         s=implode(explode(s2,"$"),"\\$")+s;
+      }
+  return s;
+}
+
+#define ALIFORMAT ({" %s\t= %s", "alias %s %s"})[display_as_aliascommand]
+// Ich weiss, den Variablennamen im define zu haben ist unfein, aber das
+// macht es im Code dann angenehm uebersichtlich.  -HrT
+
+static int query_aliases(int display_as_aliascommand)
+{
+  int i;
+  string *a,*ali;
+
+  if(i=sizeof(ali=sort_array(m_indices(aliases),#'<))) //')))
+  {
+    for(a=({}),i--; i>=0; i--)
+        a+=({sprintf(ALIFORMAT, ali[i], present_alias( aliases[ali[i]] ) ) });
+    More("Du hast folgende Aliase definiert:\n"+implode(a,"\n"));
+  }
+  else
+    write("Du hast keine Aliase definiert.\n");
+  return 1;
+}
+
+static int
+_starts_with(string str, string start)
+{
+  return (sizeof(start)>sizeof(str) ? 0
+    : str[0..sizeof(start)-1]==start);
+}
+
+static int alias(string str)
+{
+  string command;
+  string *tmp,um,*hits;
+  int num, l, pos, cont;
+  int display_as_aliascommand;
+
+  if (unmodified&&unmodified!="")
+    um=implode(old_explode(unmodified," ")[1..]," ");
+  if (um=="") um=0;
+  if(!(str=um||_unparsed_args()) || str=="*") return query_aliases(0);
+
+    if (str=="-a" || strstr(str, "-a ")==0 )  {
+    str=str[2..];
+    if (str && str!="" && str[0]==' ') str=str[1..];
+    if (!str || str=="" || str=="*") return query_aliases(1);
+    display_as_aliascommand=1;
+  }
+
+  if ((pos=member(str,' '))<0) // 1 Arg only
+  {
+    if ((tmp=aliases[str]))
+      printf(ALIFORMAT+"\n",str,present_alias(tmp));
+    else
+      if (str[<1]=='*')
+      {
+        str=str[0..<2];
+        hits=filter(m_indices(aliases), #'_starts_with, str);
+        if (!sizeof(hits))
+        {
+          printf("Du hast kein Alias, das mit \"%s\" anfaengt.\n", str);
+          return 1;
+        }
+        hits=sort_array(hits, #'>);
+        for (l=sizeof(hits); l--;)
+          hits[l]=sprintf(ALIFORMAT, hits[l], present_alias(aliases[hits[l]]));
+        More("Folgende Aliase beginnen mit \""+str+"\":\n"+implode(hits,"\n"));
+      }
+    else
+      printf("Du hast kein Alias \"%s\" definiert.\n",str);
+    return 1;
+  }
+  if (!pos)
+  {
+    write("Fehler: Blanc am Alias-Anfang\n");
+    return 1;
+  }
+  if ((command=str[0..pos-1])=="unalias")
+  {
+    write
+      ("Es nicht moeglich, den Befehl unalias zu ueberladen (waer dumm :))\n");
+    return 1;
+  }
+  if ((command=str[0..pos-1])=="*")
+  {
+    write
+      ("Es nicht moeglich, den Befehl \"*\" zu ueberladen.\n");
+    return 1;
+  }
+
+  str=str[pos+1..],tmp=({});
+  while (l=sizeof(str)) {
+    pos=0,cont=1;
+    while (cont) {
+      if (pos<l) {
+        if(str[pos]=='\\') {
+          str=str[0..pos-1]+str[pos+1..];
+          l--;
+        } else {
+          if (str[pos]=='&' || str[pos]=='$') {
+            cont=0;
+            if (pos>0) {
+              tmp+=({str[0..pos-1]});
+            }
+            if (pos==l-1) {
+              printf("Fehler: %c am Zeilenende\n",str[pos]);
+              return 1;
+            }
+            if ((num=str[++pos])=='*') {
+              num=1;
+              pos--;
+            } else {
+              num-='0';
+            }
+            if (num<0 || num>9) {
+              printf("Fehler: Nach %c muss Ziffer oder * folgen\n",
+               str[pos-1]);
+              return 1;
+            }
+            if ((str=str[pos+1..])!=""&&str[0]=='*') {
+              str=str[1..];
+              num=-num;
+            }
+            tmp+=({num});
+          }
+        }
+        pos++;
+      } else {
+        cont=0;
+        if (str!="") tmp+=({str});
+        str="";
+      }
+    }
+  }
+  if ((!aliases[command]) && (sizeof(aliases)>2000))
+    printf("Du hast schon genuegend Aliase definiert!\n");
+  else
+  {
+    aliases[command]=tmp;
+    printf("Neues Alias: %s\t= %s\n",command,present_alias(tmp));
+  }
+  return 1;
+}
+
+static int unalias(string str) {
+  int i;
+  string *als,um;
+
+  if (unmodified&&unmodified!="")
+    um=implode(old_explode(unmodified," ")[1..]," ");
+  if (um=="") um=0;
+  if ( !(str=um || _unparsed_args())) return 0;
+  if (str == "*.*" || str == "*") {
+    write(break_string(
+      "Versuchs mal mit 'unalias .*', wenn Du wirklich alle Alias entfernen "
+      "willst.",78));
+    return 1;
+  }
+  if (!member(aliases,str)) {
+    als=regexp(m_indices(aliases),("^"+str+"$"));
+    if (!(i=sizeof(als))) {
+      write("So ein Alias hast Du nicht definiert.\n");
+      return 1;
+    }
+    for (--i;i>=0;i--)
+      m_delete(aliases,als[i]);
+    write(break_string(("Du entfernst folgende Aliase: "+
+      implode(als," ")+".\n"),75));
+    return 1;
+  }
+  m_delete(aliases,str);
+  write("Du entfernst das Alias \""+str+"\".\n");
+  return 1;
+}
+
+varargs string _unparsed_args(int level)
+{
+  return unparsed_args[level];
+}
+
+#define ARTIKEL ({"das","der","die","des","dem","den","ein","eine","einer",\
+                  "eines"})
+
+#define TRENNER ({"in","aus","ueber","auf","unter","mit","durch","fuer",\
+                  "von","vom","im","aufs","ein","weg","zurueck"})
+
+static string _single_spaces(string str)
+{
+  return regreplace(str, "  *", " ", 1);
+}
+
+static mixed _return_args(string str)
+{
+  string *t,*t2,verb,s2;
+  int i,l,j,l2;
+
+  t=explode(trim(str,TRIM_BOTH)," ");
+  verb=t[0];
+  t = t[1..];
+  if (!sizeof(t))
+  {
+    unparsed_args[0]=unparsed_args[1]=unparsed_args[2]=0;
+    return str=verb;
+  }
+  else
+    str = unparsed_args[0] = implode(t, " ");
+
+  str=unparsed_args[1]=lower_case(_single_spaces(str));
+  t=regexplode(str,"\\<im\\>|\\<ins\\>");
+  for (i=1;i<sizeof(t);i+=2) t[i]="in";
+  t=regexplode(implode(t,""),"[\\,\\!\\:][\\,\\!\\:]*");
+  l=sizeof(t);
+  for(i=1;i<l;i+=2) t[i]="";
+  t=old_explode(implode(t,"")," ")-({""});
+  for (i=sizeof(t)-2;i>=0;i--)
+  {
+    if (member(ARTIKEL,t[i])>=0)
+      t=t[0..i-1]+t[i+1..];
+  }
+  unparsed_args[2]=implode(t," ");
+  t=regexplode((str=implode(t," ")),"[0-9][0-9]*\\.");
+  if ((l=sizeof(t))>2)
+  {
+    i=1;
+    while (i<l-1)
+    {
+      t[i]=" "+t[i][0..<2]+" ";
+      if ((l2=sizeof(t2=old_explode(t[i+1]," ")))<2)
+  t[i+1]+=t[i];
+      else
+      {
+  for (j=1;j<l2;j++)
+  {
+    if (member(TRENNER,t2[j])>=0)
+    {
+      t2[j-1]+=t[i];
+      l2=0;
+    }
+  }
+  if (!l2)
+    t[i+1]=implode(t2," ");
+  else
+    t[i+1]+=t[i];
+      }
+      t[i]="";
+      i+=2;
+    }
+    str=_single_spaces(verb+" "+implode(t," "));
+    if (str[<1]==' ') str=str[0..<2];
+  } else str=verb+(str==""?"":" "+str);
+  if (show_processing>2)
+    printf("-> {%s}\n",str);
+  return str;
+}
+
+static void decay_average()
+{
+  if (absolute_hb_count()-last_chg>14)
+  {
+    last_chg=absolute_hb_count()-last_chg;
+    if (last_chg>3000)
+      last_chg=absolute_hb_count(),cmds_per_time=0;
+    else
+    {
+      while (last_chg>14)
+  cmds_per_time=cmds_per_time*9/10, last_chg-=15;
+      last_chg=absolute_hb_count()-last_chg;
+    }
+  }
+}
+
+private void DelayPreparedSpells() {
+  mixed ps;
+
+  if (pointerp(ps=QueryProp(P_PREPARED_SPELL))
+      && sizeof(ps)>=1 && intp(ps[0])) {
+    ps[0]++;
+    SetProp(P_PREPARED_SPELL,ps);
+    write("Die Ausfuehrung Deines vorbereiteten Spruches wird verzoegert.\n");
+  } else if (ps) {
+    SetProp(P_PREPARED_SPELL,0);
+  }
+}
+
+static mixed bb;
+#ifndef BBMASTER
+#define BBMASTER "/secure/bbmaster"
+#endif
+
+/** Interpretiert Aliase und History-Kommandos
+  Eigentlich muesste hier noch die Umwandlung der Sonderzeichen
+  verschiedener Zeichensaetze mit convert_charset gemacht werden,
+  aber noch gibt es keine Moeglichkeit, den vom Spieler genutzten
+  Zeichensatz zu identifizieren.
+  \param[in] str string - Kommando des Spielers
+  \return interpretiertes Alias bzw. korrektes Kommando aus der History
+*/
+private string parsecommand(string str)
+{
+  if (str[0]=='\\')
+  {
+    // Kommando soll nicht interpretiert werden
+    return str[1..];
+  }
+  else if (str[0]=='&')
+  {
+    // Kommando aus der History
+    string cmd = str[1..];
+    int cmd_size = sizeof(cmd);
+    int cmd_found = 0;
+    if (cmd_size)
+    {
+      // Test ob &<text> etwas findet
+      for (int i=0;i<hist_size-1 && !cmd_found;i++)
+      {
+        int idx = (hist_size-i+hist_now-1)%hist_size;
+        if (history[idx][0..cmd_size-1]==cmd)
+        {
+          str = history[idx];
+          cmd_found = 1;
+        }
+        if (cmd_found)
+        {
+          if (show_processing)
+            printf("[%s]\n",str);
+        }
+      }
+    }
+    if (!cmd_found)
+    {
+      // Test, ob &<nr> klappt
+      int nummer;
+      if (str=="&&")
+        str = "&-0";
+      if (sscanf(str,"&%d",nummer))
+      {
+        if (nummer<0 || (!nummer && str[1]=='-'))
+        {
+          if (nummer<-(hist_size-1))
+            nummer=-1;
+          else
+            nummer=(hist_now+nummer-1+hist_size)%hist_size;
+        }
+        else
+        {
+          if (nummer>hist_now || hist_now-nummer>hist_size)
+            nummer=-1;
+          else
+            nummer=nummer%hist_size;
+        }
+        if (nummer<0 
+            || ( (cmd=history[nummer]) =="\n\n") )
+          notify_fail("Der Befehl ist nicht in der History!\n");
+        else
+        {
+          str = cmd;
+          if (show_processing)
+            printf("[%s]\n",str);
+        }
+      }
+    }
+  }
+  switch (str)
+  {
+    case "n": return "norden";
+    case "s": return "sueden";
+    case "w": return "westen";
+    case "o": return "osten";
+    case "nw": return "nordwesten";
+    case "sw": return "suedwesten";
+    case "so": return "suedosten";
+    case "no": return "nordosten";
+    case "ob": return "oben";
+    case "u": return "unten";
+  }
+  // Test auf Alias
+  string output = "";
+  string* input = explode(str," ");
+  int input_size = sizeof(input);
+  mixed alias = aliases[input[0]];
+  if (!alias)
+    return str;
+  foreach (mixed a:alias)
+  {
+    if (!intp(a))
+      output += a;
+    else
+    {
+      if (a >= 0)
+      {
+        if (input_size > a)
+          output += input[a];
+      }
+      else
+      {
+        a = -a;
+        if (input_size > a)
+          output += implode(input[a..]," ");
+      }
+    }
+  }
+  output = _single_spaces(output);
+  str = trim(output,TRIM_RIGHT);
+  if (show_processing>1)
+    printf("[%s]\n",str);
+  return str;
+}
+
+/** Behandelt alle Sonderfaelle der Eingabe des Spielers
+  Alle Befehle des Spielers, die nicht durch Objekte behandelt
+  werden sollen, werden hier erkannt und ausgefuehrt.
+  Dazu gehoert auch die Interpretation von Aliases und History-
+  befehlen.
+  \param[in] str string: Kommando des Spielers
+  \return auszufuehrendes Kommando
+    oder 0 fuer ein nicht interpretierbares Kommando
+    oder 1 fuer ein bereits durchgefuehrtes Kommando
+*/
+mixed modify_command(string str)
+{
+
+  if (extern_call() && previous_object() &&
+      (previous_object()!=this_object() || process_call()) )
+  {
+    return 0;
+  }
+
+  // Leerzeichen an den Enden abschneiden.
+  str = trim(str, TRIM_BOTH);
+
+  if (bb)
+    BBMASTER->BBWrite(trim(str,TRIM_RIGHT,"\n"), 0);
+
+  decay_average();
+  cmds_per_time+=10000;
+
+  unparsed_args[0]=unparsed_args[1]=unparsed_args[2]=unmodified=""; 
+
+  if (!sizeof(str)) return "";
+
+  // Kommando wird geparst
+  unmodified=parsecommand(str);
+
+  // Environment schonmal merken.
+  last_command_env=environment();
+
+  if (unmodified == "")
+      return "";
+  // Kommando in History merken, auch wenn es im Kommandoblock abgebrochen
+  // wird.
+  AddHistory(unmodified);
+
+  // pruefen, ob Kommandoblock gesetzt ist.
+  // (Fuer Magier mit mschau ein wird das ignoriert.)
+  // BTW: Es wird absichtlich nicht das Ergebnis der Closure zurueckgegeben,
+  // sonst wuerde man beliebigen Objekten nicht nur das Abbrechen, sondern
+  // auch das Aendern von Kommandos ermoeglichen.
+  if (disablecommands && !IS_LEARNING(ME) )
+  {
+    if (disablecommands[B_TIME] >= time()
+      && objectp(disablecommands[B_OBJECT]))
+    {
+      // disablecommands valid
+      // hart-kodierte Ausnameliste pruefen
+      if ( member(({"mrufe","mschau","bug","idee","typo","detail"}),
+            explode(str," ")[0]) == -1)
+      {
+        if (closurep(disablecommands[B_VALUE]))
+        {
+          if (funcall(disablecommands[B_VALUE],_return_args(unmodified)))
+          {
+            // Non-zero Closure-Ergebnis, Abbruch. Die gerufene Funktion ist
+            // fuer eine geeignete Meldung an den Spieler verantwortlich.
+            return 1;
+          }
+        }
+        // wenn Text, dann auch pruefen, ob das Kommandoverb in den Ausnahmen
+        // steht. (query_verb() geht leider hier noch nicht.)
+        else if (stringp(disablecommands[B_VALUE])
+                 && member(disablecommands[B_EXCEPTIONS],
+                           explode(str," ")[0]) == -1)
+        {
+          // meldung ausgeben...
+          tell_object(PL, disablecommands[B_VALUE]);
+          // und Ende...
+          return 1;
+        }
+      }
+    }
+    else disablecommands=0;
+  }
+
+  // Verfolger direkt ins Env reinholen.
+  if (remove_call_out("TakeFollowers")>=0)
+    catch(TakeFollowers();publish);
+
+  DelayPreparedSpells();
+
+  // Historyeintrag korrigieren
+  if (unmodified[0]=='^')
+  {
+    string *oldnew,pre,post;
+    if (sizeof(oldnew=explode(unmodified,"^"))>2)
+    {
+      int hist_idx = (hist_now-1)%hist_size;
+      sscanf(history[hist_idx],"%s"+oldnew[1]+"%s", pre, post);
+      unmodified = pre+oldnew[2]+post;
+      if (show_processing)
+        write("["+unmodified+"]\n");
+      // korrigiertes Kommando natuerlich auch in die History.
+      AddHistory(unmodified);
+    }
+  }
+
+  if( bb )
+    BBMASTER->BBWrite(" -> " + unmodified, 1);
+
+  if (show_processing>1)
+    printf("[%s]\n",unmodified);
+
+  mixed ret = _return_args(unmodified);
+
+  // wenn Spieler eingewilligt hat und die SyntaxDB geladen ist, Befehl
+  // dorthin melden.
+  if (syntaxdb)
+  {
+    if (!objectp(syntaxdb[0]))
+      syntaxdb[0] = find_object("/secure/syntaxdb");
+    if (syntaxdb[0])
+      catch(syntaxdb[0]->start_cmd(unmodified);nolog);
+  }
+  return ret;
+}
+
+static int do_list(string str)
+{
+  string *cmdlist;
+  int i;
+
+  if (!QueryProp(P_WANTS_TO_LEARN))
+    return 0;
+  cmdlist=old_explode(_unparsed_args()||"",";")-({ "" });
+  for (i=0;i<sizeof(cmdlist);i++)
+  {
+    cmdlist[i]=implode(old_explode(cmdlist[i]," ")-({}), " ");
+    if (show_processing)
+      write("["+cmdlist[i]+"]\n");
+    command(cmdlist[i]);
+  }
+  return 1;
+}
+
+//falls die aliasliste kaputt ist ...
+
+int unalias_all()
+{
+  if (IS_ELDER(this_interactive())) aliases=([]);
+  return 1;
+}
+
+object _query_last_command_env()
+{
+  return last_command_env;
+}
+
+int _query_show_alias_processing()
+{
+  return show_processing;
+}
+
+int _query_histmin()
+{
+  return histmin;
+}
+
+varargs void AddAction(mixed fun, mixed cmd, int flag, int lvl)
+{
+  int i;
+  mixed *cmds;
+
+  log_file( "ARCH/ADD_ACTION", sprintf(
+	"%s:\n  TO: %O TP: %O PO: %O\n   fun: %O cmd: %O flag: %O lvl: %O",
+        dtime(time()), this_object(), this_player(), previous_object(),
+	fun, cmd, flag, lvl));
+
+  if (!(cmds=Query(P_LOCALCMDS))) cmds=({});
+
+  if (!pointerp(cmd)) cmd=({cmd});
+
+  for (i = sizeof(cmd)-1; i>=0; i--)
+    cmds += ({({ cmd[i] , fun, flag, lvl})});
+
+  Set(P_LOCALCMDS, cmds);
+}
+
+static int auswerten(mixed cmd, string str)
+{
+  if (closurep(cmd))
+    return funcall(cmd,str);
+  if (stringp(cmd))
+    return call_other(this_object(),cmd,str);
+  return 0;
+}
+
+static varargs int __auswerten(string str, string intern)
+{
+  string verb;
+  mixed *cmd, cmds;
+  int i,ret,lvl,l,vl;
+
+  if (!intern)
+    verb=query_verb();
+  else
+    verb=intern;
+  lvl=query_wiz_level(ME);
+  vl=sizeof(verb);
+  cmds=QueryProp(P_LOCALCMDS);
+
+  for(i=sizeof(cmds)-1;i>=0;i--)
+  {
+    cmd=cmds[i],l=sizeof(cmd[0]);
+    if (cmd[0]==verb[0..l-1] && cmd[3]<=lvl && (cmd[2]||vl==l) &&
+  (ret=auswerten(cmd[1],str)))
+      return ret;
+  }
+  // An dieser Stelle gibt es hier und vermutlich nirgendwo anders etwas, was
+  // dieses Kommando als gueltig betrachtet. Wir informieren ggf. die
+  // Syntax-DB. (Achtung: wenn jemand ne add_action() im Spielerobjekt
+  // einbaut, die vor dieser eingetragen wird, ist die Annahme ggf. falsch.)
+  // wenn Spieler eingewilligt hat und die SyntaxDB geladen ist, Befehl
+  // dorthin melden.
+  if (syntaxdb)
+  {
+    if (!objectp(syntaxdb[0]))
+      syntaxdb[0] = find_object("/secure/syntaxdb");
+    if (syntaxdb[0])
+      catch(syntaxdb[0]->cmd_unsuccessful();nolog);
+  }
+
+  return 0;
+}
+
+public void syntax_log_ep(int type)
+{
+  // wenn Spieler eingewilligt hat und die SyntaxDB geladen ist, Befehl
+  // dorthin melden.
+  if (syntaxdb && syntaxdb[0])
+  {
+      catch(syntaxdb[0]->LogEP(type);nolog);
+  }
+}
+
+static mixed _query_localcmds()
+{
+  mixed *l;
+
+  l=Query(P_LOCALCMDS);
+  if (!pointerp(l))
+    l=({});
+  return ({
+    ({"ali","alias",0,0}),
+    ({"alias","alias",0,0}),
+    ({"unali","unalias",1,0}),
+    ({"histmin","histmin",0,0}),
+    ({"histlen","histlen",0,0}),
+    ({"hist","show_hist",0,0}),
+    ({"history","show_hist",0,0}),
+    ({"do","do_list",0,LEARNER_LVL}),
+    ({"ersetzungsanzeige","replacedisplay",0,0}),
+    ({"syntaxsammlung","collect_cmds",0,0}),
+    ({"fehlermeldung","set_errormessage",0,SEER_LVL}),
+  })+l;
+}
+
+static int collect_cmds(string cmd)
+{
+  if (!stringp(cmd))
+  {
+    _notify("Mit diesem Befehl kannst Du mithelfen, Syntaxen im MG zu "
+            "verbessern. Wenn Du einverstanden bist, speichern wir "
+            "anonym (d.h. ohne Deinen Charnamen), welche Deiner Befehle "
+            "erfolgreich und nicht erfolgreich waren. Uebliche "
+            "Kommunikationsbefehle werden dabei nicht gespeichert.",
+            0);
+    _notify("Mit 'syntaxsammlung ja' kannst Du die Speicherung einschalten, "
+            "mit 'syntaxsammlung nein' kannst Du sie ausschalten.",0);
+    _notify("Deine Befehle werden zur Zeit"
+            + (QueryProp("_syntaxdb") ? " " : " NICHT ")
+            + "gespeichert.", 0);
+  }
+  else if (cmd == "ja")
+  {
+    SetProp("_syntaxdb", 1);
+    _notify("Ab jetzt werden Deine Befehle gespeichert. Vielen Dank!", 0);
+  }
+  else
+  {
+    SetProp("_syntaxdb", 0);
+    _notify("Ab jetzt werden Deine Befehle NICHT gespeichert.", 0);
+  }
+  return 1;
+}
+
+int _query_command_average()
+{
+  decay_average();
+  return cmds_per_time;
+}
+
+nomask void __set_bb(int flag)  {
+  if( previous_object()!=find_object(BBMASTER) || process_call() )
+    return;
+  bb=flag;
+}
+
+
+nomask public void countCmds( int type, string key )
+{
+    string tmp;
+
+    if ( this_player() != this_interactive()
+         || this_interactive() != this_object()
+         || member( cmd_types, type ) < 0 )
+        return;
+
+    tmp = sprintf( "%d\n%s", type, key );
+
+    commands -= ({ tmp });
+    commands += ({ tmp });
+    commands = commands[0..max_commands-1];
+}
+
+
+nomask public string *getCmds()
+{
+    string *tmp;
+
+    if ( previous_object() != find_object(BBMASTER) )
+        return ({});
+
+    tmp = commands;
+    commands = ({});
+
+    return tmp;
+}
+
+/*
+ * Force the monster to do a command. The force_us() function isn't
+ * always good, because it checks the level of the caller, and this function
+ * can be called by a room.
+ */
+int command_me(string cmd)
+{
+  if (IS_LEARNER(ME))
+  {
+    if (!this_interactive() || !previous_object())
+      return 0;
+    if( geteuid(ME)!=geteuid(this_interactive())
+        || geteuid(ME)!=geteuid(previous_object()) )
+    {
+      if( query_wiz_level(ME)<query_wiz_level(previous_object()))
+        tell_object(ME,previous_object()->name()+" zwingt Dich zu: "
+                    + cmd + ".\n");
+      else
+      {
+        tell_object(ME,previous_object()->name()
+                    + " versucht, Dich zu " + cmd + " zu zwingen.\n" );
+        return 0;
+      }
+    }
+  }
+  return command(cmd);
+}
+
+
+static mixed _query_p_lib_disablecommands() {
+    // abgelaufen oder Objekt zerstoert? Weg damit.
+    if (pointerp(disablecommands)
+        && (disablecommands[B_TIME] < time()
+          || !objectp(disablecommands[B_OBJECT])) )
+        return(disablecommands = 0);
+
+    // sonst Kopie zurueck (copy(0) geht)
+    return(copy(disablecommands));
+}
+
+static mixed _set_p_lib_disablecommands(mixed data) {
+
+  // setzendes Objekt ermitteln, da diese Funktion nur per SetProp() gerufen
+  // werden sollte (!), ist das PO(1);
+  object origin = previous_object(1);
+  // wenn nicht existent, direkt abbruch
+  if (!objectp(origin))
+    return _query_p_lib_disablecommands();
+
+  // Prop loeschen? Explizit loeschen darf jeder, allerdings nicht direkt
+  // ungeprueft ueberschreiben.
+  if (!data) {
+      return (disablecommands = 0 );
+  }
+  // mal direkt buggen bei falschen Datentyp, damits auffaellt.
+  if (!pointerp(data) || sizeof(data) < 2 || !intp(data[0])
+      || (!stringp(data[1]) && !closurep(data[1]))
+      || (sizeof(data) >= 3 && !pointerp(data[2])) )
+      raise_error(sprintf(
+            "Wrong data type for P_DISABLE_COMMANDS. Expected Array with "
+            "2 or 3 elements (int, string|closure, [string*]), got %.25O\n",
+            data));
+
+  // Wenn abgelaufen oder gleiches Objekt wie letztes Mal: eintragen.
+  if (!disablecommands || (disablecommands[B_TIME] < time()
+        || !objectp(disablecommands[B_OBJECT]) 
+        || disablecommands[B_OBJECT] == origin) ) {
+      // Loggen nur, wenn eine Closure eingetragen wird. Reduziert den
+      // Logscroll und Strings haben deutlich weniger Missbrauchspotential.
+      if (closurep(data[1])) {
+        CBLOG(sprintf("[%s] CB gesetzt von %O, gueltig bis %s, Daten: %O\n",
+        strftime("%Y%m%d-%H:%M:%S"),origin,
+        strftime("%Y%m%d-%H:%M:%S",data[0]),
+        (stringp(data[1]) ? regreplace(data[1],"\n","\\n",0)
+                          : data[1])));
+      }
+      if (sizeof(data)+1 <= B_EXCEPTIONS)
+        disablecommands = ({ origin, data[0], data[1], ({}) });
+      else
+        disablecommands = ({ origin, data[0], data[1], data[2] });
+      return(copy(disablecommands));
+  }
+
+  return(_query_p_lib_disablecommands());
+}
+
+static mixed _set__syntaxdb(mixed v)
+{
+  Set("_syntaxdb", v, F_VALUE);
+  if (v)
+      syntaxdb = ({find_object("/secure/syntaxdb")});
+  else
+      syntaxdb = 0;
+  return QueryProp("_syntaxdb");
+}
+
diff --git a/std/player/description.c b/std/player/description.c
new file mode 100644
index 0000000..4a0663f
--- /dev/null
+++ b/std/player/description.c
@@ -0,0 +1,292 @@
+// MorgenGrauen MUDlib
+//
+// player/description.c -- player description handling
+//
+// $Id: description.c 8755 2014-04-26 13:13:40Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/description";
+
+#include <thing/language.h>
+#include <player/description.h>
+#include <living/clothing.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <defines.h>
+#include <combat.h>
+#include <sys_debug.h>
+
+#define NEED_PROTOTYPES
+#include <player/command.h>
+
+protected void create()
+{
+  ::create();
+  Set(P_NAME, NOSETMETHOD, F_SET_METHOD);
+  Set(P_PRESAY, SAVE, F_MODE_AS);
+  Set(P_TITLE, SAVE, F_MODE_AS);
+  Set(P_EXTRA_LOOK, SAVE, F_MODE_AS);
+  Set(P_GENDER, SAVE, F_MODE_AS);
+  Set(P_SIZE, SAVE, F_MODE_AS);
+  Set(P_INFO, NOSETMETHOD, F_SET_METHOD);
+  // Avatar-URIs speichern. Ausserdem hat an denen keiner rumzufummeln.
+  Set(P_AVATAR_URI, SAVE|SECURED, F_MODE_AS);
+}
+
+string _query_info()
+{
+  string info;
+  info = Query(P_INFO);
+  if(!info)info=="";
+  info = (string)query_wiz_level(this_object());
+  if(IS_GOD(this_object()))
+    return info+" [Gott]\n";
+  if(IS_ARCH(this_object()))
+    return info+" [Erzmagier]\n";
+  if(IS_ELDER(this_object()))
+    return info+" [Weiser]\n";
+  if(IS_LORD(this_object()))
+    return info+" [Regionschef]\n";
+  if(IS_SPECIAL(this_object()))
+    return info+" [Special]\n";
+  if(IS_DOMAINMEMBER(this_object()))
+    return info+" [Regionsmitarbeiter]\n";
+  if(IS_WIZARD(this_object()))
+    return info+" [Magier]\n";
+  if(IS_LEARNER(this_object()))
+    return info+" [Magieranwaerter]\n";
+  info = QueryProp(P_LEVEL);
+  if(IS_SEER(this_object()))
+    return info+" [Seher]\n";
+  return info+" [Spieler]\n";
+}
+
+int _query_size() {
+  if (QueryProp(P_FROG))
+    return 20;
+  return Query(P_SIZE);
+}
+
+// short() -- get the short description of a player
+string short()
+{
+  string answer;
+  string title;
+
+  if(QueryProp(P_INVIS))
+    if(interactive(previous_object()) && IS_LEARNING(previous_object()))
+      return "("+QueryProp(P_NAME)+") \n";
+    else
+      return 0;
+
+  if(QueryProp(P_GHOST))
+  {
+    if (QueryProp(P_FROG))
+      return "Der Geist eines kleinen Frosches namens "+QueryProp(P_NAME)+
+       ".\n";
+    else
+      return "Der Geist von "+QueryProp(P_NAME)+".\n";
+  }
+
+  if (QueryProp(P_FROG))
+    return "Ein kleiner Frosch namens "+QueryProp(P_NAME)+".\n";
+
+  answer = QueryProp(P_PRESAY) + QueryProp(P_NAME);
+  if (QueryProp(P_ARTICLE)) answer=QueryArticle(0,0)+answer;
+  if((title=QueryProp(P_TITLE)) && title != "") answer += " " + title;
+  if(!interactive(ME)) answer += " (netztot)";
+  return answer+".\n";
+}
+
+private string andlist(object *arr) {
+  string *tmp;
+  if(!pointerp(arr)) return "";
+  if(sizeof(tmp = map_objects(arr, "name", WEN)))
+    return(CountUp(tmp));
+  return "";
+}
+
+// gibt fuer nicht-Invis-Objekte den Namen zurueck
+private string get_vis_name(object ob) {
+  if (ob->QueryProp(P_INVIS))
+    return 0;
+  return (ob->name(WEN));
+}
+
+varargs string long()
+{
+  string exl, descr, tmp, size_descr;
+  object ob;
+  mixed trans, w, a, r;
+  int per;
+  string fill, fill2;
+  /* fuer groessenvergleich */
+  string comparesize, pers1, pers2;
+  int relation;
+
+  per=1000*QueryProp(P_SIZE)/QueryProp(P_AVERAGE_SIZE);
+  switch(per)
+  {
+    case 0..800: size_descr="ziemlich winzig"; break;
+    case 801..850: size_descr="eher winzig"; break;
+    case 851..900: size_descr="recht klein"; break;
+    case 901..950: size_descr="eher klein"; break;
+    case 951..1050: size_descr="mittelgross"; break;
+    case 1051..1100: size_descr="relativ gross"; break;
+    case 1101..1150: size_descr="ziemlich gross"; break;
+    case 1151..1200: size_descr="sehr gross"; break;
+    default: size_descr="riesig"; break;
+  }
+#define RassenString() ((QueryProp(P_FROG) ? "en Frosch" :\
+       (!QueryProp(P_GENDER)?" ":QueryProp(P_GENDER)==2?"e ":"en ")+\
+       (pointerp(QueryProp(P_RACESTRING))?\
+        QueryProp(P_RACESTRING)[WEN]:QueryProp(P_RACE))))
+  fill2=fill=0;
+  if (QueryProp(P_AVERAGE_SIZE)<170)
+  {
+    if (per<950)
+      fill="selbst fuer ein"+RassenString()+" ";
+    else
+      if (per>1050)
+  fill2=", wenn auch nur fuer ein"+RassenString();
+  }
+  else
+  {
+    if (QueryProp(P_AVERAGE_SIZE)>170)
+      if (per>1050)
+  fill="sogar fuer ein"+RassenString()+" ";
+      else
+  if (per<950)
+    fill2=", wenn auch nur fuer ein"+RassenString();
+  }
+  if (!fill&&!fill2) fill="fuer ein"+RassenString()+" ";
+  descr = "Das ist "+name(WER,1)+". "+capitalize(QueryPronoun())+" ist "+
+    (fill||"")+size_descr+(fill2||"")+".\n";
+
+  if(this_player()) {
+    /* groessenvergleich_anfang (NEU) */
+    pers1 = QueryPronoun(WER);
+    pers2 = QueryPronoun(WEM);
+ 
+    // || falls TP keine Groesse gesetzt hat... Warum auch immer...
+    relation = (QueryProp(P_SIZE)*100) / 
+                  (this_player()->QueryProp(P_SIZE) || 1);
+    switch (relation) 
+     {
+     case   0 ..  25 : comparesize = "Damit gibt "+pers1+" einen guten"
+                                     " Fusschemel fuer Dich ab"; 
+                                     break;
+     case  26 ..  50 : comparesize = "Damit reicht "+pers1+" Dir nicht mal bis"
+                                     " zur Huefte"; 
+                                     break;
+     case  51 ..  75 : comparesize = "Damit kannst Du "+pers2+" locker auf den"
+                                     " Kopf spucken"; 
+                                     break;
+     case  76 ..  90 : comparesize = "Damit ist "+pers1+" einen Kopf kleiner"
+                                     " als Du"; 
+                                     break;
+     case  91 .. 110 : comparesize = "Damit hat "+pers1+" etwa Deine Groesse";
+                                     break;  
+     case 111 .. 120 : comparesize = "Damit ist "+pers1+" einen Kopf groesser" 
+                                     " als Du";
+                                     break;
+     case 121 .. 150 : comparesize = "Damit holst Du Dir einen steifen Nacken,"
+                                     " wenn Du "+pers2+" in die Augen siehst";
+                                     break;
+     case 151 .. 200 : comparesize = "Damit versperrt "+pers1+" Dir absolut"
+		                     " die Sicht"; 
+                                     break;
+     case 201 .. 300 : comparesize = "Damit waere jeder Schlag von Dir ein" 
+                                     " Tiefschlag";
+                                     break;
+     default         : comparesize = "Damit kannst Du "+pers2+" bequem zwischen"
+                                     " den Beinen durchlaufen";
+                                     break;
+     }
+  
+     descr+=comparesize+".\n";
+     /* groessenvergleich_ende (NEU) */
+   }
+
+  if(QueryProp(P_GHOST)) return descr;
+
+  trans = QueryProp(P_TRANSPARENT); SetProp(P_TRANSPARENT, 0);
+  descr += ::long(); SetProp(P_TRANSPARENT, trans);
+
+  // Waffen, Ruestungen/Kleidung und Sonstiges ermitteln
+  w = ({QueryProp(P_WEAPON), QueryProp(P_PARRY_WEAPON)}) - ({0});
+  a = QueryProp(P_ARMOURS) + QueryProp(P_CLOTHING) - ({0});
+  r = all_inventory(ME) - w - a; //Rest logischerweise
+
+  // rest noch nach sichbarkeit von ausserhalb des SPielers filtern.
+  r=map(r,function string (object ob) {
+	  if(sizeof(all_inventory(ob)) || ob->QueryProp(P_LIGHT) || 
+	     ob->QueryProp(P_LIGHTED) || ob->QueryProp(P_SHOW_INV))
+	    return(get_vis_name(ob));
+          return(0); } ) - ({0});
+
+  // Invis-Objekte ausfiltern und Namen ermitteln, anschliessend sind in w, a
+  // und r je ein Array von Strings
+  w = map(w, #'get_vis_name) - ({0});
+  a = map(a, #'get_vis_name) - ({0});
+
+  return descr +
+         (QueryProp(P_TRANSPARENT) ?
+          break_string(capitalize(name(WER, 1))+" hat "
+                      +(sizeof(a)?CountUp(a):"keine Ruestung")+" an"
+                      +(sizeof(r)?", ":" und ")
+                      +(sizeof(w)?CountUp(w):"keine Waffe")+" gezueckt"
+          +(sizeof(r)?" und traegt "+CountUp(r):"")+".", 78): "");
+}
+
+
+// **** local property methods
+static mixed _query_presay()
+{
+  string presay;
+  if((presay = Query(P_PRESAY)) && (presay != "")) return presay + " ";
+  return "";
+}
+
+static string _query_name()
+{
+  return capitalize(Query(P_NAME) || "NoName");
+}
+// ****
+
+// Local commands
+static mixed _query_localcmds() {
+  return
+    ({
+      ({"avatar","set_avataruri",0,0}),
+    });
+}
+
+int set_avataruri(string arg) {
+  arg = _unparsed_args(0);
+  if (!arg || !sizeof(arg)) {
+    string uri = QueryProp(P_AVATAR_URI);
+    if (stringp(uri))
+      tell_object(ME,
+          "Aktuelle Avatar-URI: " + uri + "\n");
+    else
+      tell_object(ME, "Du hast keine Avatar-URI eingestellt.\n");
+  }
+  else if (arg == "keine") {
+      SetProp(P_AVATAR_URI, 0);
+      tell_object(ME, "Deine Avatar-URI wurde geloescht.\n");
+  }
+  else if (sizeof(arg) > 512)
+      tell_object(ME, "Deine neue Avatar-URI ist zu lang!\n");
+  else {
+      SetProp(P_AVATAR_URI, arg);
+      tell_object(ME,
+          "Aktuelle Avatar-URI: " + arg + "\n");
+  }
+  return 1;
+}
+
diff --git a/std/player/exploration.c b/std/player/exploration.c
new file mode 100644
index 0000000..8a1976e
--- /dev/null
+++ b/std/player/exploration.c
@@ -0,0 +1,125 @@
+// MorgenGrauen MUDlib
+//
+// player/exploration.c -- exploration point management
+//
+// $Id: exploration.c 9142 2015-02-04 22:17:29Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <player/life.h>
+#include <player/base.h>
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+#include <exploration.h>
+#include <scoremaster.h>
+#include <properties.h>
+#include <new_skills.h>
+
+private string given_scores;
+
+private nosave mixed epnum;
+
+void create() {
+  Set(P_LEP, SECURED|SAVE, F_MODE_AS);
+
+  given_scores = "";
+}
+
+string Forschung()
+{
+  return EPMASTER->QueryForschung();
+}
+
+static string _query_given_scores()
+{
+  return given_scores;
+}
+
+// Hier kommen Funktionen fuer die Levelpunkte
+
+#define XP_FAC ([1:10,2:40,3:150,4:600,5:2250,6:9000,7:35000,8:140000,9:500000])
+
+//#define DEBUG(x,y) printf(x,y)
+#define DEBUG(x,y)
+
+int AddScore(int contributor)
+{
+  mixed info;
+  object po;
+  int drin;
+  
+  if (!pointerp(info = SCOREMASTER->QueryNPCbyNumber(contributor)))
+    return -1;
+
+  if ((po = previous_object()) && (object_name(po) == SCOREMASTER))
+    po = previous_object(1);
+
+  if (!po || old_explode(object_name(po),"#")[0] != info[SCORE_KEY])
+    return -2;
+
+  if (!stringp(given_scores))
+    given_scores = " ";
+
+  if (catch(drin = test_bit(given_scores, contributor);publish))
+    return -3;
+
+  if (!drin) {
+    given_scores = set_bit(given_scores, contributor);
+    Set(P_LEP, Query(P_LEP) + info[SCORE_SCORE]);
+    force_save();
+    return info[SCORE_SCORE];
+  }
+  return 0;
+}
+
+int TestScore(int contributor)
+{
+  int ret;
+  
+  if (!previous_object() || (object_name(previous_object()) != SCOREMASTER))
+    return 0;
+
+  catch(ret = test_bit(given_scores, contributor);publish);
+
+  return ret;
+}
+
+int SetScoreBit(int contributor)
+{
+  int drin;
+  
+  if (!previous_object() || (object_name(previous_object()) != SCOREMASTER))
+    return -1;
+
+  if (catch(drin = test_bit(given_scores, contributor);publish))
+    return -2;
+
+  if (drin) return -3;
+
+  given_scores = set_bit(given_scores, contributor);
+  force_save();
+  return 1;
+}
+
+int ClearScoreBit(int contributor)
+{
+  int drin;
+  
+  if (!previous_object() || (object_name(previous_object()) != SCOREMASTER))
+    return -1;
+
+  if (catch(drin = test_bit(given_scores, contributor);publish))
+    return -2;
+
+  if (!drin) return -3;
+
+  given_scores = clear_bit(given_scores, contributor);
+  force_save();
+  return 1;
+}
+
diff --git a/std/player/guide.c b/std/player/guide.c
new file mode 100644
index 0000000..fb08b43
--- /dev/null
+++ b/std/player/guide.c
@@ -0,0 +1,182 @@
+// MorgenGrauen MUDlib
+// 
+// player/guide.c -- newbie guide handling 
+//
+// $Id: guide.c 7391 2010-01-25 22:52:51Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <config.h>
+#define NEED_PROTOTYPES
+#include <player/user_filter.h> // fuer is_active_guide()
+#include <properties.h>
+#include <defines.h>
+#include <thing/properties.h>
+
+/* Funktion, die die Guide-Kommandos aktiviert */
+void add_guide_commands()
+{
+	add_action("CiceroneCmd","cicerone");
+	// Sollte eigentlich an derselben Stelle moeglich sein.
+}
+
+/* Gibt die Meldung beim Aendern aus*/
+protected int NewbieChangeMsg() {
+    int cic=QueryProp(P_NEWBIE_GUIDE);
+    // begrenzen auf 1 Tag, falls jemand da Schrott reingschrieben hat.
+    if (cic > 86400) {
+	cic=86400;
+	SetProp(P_NEWBIE_GUIDE,cic);
+    }
+    if (cic<=0) {
+	write("Du bist jetzt kein Cicerone mehr.\n");
+    }
+    else if (cic < 60) {
+	write("Du bist jetzt ein Cicerone.\n");
+    }
+    else {
+	write(break_string(
+	    "Du bist jetzt ein Cicerone, allerdings nur in den Zeiten, "
+	    "in denen Du weniger als " + cic/60
+	    + ((cic/60)<2 ? " Minute ":" Minuten ")
+	    + "idle bist.\n",78));
+    }
+    return 1;
+}
+
+/* Gibt die Statusmeldung aus */
+protected int NewbieStatusMsg() {
+  int cic=QueryProp(P_NEWBIE_GUIDE);
+  // begrenzen auf 1 Tag, falls jemand da Schrott reingschrieben hat.
+  if (cic > 86400) {
+      cic=86400;
+      SetProp(P_NEWBIE_GUIDE,cic);
+  }
+
+  if (cic <= 0)
+    write ("Du bist kein Cicerone.\n");
+  else if (cic < 60)
+    write ("Du stehst Neuspielern als Cicerone zur Verfuegung.\n");
+  else {
+    write(break_string(
+	    "Du stehst Neuspielern als Cicerone zur Verfuegung, allerdings "
+	    "nur in den Zeiten, in denen Du weniger als " + cic/60
+	    + ((cic/60)<2 ? " Minute ":" Minuten ")
+	    + "idle bist.\n",78));
+  }
+  return 1;
+}
+
+/* Fuehrt das eigentliche Kommando aus*/
+
+int CiceroneCmd(string str)
+{
+	if (QueryProp(P_LEVEL)<20)
+	{
+		write(break_string("Du solltest erst noch ein wenig "
+					"Erfahrung sammeln, bevor Du Dich "
+				       "als Cicerone zur Verfuegung stellst.",78));
+		return 1;
+	}
+	// Idlezeit uebergeben?
+	int idle=to_int(str);
+	// max. einen Tag (1440 min) zulassen.
+	if (idle < 0) idle=0;
+	else if (idle > 1440) idle=1440;
+
+	if (!str) {
+	    return NewbieStatusMsg();
+	}
+	else if (str=="status") {
+	    return NewbieStatusMsg();
+	}
+	// "ein" schaltet einfach generell ein, hierbei steht 1 in der Prop
+	// fuer "permanent ein".
+	else if (str=="ein") {
+	    SetProp(P_NEWBIE_GUIDE,1);
+	    return NewbieChangeMsg();
+	}
+	// "aus" oder "0" deaktiviert.
+	else if (str=="aus") {
+	    SetProp(P_NEWBIE_GUIDE,0);
+	    return NewbieChangeMsg();
+	}
+	// wenn Zahl uebergeben ist, die groesser 0 und kleiner 1440 ist
+	// (s.o.), wird es als Anzahl an Idle-Minuten aufgefasst, ab der man
+	// ausgeblendet werden will.
+	else if (idle) {
+	    SetProp(P_NEWBIE_GUIDE, idle*60); // als Sekunden speichern.
+	    return NewbieChangeMsg();
+	}
+	write(break_string(
+	      "cicerone ein  - Du bist Cicerone\n"
+	      "cicerone aus  - Du bist kein Cicerone\n"
+	      "cicerone      - Status anzeigen\n"
+	      +break_string(
+		"Du bist Cicerone, aber wenn Du laenger als <zahl> Minuten "
+		"idle bist, wirst Du automatisch ausgeblendet, bis Du wieder "
+		"endidelt bist.",
+		76,"cicerone zahl - ",BS_INDENT_ONCE),
+	      78,"Syntaxhilfe:",BS_PREPEND_INDENT|BS_LEAVE_MY_LFS));
+
+	return 1;
+}
+
+protected string IstSindMsg(string* namen)
+{
+	if (sizeof(namen)==1)
+		return "ist davon "+namen[0];
+	else
+		return "sind davon "+CountUp(namen);
+}
+
+void NewbieIntroMsg()
+{
+	object* cicerones;
+	string* namen;
+	string restext;
+
+	// Nur bis Level 5 wird etwas ausgegeben.
+	if (QueryProp(P_LEVEL)>5) return;
+	
+	// is_active_guide() ist in /std/user_filter.c, welches vom
+	// Spielerobjekt geerbt wird und damit zur Verfuegung steht.
+	cicerones=filter(users(),#'is_active_guide);
+	// uid verwenden, da sonst kleine Spieler einen getarnten 
+	// "Riesen" oder aehnliches anstprechen.
+	namen=map(cicerones,function string (object o) 
+	    { return(capitalize(geteuid(o))); } );
+
+	if (namen && sizeof(namen)>0)
+	{
+		restext="\nEs gibt einige nette Spieler, die bereit sind, Dich "
+				"auf Deinen ersten Schritten im "MUDNAME
+				" zu begleiten. \n\nDerzeit "
+				+IstSindMsg(namen)+" eingeloggt. Du kannst "
+				"einen oder eine von Ihnen ansprechen, "
+				"indem Du z.B. einfach \n"
+				"  'teile "+
+				lower_case(namen[random(sizeof(namen))])+
+				" mit Hallo ich bin neu hier, kannst Du "
+				"mir bitte helfen?'\n"
+				"eintippst. Nur keine Scheu, diese Spieler "
+				"haben sich freiwillig dazu bereiterklaert!\n"
+				"\nDu kannst Dir diese Spieler jederzeit "
+				"mit 'kwer cicerones' anzeigen lassen.\n\n";
+		write(break_string(restext,78,"*  ",BS_LEAVE_MY_LFS));
+	}
+	else
+	{
+		// Weia, kein Newbie-Guide da. Lieber erstmal nix tun,
+		// bis uns was besseres einfaellt.
+	}
+	return;
+}
+
+
+
+
diff --git a/std/player/invmaster/gfx/Amulett_female b/std/player/invmaster/gfx/Amulett_female
new file mode 100644
index 0000000..5085026
--- /dev/null
+++ b/std/player/invmaster/gfx/Amulett_female
@@ -0,0 +1,5 @@
+6
+35
+5
+\_/
+?&
diff --git a/std/player/invmaster/gfx/Amulett_male b/std/player/invmaster/gfx/Amulett_male
new file mode 100644
index 0000000..224e09c
--- /dev/null
+++ b/std/player/invmaster/gfx/Amulett_male
@@ -0,0 +1,5 @@
+6
+36
+5
+\_/
+?&
diff --git a/std/player/invmaster/gfx/Beschriftung b/std/player/invmaster/gfx/Beschriftung
new file mode 100644
index 0000000..afddc5e
--- /dev/null
+++ b/std/player/invmaster/gfx/Beschriftung
@@ -0,0 +1,24 @@
+7
+0
+0
+??????????????????????????????????????????____________________<Helm>
+?<Waffe>______________
+??????????????????????\?????????????????????_____________<Koecher>
+
+??????????????????????????????????????????????_____________<Amulett>
+???????????????????????????????????????????????______
+?????????????????????????????????????????????????????\
+??????????????????????????????????????????????????????<Panzer>
+
+?<Ring>_____________/
+?????????????????????/??????????????????????????___________<Schild>
+?<Handschuhe>_______/??????????/
+??????????????????????????????/
+?<Guertel>___________________/
+
+?????????????????????????????????????????????\____________<Umhang>
+
+???????????????????????????????????????????_________<Hose>
+
+
+?????????<Schuhe>_____________
diff --git a/std/player/invmaster/gfx/Guertel_female b/std/player/invmaster/gfx/Guertel_female
new file mode 100644
index 0000000..83ed884
--- /dev/null
+++ b/std/player/invmaster/gfx/Guertel_female
@@ -0,0 +1,4 @@
+5
+33
+10
+`==O=='
diff --git a/std/player/invmaster/gfx/Guertel_male b/std/player/invmaster/gfx/Guertel_male
new file mode 100644
index 0000000..25c14a4
--- /dev/null
+++ b/std/player/invmaster/gfx/Guertel_male
@@ -0,0 +1,4 @@
+5
+33
+10
+`===O==='
diff --git a/std/player/invmaster/gfx/Handschuh_female b/std/player/invmaster/gfx/Handschuh_female
new file mode 100644
index 0000000..8e3f19f
--- /dev/null
+++ b/std/player/invmaster/gfx/Handschuh_female
@@ -0,0 +1,10 @@
+4
+21
+7
+?_,-_
+(__,I
+
+??????????????????????|7
+??????????????????????/|
+?????????????????????`)/
+??????????????????????'
diff --git a/std/player/invmaster/gfx/Handschuh_male b/std/player/invmaster/gfx/Handschuh_male
new file mode 100644
index 0000000..7b81dd1
--- /dev/null
+++ b/std/player/invmaster/gfx/Handschuh_male
@@ -0,0 +1,10 @@
+4
+22
+7
+,--__
+(__,I
+???????????????????????_?
+??????????????????????| |
+??????????????????????/ |
+??????????????????????`))
+?????????????????????? ' 
diff --git a/std/player/invmaster/gfx/Helm_female b/std/player/invmaster/gfx/Helm_female
new file mode 100644
index 0000000..63ba647
--- /dev/null
+++ b/std/player/invmaster/gfx/Helm_female
@@ -0,0 +1,6 @@
+1
+34
+0
+,-o-.
+====I
+???\_\
diff --git a/std/player/invmaster/gfx/Helm_male b/std/player/invmaster/gfx/Helm_male
new file mode 100644
index 0000000..2c188a3
--- /dev/null
+++ b/std/player/invmaster/gfx/Helm_male
@@ -0,0 +1,6 @@
+1
+35
+0
+,-o-.
+====I
+???\_\
diff --git a/std/player/invmaster/gfx/Hosen_female b/std/player/invmaster/gfx/Hosen_female
new file mode 100644
index 0000000..a9cfdd6
--- /dev/null
+++ b/std/player/invmaster/gfx/Hosen_female
@@ -0,0 +1,13 @@
+6
+32
+10
+/`-----'\
+|/     \|
+|   Y   |
+|   |   |
+?|  |  |
+?(  |  )
+?|  |  |
+?|  |  |
+?|  |  |
+?L_/ \_J
diff --git a/std/player/invmaster/gfx/Hosen_male b/std/player/invmaster/gfx/Hosen_male
new file mode 100644
index 0000000..e8e6390
--- /dev/null
+++ b/std/player/invmaster/gfx/Hosen_male
@@ -0,0 +1,13 @@
+6
+33
+10
+/`-----'\
+|/     \|
+|   Y   |
+|   |   |
+|   |   |
+<.  |  .>
+|   |   |
+|   |   |
+|   |   |
+?L_/ \_J
diff --git a/std/player/invmaster/gfx/Koecher_female b/std/player/invmaster/gfx/Koecher_female
new file mode 100644
index 0000000..de0b1cf
--- /dev/null
+++ b/std/player/invmaster/gfx/Koecher_female
@@ -0,0 +1,5 @@
+7
+40
+3
+"""
+//
diff --git a/std/player/invmaster/gfx/Koecher_male b/std/player/invmaster/gfx/Koecher_male
new file mode 100644
index 0000000..de0b1cf
--- /dev/null
+++ b/std/player/invmaster/gfx/Koecher_male
@@ -0,0 +1,5 @@
+7
+40
+3
+"""
+//
diff --git a/std/player/invmaster/gfx/Ring_female b/std/player/invmaster/gfx/Ring_female
new file mode 100644
index 0000000..00f8b00
--- /dev/null
+++ b/std/player/invmaster/gfx/Ring_female
@@ -0,0 +1,4 @@
+6
+21
+8
+c
diff --git a/std/player/invmaster/gfx/Ring_male b/std/player/invmaster/gfx/Ring_male
new file mode 100644
index 0000000..03620a8
--- /dev/null
+++ b/std/player/invmaster/gfx/Ring_male
@@ -0,0 +1,4 @@
+6
+22
+8
+c
diff --git a/std/player/invmaster/gfx/Ruestung_female b/std/player/invmaster/gfx/Ruestung_female
new file mode 100644
index 0000000..8849f35
--- /dev/null
+++ b/std/player/invmaster/gfx/Ruestung_female
@@ -0,0 +1,9 @@
+1
+26
+4
+??????___???___
+?????/(  \_/  )\
+????/ |\_____/| \
+___/ /?\_/_\_/?\ \
+|__\'???)\_/(???\/|
+???????/_____\???||
diff --git a/std/player/invmaster/gfx/Ruestung_male b/std/player/invmaster/gfx/Ruestung_male
new file mode 100644
index 0000000..e59b67c
--- /dev/null
+++ b/std/player/invmaster/gfx/Ruestung_male
@@ -0,0 +1,9 @@
+1
+25
+4
+?????_^____???____^_?
+????(  \   \_/   /  )
+????/\ |\___ ___/| /\
+?__/  /?\_     _/?\_ >
+I___\>???)\___/(???| |
+????????/_______\??|_|
diff --git a/std/player/invmaster/gfx/Schild_female b/std/player/invmaster/gfx/Schild_female
new file mode 100644
index 0000000..fc49eea
--- /dev/null
+++ b/std/player/invmaster/gfx/Schild_female
@@ -0,0 +1,10 @@
+7
+43
+7
+??|\
+??||
+??||
+==||)
+??||
+??||
+??|/
diff --git a/std/player/invmaster/gfx/Schild_male b/std/player/invmaster/gfx/Schild_male
new file mode 100644
index 0000000..55b329f
--- /dev/null
+++ b/std/player/invmaster/gfx/Schild_male
@@ -0,0 +1,10 @@
+7
+45
+7
+??|\
+??||
+??||
+==||)
+??||
+??||
+??|/
diff --git a/std/player/invmaster/gfx/Schuhe_female b/std/player/invmaster/gfx/Schuhe_female
new file mode 100644
index 0000000..dbe958d
--- /dev/null
+++ b/std/player/invmaster/gfx/Schuhe_female
@@ -0,0 +1,7 @@
+5
+32
+18
+??_???_
+?|_|?|_|
+?/ J?L \
+(_/???\_)
diff --git a/std/player/invmaster/gfx/Schuhe_male b/std/player/invmaster/gfx/Schuhe_male
new file mode 100644
index 0000000..2b33014
--- /dev/null
+++ b/std/player/invmaster/gfx/Schuhe_male
@@ -0,0 +1,7 @@
+5
+32
+18
+???_???_
+??|_|?|_|
+?// J?L \\
+(__/???\__)
diff --git a/std/player/invmaster/gfx/Umhang_female b/std/player/invmaster/gfx/Umhang_female
new file mode 100644
index 0000000..2d885ba
--- /dev/null
+++ b/std/player/invmaster/gfx/Umhang_female
@@ -0,0 +1,17 @@
+2
+31
+4
+??_?????_
+??V?????V
+
+
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+|?????????|
+?\???????/
diff --git a/std/player/invmaster/gfx/Umhang_male b/std/player/invmaster/gfx/Umhang_male
new file mode 100644
index 0000000..0025dbe
--- /dev/null
+++ b/std/player/invmaster/gfx/Umhang_male
@@ -0,0 +1,17 @@
+4
+31
+4
+???_?????_
+???V?????V
+
+
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+|???????????|
+?\_???????_/
diff --git a/std/player/invmaster/gfx/axe b/std/player/invmaster/gfx/axe
new file mode 100644
index 0000000..9927f48
--- /dev/null
+++ b/std/player/invmaster/gfx/axe
@@ -0,0 +1,13 @@
+7
+19
+1
+?__????__
+// \()/ \\
+||  ||  ||
+\\_/||\_//
+????||
+????||
+
+
+????||
+????\/
diff --git a/std/player/invmaster/gfx/base_female b/std/player/invmaster/gfx/base_female
new file mode 100644
index 0000000..9eaec1a
--- /dev/null
+++ b/std/player/invmaster/gfx/base_female
@@ -0,0 +1,25 @@
+3
+0
+0
+                                                                              
+                                   __                                         
+                                  |  \\                                       
+                                  |_ /|                                       
+                                ___| ||__                                     
+                               /  `   '  \                                    
+                              / |\_/ \_/| \                                   
+                       .-___,/ / \     / \ \                                  
+                       `--..__'   )   (   \ |                                 
+                                 /  .  \   ||                                 
+                                |  \ /  |  /|                                 
+                                |   Y   |  |'                                 
+                                 \  |  /                                      
+                                 |  |  |                                      
+                                 |  |  |                                      
+                                 (  |  )                                      
+                                 | | | |                                      
+                                 | ) ( |                                      
+                                 | ) ( |                                      
+                                 | | | |                                      
+                                 / J L \                                      
+                                '''   ```                                     
diff --git a/std/player/invmaster/gfx/base_male b/std/player/invmaster/gfx/base_male
new file mode 100644
index 0000000..b836653
--- /dev/null
+++ b/std/player/invmaster/gfx/base_male
@@ -0,0 +1,25 @@
+3
+0
+0
+                                                                              
+                                    ___                                       
+                                   |  \\                                      
+                                   |_ /|                                      
+                                ____| |____                                   
+                              /^   ` | '   ^\                                 
+                             / ,|\__/ \__/|.,\                                
+                      ,--___| '/ \  ___  / \_`\                               
+                      |_..___.'   )/_|_\(   | |                               
+                                  /\_|_/\   \ |                               
+                                 | .\ /. |   ||                               
+                                 l|  Y  |l  / |                               
+                                 ||  |  ||   \|                               
+                                 |` '|` '|                                    
+                                  |  |  |                                     
+                                  (  |  )                                     
+                                  | | | |                                     
+                                  |  |  |                                     
+                                  |  |  |                                     
+                                  | | | |                                     
+                                 / J. .L \                                    
+                                '''     ```                                   
diff --git a/std/player/invmaster/gfx/club b/std/player/invmaster/gfx/club
new file mode 100644
index 0000000..5566ece
--- /dev/null
+++ b/std/player/invmaster/gfx/club
@@ -0,0 +1,13 @@
+7
+21
+0
+??__
+_/__\_
+_|__|_
+_|__|_
+?\??/
+??||
+??||
+
+
+??()
diff --git a/std/player/invmaster/gfx/fernwaffe b/std/player/invmaster/gfx/fernwaffe
new file mode 100644
index 0000000..30897e8
--- /dev/null
+++ b/std/player/invmaster/gfx/fernwaffe
@@ -0,0 +1,19 @@
+7
+23
+0
+???)
+??/.
+?/?.
+|??.
+|??.
+|??.
+I??.
+
+
+I??.
+|??.
+|??.
+|??.
+?\?.
+??\.
+???)
diff --git a/std/player/invmaster/gfx/knife b/std/player/invmaster/gfx/knife
new file mode 100644
index 0000000..f6a4ecd
--- /dev/null
+++ b/std/player/invmaster/gfx/knife
@@ -0,0 +1,9 @@
+7
+23
+4
+?,
+(|
+(|
+--
+
+?U
diff --git a/std/player/invmaster/gfx/magic b/std/player/invmaster/gfx/magic
new file mode 100644
index 0000000..06c594b
--- /dev/null
+++ b/std/player/invmaster/gfx/magic
@@ -0,0 +1,10 @@
+7
+20
+0
+,
+?.?'??.
+?.\|/?,
+-?-+-?-
+,?/|\?.
+.?,.???.
+???\/
diff --git a/std/player/invmaster/gfx/misc b/std/player/invmaster/gfx/misc
new file mode 100644
index 0000000..74b5281
--- /dev/null
+++ b/std/player/invmaster/gfx/misc
@@ -0,0 +1,11 @@
+7
+23
+4
+/\
+||
+||
+
+
+||
+||
+`'
diff --git a/std/player/invmaster/gfx/spear b/std/player/invmaster/gfx/spear
new file mode 100644
index 0000000..4ae9d6c
--- /dev/null
+++ b/std/player/invmaster/gfx/spear
@@ -0,0 +1,25 @@
+7
+23
+0
+/\
+!|
+\/
+||
+||
+||
+||
+
+
+||
+||
+||
+||
+||
+||
+||
+||
+||
+||
+||
+||
+`'
diff --git a/std/player/invmaster/gfx/staff b/std/player/invmaster/gfx/staff
new file mode 100644
index 0000000..58446d0
--- /dev/null
+++ b/std/player/invmaster/gfx/staff
@@ -0,0 +1,22 @@
+7
+23
+0
+,.
+||
+||
+||
+||
+||
+||
+
+
+||
+||
+||
+||
+||
+||
+||
+||
+||
+`'
diff --git a/std/player/invmaster/gfx/sword b/std/player/invmaster/gfx/sword
new file mode 100644
index 0000000..e0ff2dc
--- /dev/null
+++ b/std/player/invmaster/gfx/sword
@@ -0,0 +1,13 @@
+7
+22
+0
+??,
+?||
+?||
+?||
+?||
+?||
+_||_
+
+
+?`'
diff --git a/std/player/invmaster/gfx/whip b/std/player/invmaster/gfx/whip
new file mode 100644
index 0000000..00db843
--- /dev/null
+++ b/std/player/invmaster/gfx/whip
@@ -0,0 +1,21 @@
+7
+16
+2
+?? ,_ 
+? /  \ 
+ / ?? \
+ | ??? |
+ | ??? ^
+ |
+? \
+?? | ??#
+?? / 
+? / 
+? | 
+? / 
+ / 
+ | 
+ | 
+ | 
+ \ 
+? ` 
diff --git a/std/player/invmaster/invmaster.c b/std/player/invmaster/invmaster.c
new file mode 100644
index 0000000..06ccda0
--- /dev/null
+++ b/std/player/invmaster/invmaster.c
@@ -0,0 +1,464 @@
+// invmaster.c by Nachtwind@MG V1.1 (5.3.2001)
+// A small master that provides a graphical display of the player´s
+// equipment. 
+#pragma strong_types
+#pragma save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <input_to.h>
+#include <properties.h>
+#include <ansi.h>
+#include <combat.h>
+#include <language.h>
+#include "invmaster.h"
+
+
+mapping data;
+closure abbreviate;
+
+
+// i'm aware this can be determined with m_indices(VALID_ARMOUR_TYPE),
+// but the position in the arrays is important for the drawing order.
+// first item in the array is drawn last
+static string *armour_types = 
+({AT_BELT,
+  AT_SHIELD,
+  AT_HELMET,
+  AT_BOOT,
+  AT_TROUSERS,
+  AT_AMULET,
+  AT_RING,
+  AT_GLOVE,
+  AT_QUIVER,
+  AT_CLOAK,
+  AT_ARMOUR});
+
+static mapping colors = 
+([0:ANSI_BLACK,
+  1:ANSI_RED, 
+  2:ANSI_GREEN,
+  3:ANSI_YELLOW,
+  4:ANSI_BLUE,
+  5:ANSI_PURPLE,
+  6:ANSI_CYAN,
+  7:ANSI_WHITE,
+  8:""]);
+  
+static mapping bgcolors = 
+([0:ANSI_BG_BLACK,
+  1:ANSI_BG_RED, 
+  2:ANSI_BG_GREEN,
+  3:ANSI_BG_YELLOW,
+  4:ANSI_BG_BLUE,
+  5:ANSI_BG_PURPLE,
+  6:ANSI_BG_CYAN,
+  7:ANSI_BG_WHITE,
+  8:""]);
+  
+
+
+static string Mapping2ColoredText(mapping pic, object player);
+static string Mapping2PlainText(mapping pic);
+static void AddDescription(mapping pic, string type, object item);
+static string ComposeDesc(object item);
+static void ConfigureColors(string text);
+
+void ShowInv(object player, string arg);
+
+// ok, let´s just read in the graphics...
+// really takes some time (~250 eval ticks) but is only done
+// once in an uptime
+void create()
+{
+  mapping pic;
+  string *files, *lines, text;
+  int i,j,k, indentx,indenty, color;
+
+  data=([]);
+  
+  DB("Trying to fire up master, path is '"+INVPATH+"'...");
+  files=get_dir(INVPATH+"gfx/*")-({".", ".."});
+  DB(sprintf("Files found in 'gfx/': \n%O", files));
+  for (i=sizeof(files)-1;i>=0;i--)
+  {
+    DB("Reading '"+files[i]+"' ...");
+    text=read_file(INVPATH+"gfx/"+files[i]);
+    if (!stringp(text))
+    {
+      DB("Failed to read file.");
+      continue;
+    }
+    lines=explode(text, "\n");
+    if (sizeof(lines) < 4)
+    {
+      DB("File corrupt.");
+      continue;
+    }
+    indentx=to_int(lines[1]);
+    indenty=to_int(lines[2]);
+    color=to_int(lines[0]);
+    pic=([]);
+    for (j=sizeof(lines)-1;j>2;j--)
+    {
+      for (k=sizeof(lines[j])-1;k>=0;k--)
+      {
+        if (lines[j][k..k]!="?")
+          pic+=([(j-3+indenty)*80+k+indentx:lines[j][k..k];color]);
+      }
+    }
+    data+=([files[i]:pic]);
+    DB("File successfully read.");
+  }
+  DB(sprintf("Types covered:\n%O\n", m_indices(data)));
+
+  // create closure only once to save time
+  // needed by ComposeDesc()
+  // the closure ist not as complicated as it seems ;)
+  // it just checks every word of the name, if it does not begin
+  // with a capital letter, it is abbreviated
+  // this happens only if the name length exceeds 20 chars...
+  abbreviate=lambda(({'x}), 
+      ({#'?, ({#'>, ({#'member, quote(({"der", "des"})), 'x}), 0}),
+        "d.",
+        ({#'?, ({#'>, ({#'sizeof, 'x}), 3}), 
+          ({#'?, ({#',, ({#'=, 'a, ({#'allocate, 1}) }),
+                        ({#'=, ({#'[, 'a, 0}), 'x }), 
+                        ({#'sizeof, ({#'regexp, 'a, "^[a-z].*"}) }) 
+                 }), 
+            ({#'+, ({#'[..], 'x, 0, 1}), "."}),
+            'x
+          }), 
+          'x
+        })
+      }));
+}
+
+// function that tries to guess a good item name and use abbrevations
+// where possible
+static string ComposeDesc(object item)
+{
+  int i;
+  string text, *buff;
+  
+  text=regreplace(item->QueryProp(P_SHORT)
+                ||item->QueryProp(P_NAME)
+                ||"<?>",
+                "^(Ein Paar|Ein|Eine|Der|Die|Das) ","",0);
+                
+// try to shorten the name with the closure
+  if (sizeof(text) > 20)
+    return implode(map(explode(text, " "), abbreviate), " ");
+  else      
+    return text;
+}
+
+// converts a mapping with characters and color info into a
+// text. data in the mapping is stored in a one-dimensional 
+// order with the position as key (ypos is pos/80, xpos pos%80)
+// this setup has a huge advantage: combining several
+// graphics just takes a "+" operator, the rest is handled
+// by the game driver. freakin' fast, much better than doing an
+// iteration over one or more array in lpc.
+static string Mapping2ColoredText(mapping pic, object player)
+{
+  string text;
+  mapping configmap;
+  int i,j,color;
+  
+  configmap=default_config+(player->QueryProp(P_INVMASTER_CONFIG)||([]));
+
+  text="";
+  color=0;
+  for (i=0;i<22;i++)
+  {
+    text+=bgcolors[configmap[8]];
+    for (j=0;j<78;j++)
+    { 
+      if (pic[i*80+j,1]!=color)
+      {
+        color=pic[i*80+j,1];
+        text+=colors[configmap[color]];
+      }
+      text+=pic[i*80+j];
+    }
+    text+=ANSI_NORMAL+"\n";
+    color=0;
+  }
+  return text;
+}
+
+static string Mapping2PlainText(mapping pic)
+{
+  string text;
+  int i,j;
+  
+  text="";
+
+  for (i=0;i<22;i++)
+  {
+    for (j=0;j<78;j++)
+      text+=pic[i*80+j];
+    text+="\n";
+  }
+  return text;
+}
+static void AddDescription(mapping pic, string type, object item)
+{
+  int indentx, indenty, i;
+  string text;
+
+  switch(type)
+  {
+   case AT_HELMET:
+      indentx=47;
+      indenty=0;
+      text=sprintf("%-30s",ComposeDesc(item)[0..30]);break;
+    case AT_QUIVER:
+      indentx=49;
+      indenty=2;
+      text=sprintf("%-28s",ComposeDesc(item)[0..28]);break;     
+    case AT_AMULET:
+      indentx=49;
+      indenty=4;     
+      text=sprintf("%-27s",ComposeDesc(item)[0..28]);break;     
+    case AT_ARMOUR:
+      indentx=53;
+      indenty=7;
+      text=sprintf("%-24s",ComposeDesc(item)[0..25]);break;          
+    case AT_SHIELD:
+      indentx=54;
+      indenty=10;   
+      text=sprintf("%-20s",ComposeDesc(item)[0..24]);break;
+    case AT_CLOAK:
+      indentx=53;
+      indenty=15;
+      text=sprintf("%-20s",ComposeDesc(item)[0..25]);break;
+    case AT_TROUSERS:
+      indentx=49;
+      indenty=17;
+      text=sprintf("%-20s",ComposeDesc(item)[0..20]);break;     
+    case AT_RING:
+      indentx=0;
+      indenty=9;
+      text=sprintf("%14s",ComposeDesc(item)[0..17]);break;
+    case AT_GLOVE:
+      indentx=0;
+      indenty=11;
+      text=sprintf("%14s",ComposeDesc(item)[0..17]);break;
+    case AT_BELT:
+      indentx=1;
+      indenty=13;
+      text=sprintf("%14s",ComposeDesc(item)[0..18]);break;     
+    case AT_BOOT:
+      indentx=1;
+      indenty=20;
+      text=sprintf("%18s",ComposeDesc(item)[0..18]);break;
+    case "Waffe":
+      indentx=1;
+      indenty=1;
+      text=sprintf("%18s",ComposeDesc(item)[0..25]);
+      if (item->QueryProp(P_NR_HANDS) > 1 &&
+          this_player() &&
+          !(this_player()->QueryArmorByType(AT_SHIELD)))
+        AddDescription(pic, AT_SHIELD, item);break;
+    default: return;
+  }
+  for (i=0;i<sizeof(text);i++)
+    pic+=([(80*indenty+indentx+i):text[i..i];2]);
+}
+
+varargs static void ConfigureColors(string text)
+{ 
+  mapping config, display;
+  string *strs;
+  
+  if (!objectp(this_player())) return;
+  
+  if (this_player()->InFight())
+  {
+    write(break_string(
+      "Im Kampf? Na Du hast Nerven, das lassen wir doch mal lieber! "
+      "Probier es danach nochmal...", 78));
+    return;
+  }
+  
+  if (stringp(text)) text=lower_case(text);
+  
+  if (text=="ok")
+  {
+    write("Farbkonfiguration beendet.\n");
+    return;
+  }
+
+  //"ansi_config", def in invmaster.h
+  config=this_player()->QueryProp(P_INVMASTER_CONFIG)||([]);
+  display=default_config+config;
+  
+  if (!text || text=="")
+  {
+    write( 
+    "*** Farbkonfiguration fuer den Ausruestungsbefehl ***\n\n"
+    " Farbe:                     wird dargestellt mit:\n"
+    "------------------          --------------------\n"
+    " Hintergrund                 "+COLORNAMES[display[8]]+"\n"
+    " Schwarz                     "+COLORNAMES[display[0]]+"\n"
+    " Rot                         "+COLORNAMES[display[1]]+"\n"
+    " Gruen                       "+COLORNAMES[display[2]]+"\n"
+    " Gelb                        "+COLORNAMES[display[3]]+"\n"
+    " Blau                        "+COLORNAMES[display[4]]+"\n"
+    " Magenta                     "+COLORNAMES[display[5]]+"\n"
+    " Tuerkis                     "+COLORNAMES[display[6]]+"\n"
+    " Weiss                       "+COLORNAMES[display[7]]+"\n\n"
+    "Farbe aendern mit '<farbe> <gewuenschte farbe>'.\n"
+    "Beispiel: 'gelb rot'.\n"
+    "Alles, was standardmaessig gelb waere, wuerde dann mit der ANSI-Farbe \n"
+    "Rot dargestellt.\n"
+    "Der Hintergrund kann zusaetzlich die Farbe 'keine' haben, bei der der \n"
+    "Hintergrund eben ueberhaupt nicht gefaerbt wird.\n"
+    "Beispiel: 'hintergrund keine'. Schaltet die Hintergrundfarbe aus.\n\n"
+    "Beenden mit 'ok'. \n"
+    "Wiederholung der Farbliste mit <Return>.\n"
+    "Farbliste auf Standard zuruecksetzen mit 'reset'.\n");
+  }
+  else
+  if (text=="reset")
+  {
+    this_player()->Set(P_INVMASTER_CONFIG, SAVE, F_MODE_AD);    
+    this_player()->SetProp(P_INVMASTER_CONFIG, 0);
+    write("Farben zurueckgesetzt!\n");    
+  }
+  else
+  {
+    if ( sizeof(strs=explode(text, " ")-({""})) !=2 
+      || !member((COLORCODES-(["keine"])), strs[0])
+      || !member((COLORCODES-(["hintergrund"])), strs[1])
+      || ((strs[0]!="hintergrund") && (strs[1]=="keine")) ) 
+    {
+      write("Falsche Eingabe.\n"
+            "Format: <farbe|hintergrund> <zugewiesene Farbe>\n"
+            "Abbrechen mit 'ok'.\n");
+    }
+    else
+    {
+      if (COLORCODES[strs[1]]==default_config[COLORCODES[strs[0]]])
+        config-=([COLORCODES[strs[0]]]);
+      else
+        config+=([COLORCODES[strs[0]]:COLORCODES[strs[1]]]);
+      if (!sizeof(config))
+      {
+        this_player()->Set(P_INVMASTER_CONFIG, SAVE, F_MODE_AD);    
+        this_player()->SetProp(P_INVMASTER_CONFIG, 0);
+      }
+      else
+      {
+        this_player()->SetProp(P_INVMASTER_CONFIG, deep_copy(config));
+        this_player()->Set(P_INVMASTER_CONFIG, SAVE, F_MODE_AS);        
+      }
+      write("Ok, Farbe gewaehlt!\n");
+    }
+  }
+  input_to("ConfigureColors", INPUT_PROMPT, "\nEingabe: ");
+}
+
+
+string* armour_order=({ 
+    AT_HELMET, AT_AMULET, AT_QUIVER, AT_ARMOUR, AT_CLOAK,
+  AT_GLOVE, AT_RING, AT_BELT,
+  AT_TROUSERS, AT_BOOT, AT_SHIELD, AT_MISC}); 
+
+mapping weapon_names=([
+		       WT_SPEAR : "Speer",
+		       WT_SWORD : "Schwert",
+		       WT_STAFF : "Kampfstab",
+		       WT_WHIP  : "Peitsche",
+		       WT_CLUB  : "Keule",
+		       WT_KNIFE : "Messer",
+		       WT_MISC  : "Irgendwas",
+		       WT_MAGIC : "Artefakt",
+		       WT_AXE   : "Axt",
+	       WT_RANGED_WEAPON : "Fernwaffe"
+		       ]);
+
+string SimpleInv(object player) {
+  object* armours=player->QueryProp(P_ARMOURS);
+  int count=sizeof(armour_order);
+  string* list=allocate(count);
+  string result="Ausruestung\n";
+  int i;
+
+  foreach(object ob: armours) {
+    if (!objectp(ob)) continue;
+      int idx = member(armour_order, ob->QueryProp(P_ARMOUR_TYPE));	      
+      if (idx>=0)
+        list[idx]=ob->QueryProp(P_SHORT);
+  }
+
+  // AT_MISC (letztes Element in list und armour_order) weglassen.
+  for (i=0;i<count-1;i++) {
+    result+=sprintf("%-20s %-57s\n",armour_order[i],list[i] || "");
+  }
+
+  object ob=ob=player->QueryProp(P_WEAPON);
+  if (objectp(ob)) {
+    result+=sprintf("%-20s %-57s\n",
+		    (ob->QueryProp(P_NR_HANDS)==1 ? "Einhand-":"Zweihand-")
+		    +weapon_names[ob->QueryProp(P_WEAPON_TYPE)],
+		    ob->QueryProp(P_SHORT));
+  } else result+="Keine Waffe\n";
+
+  return result;
+}
+// the main function called by the player object.
+// determines gender, then adds armor/weapon graphics
+// dynamically. still very fast due to the use of the "+" operator,
+// see above.
+void ShowInv(object player, string arg)
+{
+  string gender, type;
+  mapping pic;
+  int i;
+  object item;
+
+  if (!objectp(player)||!interactive(player)) return;
+
+  // split args.
+  string *args;
+  if (stringp(arg))
+    args = explode(lower_case(arg), " ") - ({" "});
+  else
+    args = ({});
+
+  if (member(args, "farben") > -1) {
+      ConfigureColors();
+      return;
+  }
+
+  if (member(args, "-k") > -1 || player->QueryProp(P_NO_ASCII_ART)) {
+    tell_object(player, SimpleInv(player));
+    return;
+  }
+
+  gender=player->QueryProp(P_GENDER)==FEMALE?"_female":"_male";
+  pic=deep_copy(data["base"+gender]);
+  pic+=data["Beschriftung"];
+  for (i=sizeof(armour_types)-1;i>=0;i--)
+    if (objectp(item=player->QueryArmourByType(armour_types[i])))
+    {
+      pic+=data[armour_types[i]+gender];
+      AddDescription(pic, armour_types[i], item);
+    }
+  if (item=player->QueryProp(P_WEAPON))
+  {
+    pic+=data[(VALID_WEAPON_TYPE(type=item->QueryProp(P_WEAPON_TYPE)))?
+      type:WT_MISC];
+    AddDescription(pic, "Waffe", item);
+  }      
+  if (player->QueryProp(P_TTY)!="ansi")
+    player->More(Mapping2PlainText(pic));
+  else
+    player->More(Mapping2ColoredText(pic, player));
+  DB(geteuid(player)+" eval cost: "+(1000000-get_eval_cost())+" ticks.\n");
+}
+
diff --git a/std/player/invmaster/invmaster.h b/std/player/invmaster/invmaster.h
new file mode 100644
index 0000000..8821b63
--- /dev/null
+++ b/std/player/invmaster/invmaster.h
@@ -0,0 +1,21 @@
+#define INVPATH "/std/player/invmaster/"
+#define DB(x)
+/*
+#define DB(x) if (find_player("rikus")) \
+                   tell_object(find_player("rikus"), \
+                     break_string(x, 78, "INVMASTER: ", 1));
+*/
+#define P_INVMASTER_CONFIG "invmaster_config"
+#define COLORNAMES ({"Schwarz","Rot","Gruen","Gelb",\
+                     "Blau","Magenta","Tuerkis","Weiss","Keine"})
+#define COLORCODES (["schwarz":0,\
+                     "rot":1,\
+                     "gruen":2,\
+                     "gelb":3,\
+                     "blau":4,\
+                     "magenta":5,\
+                     "tuerkis":6,\
+                     "weiss":7,\
+                     "keine":8,\
+                     "hintergrund":8])
+#define default_config ([0:0, 1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:0])
diff --git a/std/player/life.c b/std/player/life.c
new file mode 100644
index 0000000..73f1ff7
--- /dev/null
+++ b/std/player/life.c
@@ -0,0 +1,841 @@
+// MorgenGrauen MUDlib
+//
+// player/life.c -- player life handling
+//
+// $Id: life.c 9397 2015-12-11 20:29:26Z Zesstra $
+
+// Defines some things which are different than in living.c
+// One example is the heart_beat().
+
+// Properties
+//  P_AGE           -- Age
+
+#pragma strong_types, save_types, rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/life";
+
+#include <rtlimits.h>
+#include <debug_info.h>
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <player/base.h>
+#include <player/life.h>
+#include <moving.h>
+#include <player/comm.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+
+#include <config.h>
+#include <wizlevels.h>
+#include <defines.h>
+#include <language.h>
+#include <properties.h>
+#include <hook.h>
+
+#include <living/life.h>
+#include <player/pklog.h>
+#include <player/combat.h>
+#include <health.h>
+#include <living/combat.h>
+#include <attributes.h>
+#include <defuel.h>
+#include <new_skills.h>
+
+// Die Folgen eines Todes wirken 4 Stunden lang nach
+#define SUFFER_TIME 7200
+#define P_DEATH_INFO "death_info"
+
+int age;                  /* Number of heart beats of this character. */
+
+private int suffer_time;
+static int time_to_save;  /* when to next save player */
+private nosave int die_in_progress; // semaphore fuer die()-Aufrufe.
+
+static int _set_playerkills( int val );
+
+protected void create()
+{
+    ::create();
+    Set(P_AGE, -1, F_SET_METHOD);
+    Set(P_AGE, PROTECTED, F_MODE);
+    Set( P_KILLS, SAVE|SECURED, F_MODE_AS );
+    Set( P_GHOST, SAVE, F_MODE_AS );
+    Set( P_TIMING_MAP, SAVE|SECURED, F_MODE_AS );
+    Set( P_LAST_DEATH_TIME, SAVE|SECURED, F_MODE_AS );
+    Set( P_DEATH_INFO, SAVE|SECURED, F_MODE_AS );
+    Set( P_DEFUEL_LIMIT_FOOD,PROTECTED,F_MODE_AS);
+    Set( P_DEFUEL_LIMIT_DRINK,PROTECTED,F_MODE_AS);
+    Set( P_DEFUEL_TIME_FOOD,PROTECTED,F_MODE_AS);
+    Set( P_DEFUEL_TIME_DRINK,PROTECTED,F_MODE_AS);
+    Set( P_DEFUEL_AMOUNT_FOOD,PROTECTED,F_MODE_AS);
+    Set( P_DEFUEL_AMOUNT_DRINK,PROTECTED,F_MODE_AS);
+    offerHook(H_HOOK_HP,1);
+    offerHook(H_HOOK_SP,1);
+    // P_TIMING_MAP aufraeumen, aber zeitverzoegert, weil jetzt die Daten noch
+    // nicht aus dem Savefile geladen wurden.
+    call_out(#'expire_timing_map, 4);
+}
+
+// called from base.c in Reconnect()
+protected void reconnect() {
+  expire_timing_map();
+}
+
+static int _set_playerkills( int val )
+{
+    string tmp;
+    int playerkills;
+
+    // ist der Setzer in einer Arena/Schattenwelt. Dann nicht. (Ja, Bug ist,
+    // dass EMs aus Schattenwelt/Arena heraus auch das PK-Flag nicht
+    // zuruecksetzen koennen.)
+    if ( previous_object(1) && environment(previous_object(1)) &&
+         (tmp = object_name(environment(previous_object(1)))) &&
+         CheckArenaFight(previous_object(1)) )
+        return 0;
+
+    tmp = sprintf( "%O: %s %O %s",
+                   previous_object(1) || this_interactive() || this_player(),
+                   getuid(ME), val, dtime(time()) );
+
+    playerkills = Query(P_KILLS);
+
+    if( intp(val) && val >= 0 )
+        if( previous_object(1) && IS_ARCH(getuid(previous_object(1))) &&
+            this_interactive() && IS_ARCH(this_interactive()) )
+            playerkills = val;
+        else
+            tmp += " ILLEGAL!";
+    else
+        playerkills++;
+
+    if ( !previous_object(1) || !query_once_interactive(previous_object(1)) ||
+         IS_LEARNER(previous_object(1)) )
+        log_file( "SET_KILLS", tmp + "\n" );
+
+    return Set( P_KILLS, playerkills );
+}
+
+
+public int death_suffering()
+{
+    if ( suffer_time <= 0 )
+    return suffer_time = 0;
+
+    return 1 + (10 * suffer_time) / SUFFER_TIME;
+}
+
+
+protected void heart_beat()
+{
+    mapping di, mods;
+
+    ++age; // erstmal altern ;-)
+
+    ::heart_beat();
+
+    if ( age > time_to_save ){
+        save_me(1);
+        time_to_save = age + 500;
+    }
+
+    // als geist hat man mit den meisten weltlichen Dingen nicht allzuviel zu
+    // tun.
+    if ( QueryProp(P_GHOST) )
+        return;
+
+    if ( suffer_time > 0 )
+        suffer_time--;
+
+    // Todesfolgen nur alle 20 Sekunden (10 HB) checken.
+    // Das ist immer noch oft genug und spart Rechenzeit.
+    if ( (age % 10) || !mappingp(di = QueryProp(P_DEATH_INFO)) )
+        return;
+
+    mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"];
+    if (!mappingp(mods)) return;
+
+    if ( mods[A_STR] && --di[A_STR] <= 0) {
+        // einen Attributspunkt regenerieren
+        if ( mods[A_STR] < -1 ) {
+            mods[A_STR]++;
+            di[A_STR] = (110 + 5 * (di[A_STR, 1] + mods[A_STR])) / 10;
+        }
+        else {
+            m_delete( mods, A_STR );
+            m_delete( di, A_STR );
+        }
+    }
+
+    if ( mods[A_CON] && --di[A_CON] <= 0) {
+        // einen Attributspunkt regenerieren
+        if ( mods[A_CON] < -1 ){
+            mods[A_CON]++;
+            di[A_CON] = (110 + 5 * (di[A_CON, 1] + mods[A_CON])) / 10;
+        }
+        else {
+            m_delete( mods, A_CON );
+            m_delete( di, A_CON );
+        }
+    }
+
+    if ( mods[A_DEX] && --di[A_DEX] <= 0) {
+        // einen Attributspunkt regenerieren
+        if ( mods[A_DEX] < -1 ){
+            mods[A_DEX]++;
+            di[A_DEX] = (110 + 5 * (di[A_DEX, 1] + mods[A_DEX])) / 10;
+        }
+        else {
+            m_delete( mods, A_DEX );
+            m_delete( di, A_DEX );
+        }
+    }
+
+    if ( mods[A_INT] && --di[A_INT] <= 0) {
+        // einen Attributspunkt regenerieren
+        if ( mods[A_INT] < -1 ){
+            mods[A_INT]++;
+            di[A_INT] = (110 + 5 * (di[A_INT, 1] + mods[A_INT])) / 10;
+        }
+        else {
+            m_delete( mods, A_INT );
+            m_delete( di, A_INT );
+        }
+    }
+
+    if ( sizeof(di) && sizeof(mods))
+        SetProp( P_DEATH_INFO, di );
+    else
+        SetProp( P_DEATH_INFO, 0 );
+
+    SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
+}
+
+
+public void force_save() {
+    time_to_save = 0;
+}
+
+
+nomask public int do_damage( int dam, object enemy )
+{
+    int hit_point;
+
+    if( QueryProp(P_GHOST) || dam <= 0 )
+        return 0;
+
+    hit_point = QueryProp(P_HP);
+
+    if ( query_once_interactive(ME) && dam >= hit_point && IS_LEARNING(ME) ){
+        tell_object( ME, "Deine magischen Kraefte verhindern Deinen Tod.\n" );
+        return 0;
+    }
+
+    if ( !objectp(enemy) )
+        enemy = previous_object() || this_interactive() || this_player();
+
+    hit_point -= dam;
+
+    if( hit_point < 0 ){
+        if ( !interactive(ME) )
+            // Netztote sterben nicht
+            hit_point = 10;
+        else {
+            if ( objectp(enemy) && interactive( enemy ) && enemy != ME &&
+                 !QueryProp(P_TESTPLAYER) && !IS_WIZARD(ME) &&
+                 !CheckArenaFight(ME) ) {
+                if ( QueryPlAttacked(enemy) )
+                    hit_point = 1;
+                else {
+                    hit_point = 0;
+                    enemy->SetProp( P_KILLS, -1 );
+                }
+
+                log_file( "KILLER",
+                          sprintf( "%s %s(%d/%d) toetete %s(%d/%d)%s\n",
+                                   ctime(time()),
+                                   getuid(enemy), query_wiz_level(enemy),
+                                   (int) enemy->QueryProp(P_LEVEL), getuid(ME),
+                                   query_wiz_level(ME), QueryProp(P_LEVEL),
+                                   (hit_point ? " NOTWEHR=>KEIN PK" : "") ) );
+            }
+            else { 
+                string killername;
+                if (objectp(enemy))
+                    killername=sprintf("%s (%s)",
+                        BLUE_NAME(enemy), REAL_UID(enemy));
+                else
+                    killername="??? (???)"; 
+
+                if ( !QueryProp(P_TESTPLAYER) )
+                    create_kill_log_entry(killername, enemy );
+            }
+
+            if ( enemy )
+                enemy->StopHuntFor( ME, 1 );
+
+            map_objects( QueryEnemies()[0], "StopHuntFor", ME, 1 );
+            StopHuntingMode(1);
+
+            Set( P_KILLER, enemy );
+            die();
+        }
+    }
+
+    SetProp( P_HP, hit_point );
+    return dam;
+}
+
+
+// Loescht im sterbenden Spieler die 'koerperabhaengigen' Properties
+private void reset_my_properties()
+{
+  // Loeschen der Properties
+  if ( QueryProp(P_POISON) )
+  {
+     // Don't die twice 'cause of the same poison
+     Set( P_POISON, 0, F_SET_METHOD );
+     Set( P_POISON, 0, F_QUERY_METHOD );
+     SetProp( P_POISON, 0 );
+  }
+
+  Set( P_FROG, 0, F_QUERY_METHOD ); 
+  Set( P_FROG, 0, F_SET_METHOD );
+  SetProp( P_FROG, 0 ); // Damit die Attribute auch stimmen.
+  Set( P_ALCOHOL, 0, F_QUERY_METHOD );
+  Set( P_ALCOHOL, 0, F_SET_METHOD );
+  SetProp(P_ALCOHOL, 0 );
+  Set( P_DRINK, 0, F_QUERY_METHOD );
+  Set( P_DRINK, 0, F_SET_METHOD );
+  SetProp(P_DRINK, 0 );
+  Set( P_FOOD, 0, F_QUERY_METHOD );
+  Set( P_FOOD, 0, F_SET_METHOD );
+  SetProp(P_FOOD, 0 );
+  Set( P_BLIND, 0, F_QUERY_METHOD );
+  Set( P_BLIND, 0, F_SET_METHOD );
+  SetProp(P_BLIND, 0 );
+  Set( P_DEAF, 0, F_QUERY_METHOD );
+  Set( P_DEAF, 0, F_SET_METHOD );
+  SetProp(P_DEAF, 0 );
+  Set( P_MAX_HANDS, 0, F_QUERY_METHOD );
+  Set( P_MAX_HANDS, 0, F_SET_METHOD );
+  SetProp( P_MAX_HANDS, 2 );
+  Set( P_HANDS_USED_BY, 0, F_QUERY_METHOD );
+  Set( P_HANDS_USED_BY, 0, F_SET_METHOD );
+  SetProp( P_HANDS_USED_BY, ({}) );
+  Set( P_PARA, 0 );
+  Set( P_NO_REGENERATION, 0, F_QUERY_METHOD );
+  Set( P_NO_REGENERATION, 0, F_SET_METHOD );
+  SetProp(P_NO_REGENERATION, 0 );
+  Set( P_TMP_MOVE_HOOK, 0, F_QUERY_METHOD );
+  Set( P_TMP_MOVE_HOOK, 0, F_SET_METHOD );
+  SetProp(P_TMP_MOVE_HOOK, 0 );
+  Set( P_LAST_DEATH_TIME , time() );
+  // damit der Teddy o.ae. mitbekommt, dass man jetzt tot ist ]:->
+  SetProp( P_HP, 0 );
+  SetProp( P_SP, 0 );
+}
+
+varargs protected int second_life( object corpse )
+{
+    int lost_exp, level;
+    // Es gibt Funktionen, die sollte man nicht per Hand aufrufen duerfen ;-)
+    if ( extern_call() && previous_object() != ME )
+        return 0;
+
+    if ( query_once_interactive(ME) && IS_LEARNING(ME) ){
+        tell_object( ME, "Sei froh, dass Du unsterblich bist, sonst waere "
+                     "es eben zu Ende gewesen.\n" );
+        return 1;
+    }
+
+    if ( !IS_SEER(ME) || (level = QueryProp(P_LEVEL)) < 20 )
+        lost_exp = QueryProp(P_XP) / 3;
+    else
+        lost_exp = QueryProp(P_XP) / (level - 17);
+
+    AddExp(-lost_exp);
+
+
+    // Todesfolgen setzen....
+    //SetProp( P_DEATH_INFO, 1);
+    if ( !IS_LEARNING(ME) && !QueryProp(P_TESTPLAYER) ) {
+
+        mapping attr = QueryProp(P_ATTRIBUTES);
+        mapping mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"] || ([]);
+
+        // Attribute auf 75% vom aktuellen Wert senken
+        mods[A_STR] = -attr[A_STR] + (3 * (attr[A_STR] + mods[A_STR]) / 4);
+        mods[A_CON] = -attr[A_CON] + (3 * (attr[A_CON] + mods[A_CON]) / 4);
+        mods[A_DEX] = -attr[A_DEX] + (3 * (attr[A_DEX] + mods[A_DEX]) / 4);
+        mods[A_INT] = -attr[A_INT] + (3 * (attr[A_INT] + mods[A_INT]) / 4);
+
+        SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
+
+        int offs = 220;  // 220 heart_beats == 7min20
+        // Die 220 HB sind fix, dazu kommen noch 5 HB pro realem 
+        // Attributspunkt. Geteilt wird das ganze noch durch 10, weil im HB
+        // nur alle 10 HBs die TF gecheckt werden. Da wird dann alle 10 HB ein
+        // Punkt abgezogen und wenn 0 erreicht ist, wird das Attribut um eins
+        // regeneriert.
+        SetProp( P_DEATH_INFO, ([ 
+            A_STR: (offs + 5 * (attr[A_STR] + mods[A_STR]))/10; attr[A_STR],
+            A_CON: (offs + 5 * (attr[A_CON] + mods[A_CON]))/10; attr[A_CON],
+            A_DEX: (offs + 5 * (attr[A_DEX] + mods[A_DEX]))/10; attr[A_DEX],
+            A_INT: (offs + 5 * (attr[A_INT] + mods[A_INT]))/10; attr[A_INT]
+                  ]) );
+
+        // die suffer_time wird via death_suffering() von
+        // QuerySkillAttribute() abgefragt und geht dann als Malus in
+        // SA_QUALITY mit ein.
+        if ( suffer_time <= 2*SUFFER_TIME )
+            suffer_time += SUFFER_TIME - 1;
+        else
+            suffer_time = 3 * SUFFER_TIME - 1;
+    }
+
+    // Die verschiedenen NotifyPlayerDeath-Funktionen koennen u.U. schlecht
+    // programmiert sein und zuviel Rechenzeit ziehen. Deshalb werden sie mit
+    // einem Limits von 150k bzw. 80k aufgerufen. Ausserdem werden sie nur
+    // gerufen, solange noch min. 25k Ticks da sind.
+    int *limits=query_limits();
+    limits[LIMIT_EVAL] = 150000;
+    limits[LIMIT_COST] = LIMIT_DEFAULT;
+
+    mixed killer = QueryProp(P_KILLER);
+    mixed gi = QueryProp(P_GUILD);
+    if (stringp(gi))
+        gi = find_object("/gilden/"+gi);
+    // jedes Objekt nur einmal, aber nicht via m_indices(mkmapping)) wegen
+    // Verlust der Reihenfolge.
+    object *items = ({killer});
+    if (environment() != killer)
+        items += ({environment()});
+    if (gi != killer && gi != environment())
+        items += ({gi});
+    foreach(object item: items) {
+        if (get_eval_cost() < limits[LIMIT_EVAL] + 20000)
+            break;
+        // falls ein NPD() implizit andere Objekt zerstoert hat.
+        if (objectp(item)) {
+            catch(limited(#'call_other, limits, item, "NotifyPlayerDeath",
+                  ME, killer, lost_exp);publish);
+        }
+    }
+    // jetzt den Rest.
+    limits[LIMIT_EVAL] = 80000;
+    foreach(object item: (environment() ? all_inventory(environment()) : ({}))
+                        + deep_inventory(ME)
+                        + (objectp(corpse) ? deep_inventory(corpse) : ({}))
+                        - items ) {
+        // wenn nicht mehr genug Ticks, haben die restlichen Pech gehabt.
+        if (get_eval_cost() < limits[LIMIT_EVAL] + 20000)
+            break;
+        // NPD() koennen andere Objekt zerstoeren.
+        if (objectp(item)) {
+            catch(limited(#'call_other, limits, item, "NotifyPlayerDeath",
+                                    ME, killer, lost_exp);publish);
+        }
+    }
+
+    // Properties zuruecksetzen, sollte nach dem NotifyPlayerDeath()
+    // passieren, weil Objekte sich darin evtl. erstmal noch aufraeumen und
+    // props manipulieren.
+    reset_my_properties();
+    UpdateAttributes(); // Beim Tod werden Dinge entfernt, Attribute pruefen
+    
+    // Auch Bewegung erst nach den NPD(), da diese den Spieler bewegen
+    // koennten, was eine Bewegung aus dem Todesraum waere, wenn die Bewegung
+    // vor dem NPD() stattfaende.
+    SetProp( P_GHOST, 1 ); // nach reset_my_properties() !
+    clone_object( "room/death/death_mark" )->move( ME, M_NOCHECK ); 
+
+    return 1;
+}
+
+
+public int AddHpHook( object ob )
+{
+    object *hooks;
+
+    if ( !objectp(ob) )
+        return 0;
+
+    if ( !pointerp(hooks = Query(P_HP_HOOKS)) ){
+        Set( P_HP_HOOKS, ({ ob }) );
+        return 1;
+    }
+
+    if ( member( hooks, ob ) >= 0 )
+        return 0;
+
+    Set( P_HP_HOOKS, (hooks - ({0})) + ({ ob }) );
+    return 1;
+}
+
+
+public int RemoveHpHook( object ob )
+{
+    object *hooks;
+
+    if ( !pointerp(hooks = Query(P_HP_HOOKS)) )
+        return 0;
+
+    Set( P_HP_HOOKS, hooks - ({ ob, 0 }) );
+    return 1;
+}
+
+
+static int _query_age() {
+    return Set(P_AGE, age, F_VALUE);
+}
+
+static int _set_hp( int hp )
+{
+    object *hooks;
+    int ret, i, old;
+
+    if ( (old = Query(P_HP, F_VALUE)) == hp )
+        return old;
+
+    ret = life::_set_hp(hp);
+
+    if ( ret == old )
+        return ret;
+
+    // Call old hooks in all objects... destructed objects will be ignored.
+    if (pointerp(hooks = Query(P_HP_HOOKS)))
+      call_other(hooks, "NotifyHpChange");
+
+    // Call new hooks.
+    HookFlow(H_HOOK_HP,ret);
+
+    // Report-ausgabe
+    status_report(DO_REPORT_HP, ret);
+
+    return ret;
+}
+
+
+static int _set_sp( int sp )
+{
+    object *hooks;
+    int ret, i, old;
+
+    if ( (old = Query(P_SP,F_VALUE)) == sp )
+        return old;
+
+    ret = life::_set_sp(sp);
+
+    if ( ret == old )
+        return ret;
+
+    // Call old hooks in all objects... destructed objects will be ignored.
+    if (pointerp(hooks = Query(P_HP_HOOKS)))
+      call_other(hooks, "NotifyHpChange");
+
+    // Call new hooks.
+    HookFlow(H_HOOK_SP,ret);
+
+    // Report-ausgabe
+    status_report(DO_REPORT_SP, ret);
+
+    return ret;
+}
+
+static int _set_poison(int n)
+{
+  int old = Query(P_POISON, F_VALUE);
+  if (old == n )
+      return old;
+  n = life::_set_poison(n);
+  if ( n == old )
+      return n;
+  // ggf. Statusreport ausgeben
+  if (interactive(ME))
+    status_report(DO_REPORT_POISON, n);
+  return n;
+}
+
+static int _set_max_poison(int n)
+{
+  if (n >= 0)
+  {
+    Set(P_MAX_POISON, n, F_VALUE);
+    int maxp=QueryProp(P_MAX_POISON); // koennte ne Querymethode drauf sein...
+    if (QueryProp(P_POISON) > maxp)
+      SetProp(P_POISON, maxp);
+  }
+  GMCP_Char( ([P_MAX_POISON: n]) );
+  return n;
+}
+
+static int _set_max_hp( int hp )
+{
+  if (hp >= 0)
+  {
+    Set(P_MAX_HP, hp, F_VALUE);
+    int maxhp=QueryProp(P_MAX_HP); // koennte ne Querymethode drauf sein...
+    if (QueryProp(P_HP) > maxhp)
+      SetProp(P_HP, maxhp);
+  }
+  GMCP_Char( ([P_MAX_HP: hp]) );
+  return hp;
+}
+
+static int _set_max_sp( int sp )
+{
+  if (sp >= 0)
+  {
+    Set(P_MAX_SP, sp, F_VALUE);
+    int maxsp=QueryProp(P_MAX_SP); // koennte ne Querymethode drauf sein...
+    if (QueryProp(P_SP) > maxsp)
+      SetProp(P_SP, maxsp);
+  }
+  GMCP_Char( ([P_MAX_SP: sp]) );
+  return sp;
+}
+
+static int _set_ghost( int g ) {
+    object team;
+
+    if(!g && query_hc_play()>1)
+    {
+      write("HAHAHA, DU BIST AUF EWIG MEIN.\n");
+      return Query(P_GHOST);
+    }
+
+    g = Set( P_GHOST, g );
+
+    if ( g && objectp(team = Query(P_TEAM)) )
+        team->RemoveMember(ME);
+
+    return g;
+}
+
+
+public int undie()
+{
+    mixed x, di;
+    mapping attr, mods;
+
+    if ( !this_interactive() || !previous_object() )
+        return 0;
+
+    if ( !IS_ARCH(this_interactive()) || !IS_ARCH(getuid(previous_object())) ||
+         process_call() )
+        log_file( "UNDIE", sprintf( "%s %O -> %O\n", dtime(time())[5..16],
+                                    this_interactive(), ME ) );
+
+    if ( x = Query(P_DEADS) )
+        x--;
+
+    Set( P_DEADS, x );
+
+    x = QueryProp(P_XP);
+
+    if ( (di = QueryProp(P_LEVEL)) < 20 || !IS_SEER(ME) )
+        x = (int)(x * 1.5);
+    else
+        // Umweg ueber float, weil bei hohen XP+Level sonst 32Bit nicht
+        // mehr ausreichen -> negative XP
+        x = (int) ( x * ((float) (di - 17) / (di - 18)) );
+
+    Set( P_XP, x );
+
+    attr = QueryProp(P_ATTRIBUTES) || ([]);
+    mods = QueryProp(P_ATTRIBUTES_MODIFIER)["#death"] || ([]);
+
+    if ( mappingp(di = QueryProp(P_DEATH_INFO)) ){
+        // Beim naechsten heart_beat checken
+        // Zesstra: Wieso eigentlich? Die Modifier werden doch direkt hier
+        // geloescht. So expired man auch einen Teil von nicht-undie-ten Toden
+        // vorzeitig...? Mal auskommentiert. 29.10.2007
+        //di[A_STR] = 1;
+        //di[A_DEX] = 1;
+        //di[A_INT] = 1;
+        //di[A_CON] = 1;
+    }
+    else
+      di = ([]);
+    
+    mods[A_STR] = ((4 * (attr[A_STR] + mods[A_STR])) / 3) - attr[A_STR];
+    mods[A_DEX] = ((4 * (attr[A_DEX] + mods[A_DEX])) / 3) - attr[A_DEX];
+    mods[A_INT] = ((4 * (attr[A_INT] + mods[A_INT])) / 3) - attr[A_INT];
+    mods[A_CON] = ((4 * (attr[A_CON] + mods[A_CON])) / 3) - attr[A_CON];
+
+    if ( mods[A_STR] >= 0 ) {
+        m_delete( mods, A_STR );
+        m_delete( di, A_STR);
+    }
+    if ( mods[A_DEX] >= 0 ) {
+        m_delete( mods, A_DEX );
+        m_delete( di, A_DEX);
+    }
+    if ( mods[A_INT] >= 0 ) {
+        m_delete( mods, A_INT );
+        m_delete( di, A_INT);
+    }
+    if ( mods[A_CON] >= 0 ) {
+        m_delete( mods, A_CON );
+        m_delete( di, A_CON);
+    }
+
+    SetProp( P_ATTRIBUTES_MODIFIER, ({ "#death", mods }) );
+    if (sizeof(di))
+      SetProp( P_DEATH_INFO, di );
+    else
+      SetProp( P_DEATH_INFO, 0);
+
+    suffer_time -= ((SUFFER_TIME)-1);
+
+    if ( suffer_time < 0 )
+        suffer_time = 0;
+
+    Set( P_GHOST, 0 );
+    return 1;
+}
+
+
+varargs public void die( int poisondeath, int extern)
+{
+    // laeuft schon ein die()? Fehler ausloesen, Ursache rekursiver die() soll
+    // gefunden werden. DINFO_EVAL_NUMBER wird in jedem Top-Level Call im
+    // driver erhoeht, d.h. gleiche Zahl signalisiert ein rekursives die().
+
+    if (die_in_progress == debug_info(DINFO_EVAL_NUMBER))
+    {
+      // TODO: ist das die_in_progress aus dem letzten Backend-Cycle?
+      raise_error(sprintf(
+            "die() in %O gerufen, aber die() laeuft bereits!\n",
+            this_object()));
+    }
+    die_in_progress = debug_info(DINFO_EVAL_NUMBER);
+    
+    // Fuer HC-Player ists jetzt gelaufen...
+    if(query_hc_play()==1)
+    {
+      set_hc_play(capitalize(geteuid(ME)),time());
+      SetDefaultHome("/room/nirvana");
+      SetDefaultPrayRoom("/room/nirvana");
+      SetProp(P_START_HOME,"/room/nirvana");
+      log_file("HCDEAD",dtime(time())+" "+capitalize(geteuid(ME))
+          +" geht in das Nirvana ein!\n");
+    }
+
+    // Wenn das die() direkt von aussen gerufen wurde, muss P_KILLER hier
+    // gespeichert werden.
+    if (extern_call())
+        SetProp(P_KILLER, previous_object());
+
+    // Sichern der zu loeschenden Properties. Diese Props werden im Verlauf des
+    // Todes zurueckgesetzt. Einige Magier wollen diese Daten aber spaeter
+    // noch haben und fragen teilweise P_LAST_DEATH_PROPS im
+    // NotifyPlayerDeath() ab. Daher wird der Kram jetzt hier schonmal
+    // gesichert.
+    // BTW: Props mit Mappings/Arrays sollten kopiert werden.
+    SetProp(P_LAST_DEATH_PROPS, ([
+      P_POISON          : QueryProp(P_POISON),
+      P_FROG            : QueryProp(P_FROG),
+      P_ALCOHOL         : QueryProp(P_ALCOHOL),
+      P_DRINK           : QueryProp(P_DRINK),
+      P_FOOD            : QueryProp(P_FOOD),
+      P_BLIND           : QueryProp(P_BLIND),
+      P_DEAF            : QueryProp(P_DEAF),
+      P_MAX_HANDS       : QueryProp(P_MAX_HANDS),
+      P_PARA            : QueryProp(P_PARA),
+      P_NO_REGENERATION : QueryProp(P_NO_REGENERATION),
+      P_HP              : QueryProp(P_HP),
+      P_SP              : QueryProp(P_SP),
+      P_LAST_DEATH_TIME : QueryProp(P_LAST_DEATH_TIME )
+    ]) );
+
+    // call the inherited die() with 10 Mio Ticks which will be accounted as 1
+    // Tick... ;-)
+    int *limits = query_limits();
+    limits[LIMIT_EVAL] == 10000000;
+    limits[LIMIT_COST] == LIMIT_UNLIMITED;
+    limited(#'::die, limits, poisondeath, (extern_call() ? 1 : 0)); 
+
+    // nach dem Tod sollte man auch keine LP mehr haben.
+    SetProp(P_HP, 0);
+
+    // naechster Tod kann kommen. Dekrementierung, da 0 ein gueltiger Wert
+    // fuer DINFO_EVAL_NUMBER waere. abs(), um nicht  -__INT_MAX__ zu
+    // dekrementieren.
+    die_in_progress = abs(die_in_progress) - 1;
+}
+
+int defuel_food()
+{
+    int ret;
+    object prev;
+    
+    ret=::defuel_food();
+    prev=previous_object();
+    if(!prev || !objectp(prev))
+    {
+        prev=this_object();
+    }
+    
+    if(ret<=0)
+    {
+            call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,1,0,1);
+    }
+    else
+    {
+            call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,0,ret,1);
+    }
+    return ret;
+}
+
+int defuel_drink()
+{
+    int ret;
+    object prev;
+    
+    ret=::defuel_drink();
+    prev=previous_object();
+    if(!prev || !objectp(prev))
+    {
+        prev=this_object();
+    }
+    
+    if(ret<=0)
+    {
+            call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,1,0,0);
+    }
+    else
+    {
+            call_other(FUELSTAT,"addDefuelStatEntry",prev,this_object(),0,0,ret,0);
+    }
+    return ret;
+}
+
+public void show_age()
+{ int i,j;
+
+    write("Alter:\t");
+    i = QueryProp(P_AGE);
+    if ((j=i/43200))
+    {
+      write(j + " Tag"+(j==1?" ":"e "));
+      i %= 43200;
+    }
+    if ((j=i/1800))
+    {
+      write(j + " Stunde"+(j==1?" ":"n "));
+      i %= 1800;
+    }
+    if ((j=i/30))
+    {
+      write(j + " Minute"+(j==1?" ":"n "));
+      i %= 30;
+    }
+    write(i*2 + " Sekunden.\n");
+}
+
diff --git a/std/player/moneyhandler.c b/std/player/moneyhandler.c
new file mode 100644
index 0000000..86a17db
--- /dev/null
+++ b/std/player/moneyhandler.c
@@ -0,0 +1,56 @@
+// MorgenGrauen MUDlib
+//
+// player/moneyhandler.c -- money handler for players
+// Nur noch aus Kompatibilitaetsgruenden vorhanden
+//
+// $Id: moneyhandler.c 9051 2015-01-11 20:28:00Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/container/moneyhandler";
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+#include <container/moneyhandler.h>
+#include <wizlevels.h>
+#include <properties.h>
+#include <money.h>
+#include <moving.h>
+
+public int AddMoney( int amount )
+{
+  object ob;
+
+  if ( !amount )
+    return 1;
+
+  int ret = moneyhandler::AddMoney(amount);
+
+  // ggf. noch loggen
+  if ( ret == MOVE_OK
+       && objectp(ob = find_object("/p/daemon/moneylog"))
+       && amount > 0
+       // dieses muss leider drinbleiben, weil viele nicht-Spieler dieses
+       // erben
+       && query_once_interactive(this_object())
+       && !IS_WIZARD(this_object())
+       && !Query(P_TESTPLAYER) )
+    ob->AddMoney( previous_object(), amount );
+
+  return ret;
+}
+
+public int QueryMoney()
+{
+  object money = present(SEHERKARTEID_AKTIV, this_object());
+  // zusaetzlich zu den anderen Geldquellen auch noch die Seherkarte pruefen.
+  if (money)
+    return moneyhandler::QueryMoney() + money->QueryProp(P_AMOUNT);
+
+  return moneyhandler::QueryMoney();
+}
+
diff --git a/std/player/moving.c b/std/player/moving.c
new file mode 100644
index 0000000..ab62f59
--- /dev/null
+++ b/std/player/moving.c
@@ -0,0 +1,182 @@
+// MorgenGrauen MUDlib
+//
+// player/moving.c -- player moving
+//
+// $Id: moving.c 9434 2016-01-12 12:34:05Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "std/living/moving";
+
+#define NEED_PROTOTYPES
+#include <player/base.h>
+#include <living/description.h>
+#include <player/viewcmd.h>
+#include <player.h>
+#undef NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <events.h>
+#include <pathd.h>
+
+private nosave string *connections;
+
+public void create()
+{
+    SetProp( P_MSGIN,  "kommt an" );
+    SetProp( P_MSGOUT, "geht" );
+    SetProp( P_MMSGIN, "tritt aus einer Schwefelwolke" );
+    SetProp( P_MMSGOUT, "verschwindet mit Knall und Schwefelduft" );
+    Set( P_MSGIN,   SAVE, F_MODE );
+    Set( P_MSGOUT,  SAVE, F_MODE );
+    Set( P_MMSGIN,  SAVE, F_MODE );
+    Set( P_MMSGOUT, SAVE, F_MODE );
+    connections = ({});
+
+    ::create();
+}
+
+
+static mixed _to_remove( object ob )
+{
+    return ob->QueryProp(P_AUTOLOADOBJ) || ob->QueryProp(P_NODROP);
+}
+
+
+// autoload and nodrop object may not fall into the environment
+static varargs int remove( int silent )
+{
+    object* dest_obj = filter( deep_inventory(ME) - ({0}), "_to_remove" );
+    filter_objects( dest_obj, "remove" );
+    filter( dest_obj - ({0}), #'destruct );
+
+    if ( !QueryProp(P_INVIS) && !silent )
+        catch( say( name(WER, 1) + " verlaesst diese Welt.\n", ME );publish );
+    
+    if ( ME && !silent )
+        tell_object( ME, "Bis bald!\n" );
+
+    // Im Falle des Resets ist previous_object() == ME, aber
+    // previous_object(0) == 0. Ausserdem ist der Caller-Stack leer. Also
+    // schauen, ob es ein PO gibt, was nicht gleich dem Objekt selber ist, TI
+    // pruefen und 
+    if ( this_interactive() != ME
+        && objectp(previous_object()) && previous_object() != ME
+        && object_name(previous_object())[0..7] != "/secure/"
+        && member(object_name(ME), ':') > -1 )
+      log_file( "PLAYERDEST",
+                sprintf( "%s: %O vernichtet von PO %O, TI %O, TP %O\n",
+                         dtime(time()), ME, previous_object(),
+                         this_interactive(), this_player() ) );
+
+    // Logout-event ausloesen
+    EVENTD->TriggerEvent(EVT_LIB_LOGOUT, ([
+	    E_OBJECT: ME,
+	    E_PLNAME: getuid(ME),
+	    E_ENVIRONMENT: environment() ]) );
+
+    return ::remove();
+}
+
+public string NotifyDestruct(object caller) {
+  
+  if (previous_object() != master()
+      || object_name(this_object()) == __FILE__[..<3])
+    return 0;
+
+  // Das Zerstoeren von Spielern wird ggf. geloggt.
+  if ( objectp(caller) && caller != this_object()
+       && getuid(caller) != ROOTID ) {
+      log_file( "PLAYERDEST",
+		sprintf( "%s: %O VERNICHTET von PO %O, TI %O, TP %O\n",
+			 dtime(time()), this_object(), caller,
+			 this_interactive(), this_player() ) );
+  }
+  // erstmal nix weiter tun, destruct gestatten.
+  return 0;
+}
+
+protected int PreventMove(object dest, object oldenv, int method) {
+  string hcroom;
+  mixed res;
+
+  // gestorbene HC-Spieler duerfen nicht mehr aus dem Nirvana, nicht umgehbar
+  // durch M_NOCHECK
+  if ( interactive(ME) && (query_hc_play()>1) ) {
+    if (objectp(dest))
+      hcroom=object_name(dest);
+    if (sizeof(hcroom)<7 || hcroom[0..5]!="/room/") { 
+      return ME_CANT_BE_INSERTED;
+    }
+  }
+
+  // alle anderen Pruefungen sind mit M_NOCHECK egal.
+  if ( (method & M_NOCHECK) )
+    return(::PreventMove(dest,oldenv,method));
+
+  // Spieler duerfen in Raeume mit gesetztem P_NO_PLAYERS nicht hinein
+  if ( dest->QueryProp(P_NO_PLAYERS) && interactive(ME) &&
+	!(method & M_NOCHECK) &&
+        !IS_LEARNER(ME) && (!stringp(res = QueryProp(P_TESTPLAYER))
+                             || !IS_LEARNER( lower_case(res) )) ){
+      tell_object( ME, "Da darfst Du als Spieler nicht hin.\n" );
+      return ME_NOT_ALLOWED;
+  }
+
+  return ::PreventMove(dest,oldenv,method);
+}
+
+// Fuck. Ausnahmsweise wegen VC brauch ich nen anderes BLUE_NAME
+#define OLD_BLUE_NAME(ob) (explode(object_name(ob),"#")[0])
+// Krams nach dem Move machen und nebenbei zum Ueberschreiben.
+protected void NotifyMove(object dest, object oldenv, int method) {
+
+  // erstmal ggf. Rauminhalt an Spieler ausgeben.
+  if ( interactive(ME) && !(method & M_NO_SHOW) ) {
+      if (!CannotSee(1))
+          tell_object( ME, "" + env_descr(1) );
+      else if ( QueryProp(P_BRIEF) < 2 )
+          tell_object( ME, "Finsternis.\n" );
+  }
+
+  //dann geerbten Kram machen
+  ::NotifyMove(dest,oldenv,method);
+
+  // Schlussendlich noch fuer den PathD bewegung protokollieren.
+  // (dest nicht geprueft, da ein Spieler nicht ausserhalb jedes Env bewegt
+  // werden kann)
+  if ( interactive() && environment() && query_verb() && objectp(oldenv)) {
+      connections += ({ ({ OLD_BLUE_NAME(oldenv), OLD_BLUE_NAME(dest),
+                        query_verb() + " " + (_unparsed_args(2) || ""),
+                        method, dest->QueryProp(P_PARA) }) });
+
+      if ( sizeof(connections) > 50
+           && find_call_out("flush_connections") == -1 )
+            call_out( "flush_connections", 0, connections );
+  }
+}
+
+public void flush_connections() {
+    catch(PATHD->add_paths(connections);publish);
+    connections = ({});
+}
+
+/*** Query-Methoden fuer Froesche... ;^) ***/
+static string _query_msgin()
+{
+    return QueryProp(P_FROG) ? "huepft herein" : Query(P_MSGIN);
+}
+
+
+static string _query_msgout()
+{
+    return QueryProp(P_FROG) ? "huepft" : Query(P_MSGOUT);
+}
diff --git a/std/player/objects.c b/std/player/objects.c
new file mode 100644
index 0000000..9263ea3
--- /dev/null
+++ b/std/player/objects.c
@@ -0,0 +1,243 @@
+// MorgenGrauen MUDlib
+//
+// player/objects.c -- object handling for player
+//
+// $Id: objects.c 8675 2014-02-18 20:39:54Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include "/sys/player/filesys.h"
+
+#include <config.h>
+#include <player.h>
+#include <properties.h>
+#include <language.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <thing/moving.h>
+
+static int update_object(string str) {
+  object ob;
+  string upd_file;
+  if (!(str=_unparsed_args())) {
+    notify_fail("Usage: Update <object_path>\n"); return 0;
+  }
+  upd_file = find_file(str,".c");
+  if (!upd_file) upd_file=find_file(str);
+  if (!upd_file) {
+    notify_fail(str+": No such file.\n"); return 0;
+  }
+  ob = find_object(upd_file);
+  if (!ob) {
+    notify_fail(upd_file+": No such object.\n"); return 0;
+  }
+  destruct(ob);
+  write(upd_file + ": will be reloaded at next reference.\n");
+  return 1;
+}
+
+/*
+ * "soft" means that the object is given the chance to self-destruct, thus
+ * allowing it to do necessary cleanup like subtracting from the carried
+ * weight of the environment(). We call remove() in the object to be
+ * destructed.
+ */
+static int soft_update_object(string str) 
+{
+  object ob;
+  string upd_file;
+  if (!(str=_unparsed_args())) {
+    notify_fail("Usage: update <object_path>\n"); return 0;
+  }
+  upd_file = find_file(str,".c");
+  if (!upd_file) upd_file=find_file(str);
+  if (!upd_file) {
+    notify_fail(str+": No such file.\n"); return 0;
+  }
+  ob = find_object(upd_file);
+  if (!ob) {
+    notify_fail(upd_file+": No such object.\n"); return 0;
+  }
+  if (ob->remove() == 0) {
+    notify_fail(upd_file+": doesn't want to be destructed!\n"); return 0;
+  }
+  write(upd_file + ": will be reloaded at next reference.\n");
+  return 1;
+}
+
+int clone(string str) 
+{
+  object ob;
+  string clone_file;
+
+  if (!(str=_unparsed_args())){
+    notify_fail("Usage: clone <object_path>\n"); return 0;
+  }
+  clone_file = find_file(str,".c");
+  if (!clone_file) clone_file=find_file(str);
+  if (!clone_file) {
+    notify_fail(str+": No such file.\n"); return 0;
+  }
+  if (!(ob = clone_object(clone_file)))
+    return notify_fail(str+": Failed to load.\n"), 0;
+
+  /* Some objects destruct themselves rather fast */
+  if (!objectp(ob))
+    return notify_fail(str+": Destructed whilst created.\n"), 0;
+  
+  /* try to move the object to my environment */
+  if ((ob->move(this_object(),M_GET)>0) || 
+      (ob->move(environment(),M_NOCHECK)>0))
+  {
+    if (!objectp(ob))
+      return notify_fail(str+": Destructed whilst created.\n"), 0;
+    write("Cloned "+object_name(ob)+".\n");
+    say(this_player()->name(WER,1) + " "
+		+ this_player()->QueryProp(P_CLONE_MSG)+".\n");
+    return 1;
+  }
+  say(this_player()->name(WER,1)+" malt wilde Zeichen in die Luft und "
+      +"murmelt vor sich hin, aber nichts passiert...\n");
+  destruct(ob);
+  write(clone_file+": failed to move\n");
+  return 1;
+}
+
+/*
+ * "soft" means that the object is given the chance to self-destruct, thus
+ * allowing it to do necessary cleanup like subtracting from the carried
+ * weight of the environment(). We call remove() in the object to be
+ * destructed.
+ */
+static int soft_destruct_object(string str)
+{ 
+  object ob;
+  object *obs;
+  string strWER,strWEN;
+
+  if (!(str=_unparsed_args())){
+    notify_fail("Usage: destruct <objectname>\n"); return 0;
+  }
+  strWER = lower_case(str);
+  obs = this_player()->find_obs(strWER,PUT_GET_NONE);
+  if (!obs || !sizeof(obs)) {
+    notify_fail("Kein \"" + str + "\" gefunden.\n");
+    return 0;
+  }
+  ob=obs[0];
+  strWER=ob->name(WER);
+  strWEN=ob->name(WEN);
+  if (!strWER)
+    strWER="jemand";
+  if (!strWEN)
+    strWEN="jemanden";
+
+  if (ob->remove() == 0) {
+    notify_fail(strWER+" will nicht 'destructed' werden!\n");
+    say(this_player()->name(WER,1)+" versucht vergeblich, "+strWEN+
+        " zu atomisieren.\n");
+    return 0;
+  }
+  say(capitalize(strWER)+" "+this_player()->QueryProp(P_DESTRUCT_MSG)+".\n");
+  write(capitalize(strWER)+" wird von dir zerstaeubt.\n");
+  return 1;
+}
+
+static int destruct_object(string str)
+{ 
+  object ob;
+  object *obs;
+  string strWER,strWEN;
+
+  if (!(str=_unparsed_args())) {
+    notify_fail("Usage: Destruct <objectname>\n"); return 0;
+  }
+  strWER = lower_case(str);
+  obs = this_player()->find_obs(strWER,PUT_GET_NONE);
+  if (!obs || !sizeof(obs)) {
+    notify_fail("Kein \"" + str + "\" gefunden.\n"); return 0;
+  }
+  ob=obs[0];
+  strWER=ob->name(WER);
+  strWEN=ob->name(WEN);
+
+  say(capitalize(strWER)+" "+this_player()->QueryProp(P_DESTRUCT_MSG)+".\n");
+  destruct( ob );
+  write(capitalize(strWER)+" wird von dir zerstaeubt.\n");
+  return 1;
+}
+
+static int load(string str)
+{ 
+  object env;
+  string file;
+  string err;
+
+  if (!(str=_unparsed_args())) {
+    notify_fail("Usage: load <object_path>\n"); return 0;
+  }
+  file = find_file(str,".c");
+  if (!file) file=find_file(str);
+  if (!file) {
+    notify_fail(str+": No such file.\n"); return 0;
+  }
+  if ( err = catch(load_object(file);publish) )
+    printf("Cannot load %O, err = %O\n",file,err);
+  else write(file+"\n");
+  return 1;
+}
+
+static int exec_playerob(string name)
+{
+  object ob, *inv;
+  int i;
+
+  if (!IS_LORD(this_object())) return 0;
+  if (this_player() != this_interactive()) return 0;
+  if (this_player() != this_object()) return 0;
+  if (!(name=_unparsed_args())) return 0;
+  name="secure/master"->_get_path(name,getuid(this_object()));
+  if (catch(load_object(name);publish) ) 
+  {
+    write("BUG in "+name+"\n");
+    return 1;
+  }
+  ob=clone_object(name);
+  if (!ob) return 0;
+  if (getuid(ob) != getuid(this_object()))
+  {
+    write("UID conflict.\n");
+    destruct(ob);
+    return 1;
+  }
+  log_file("EXEC", getuid(this_object())+" "+name+" "+dtime(time()));
+  disable_commands();
+  exec(ob,this_object());
+  if (interactive(this_object()) || !interactive(ob))
+  {
+    enable_commands();
+    write("Fehler in exec\n");
+    return 1;
+  }
+  inv=all_inventory(this_object());
+  ob->start_player(capitalize(getuid(this_object())));
+  remove();
+  return 1;
+}
+
+static mixed _query_localcmds()
+{
+  return ({
+           ({"clone","clone",0,WIZARD_LVL}),
+	   ({"destruct","soft_destruct_object",0,LEARNER_LVL}),
+	   ({"Destruct","destruct_object",0,LEARNER_LVL}),
+	   ({"load","load",0,WIZARD_LVL}),
+	   ({"update","soft_update_object",0,LEARNER_LVL}),
+	   ({"Update","update_object",0,LEARNER_LVL}),
+	   ({"exec","exec_playerob",0,LEARNER_LVL})
+	 });
+}
diff --git a/std/player/pklog.c b/std/player/pklog.c
new file mode 100644
index 0000000..4613ebe
--- /dev/null
+++ b/std/player/pklog.c
@@ -0,0 +1,108 @@
+// MorgenGrauen MUDlib
+/** \file /std/player/pklog.c
+* Funktion zur Detektion und Loggen von Angriffen von Spielern auf Spieler.
+* \author Zesstra
+* \date 12.08.2008
+* \version $Id$
+*/
+/* Changelog:
+*/
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic
+#pragma range_check
+
+#include <defines.h>
+#include <commands.h>
+#include <wizlevels.h>
+#include <player/base.h>
+
+#define RNAME(x) capitalize(getuid(x))
+
+/** Ist victim in einer Arena oder Schattenwelt?
+  */
+nomask int CheckArenaFight(object victim) {
+  return (object_name(environment(victim))[0..14]=="/d/schattenwelt");
+}
+
+nomask protected int CheckPlayerAttack(object attacker, object victim,
+                                       string angriffsmsg)
+{
+  // falls mal jemand ne Closure auf diese Funktion in die Finger bekommt und
+  // protected umgeht.
+  if (extern_call())
+      raise_error(sprintf("Extern Call to CheckPlayerAttack in %O!\n",ME));
+
+  // nur Spieler sind interessant.
+  if ( query_once_interactive(attacker) && query_once_interactive(victim)
+      && !( IS_LEARNER(attacker) && IS_LEARNER(victim) ) ) {
+    string filemessage, wizshout;
+    int arena;
+
+    // Arena- oder Schattenweltkampf?
+    arena=CheckArenaFight(attacker);
+
+    wizshout = sprintf("\n**** %s greift %s an. (%s) ***\n",
+        RNAME(attacker), RNAME(victim), object_name(ME));
+    filemessage = sprintf("\n[%s] %s greift %s an. (%s) %s %s\n",
+        strftime("%d%m%y-%H:%M:%S",time()), RNAME(attacker),
+        RNAME(victim), object_name(this_object()),
+        (arena ? "(ARENA)" : ""),
+        (victim->QueryProp(P_TESTPLAYER) ? "(Testspieler)" : ""));
+
+    // Angriffsmsg vom Aufrufer anhaengen.
+    if (stringp(angriffsmsg) && sizeof(angriffsmsg)) {
+      wizshout += angriffsmsg;
+      filemessage += angriffsmsg;
+    }
+    // ggf. echten TI anhaengen oder warnen, falls keiner existiert.
+    if ( this_interactive() != attacker ) {
+        if ( this_interactive() ) {
+          wizshout += "ACHTUNG: TI = " + getuid(this_interactive())
+            +"\n";
+          filemessage += "ACHTUNG: TI = "+getuid(this_interactive())
+            +"\n";
+        }
+        else {
+            filemessage += " ACHTUNG: Kein TI vorhanden!\n";
+            wizshout += " ACHTUNG: Kein TI vorhanden!\n";
+        }
+    }
+    // caller_stack() mitloggen (aber nicht Magier vollscrollen).
+    filemessage += "Callerstack: " + CountUp(map(caller_stack(1),
+          function string (object po) {return to_string(po);}),
+        ", ", ", ") + "\n";
+    // Commandstack anhaengen
+    mixed cstack = command_stack();
+    filemessage += "Commandstack: " + CountUp(map(cstack,
+          function string (mixed arr) {
+              return sprintf("({Original-TP: %O, TP: %O, Kommando: %s})",
+                arr[CMD_ORIGIN],arr[CMD_PLAYER],arr[CMD_TEXT] || "");
+          },", ",", ")) + "\n";
+    // fuer Magier originaeren Befehl anhaengen:
+    if (sizeof(cstack))
+      wizshout += sprintf("Kommando: %s\n",
+          cstack[0][CMD_TEXT] || "<unbekannt>");
+
+    wizshout += "\n";
+
+    // erstmal loggen
+    if ( arena )
+        log_file("ATTACKS_ARENA", filemessage);
+    else 
+        log_file("ATTACKS", filemessage);
+
+    // dann Magiern bescheidgeben
+    if ( !arena && !(victim->QueryProp(P_TESTPLAYER)) ) {
+      foreach(object wiz: users()) {
+        if ( IS_LORD(wiz) || IS_DEPUTY(wiz) )
+          tell_object(wiz, wizshout);
+      }
+    }
+    return 1; // Spieler-Spielerkampf
+  }
+  return 0; // kein Spieler-Spielerkampf
+}
+
diff --git a/std/player/potion.c b/std/player/potion.c
new file mode 100644
index 0000000..23d8caa
--- /dev/null
+++ b/std/player/potion.c
@@ -0,0 +1,252 @@
+// MorgenGrauen MUDlib
+//
+// player/potion.c -- potion handling for player
+//
+// $Id: potion.c 9280 2015-08-15 22:20:36Z Arathorn $
+//
+
+#pragma strong_types,save_types
+
+#include <input_to.h>
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <player/potion.h>
+#include <attributes.h>
+#include <living/life.h>
+#include <player/base.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <defines.h>
+#include <wizlevels.h>
+
+#define POTIONMASTER "/secure/potionmaster"
+
+static mixed *list;
+
+mixed *potionrooms;
+mixed *known_potionrooms;
+
+static mixed _query_potionrooms();
+static mixed _query_known_potionrooms();
+
+protected void create()
+{
+  if (!potionrooms) potionrooms = POTIONMASTER->InitialList();
+  Set(P_POTIONROOMS, NOSETMETHOD, F_SET_METHOD);  // no tampering by methods
+  Set(P_POTIONROOMS, SECURED, F_MODE_AS);         // no tampering with list
+
+  if (!known_potionrooms) known_potionrooms = ({});
+  Set(P_KNOWN_POTIONROOMS, NOSETMETHOD, F_SET_METHOD);
+  Set(P_KNOWN_POTIONROOMS, SECURED, F_MODE_AS);
+}
+
+static int ReportPotion(string s, int pnum);
+static int SelectWhich(int pnum);
+static int ask_question(int pnum);
+static int get_answer(string erg, int pnum);
+static int raise(string what, int pnum);
+int AddKnownPotion(int nr);
+int RemoveKnownPotion(int nr);
+
+protected void updates_after_restore(int newflag)
+{
+  // P_VISITED_POTIONROOMS ist lang veraltet und unbenutzt, aber bis zum
+  // 21.1.2015 sogar in neuen Spielern gespeichert worden.
+  // Aehnlich fuer P_BONUS_POTIONS. Weg damit.
+  Set(P_VISITED_POTIONROOMS, SAVE|PROTECTED, F_MODE_AD);
+  Set(P_BONUS_POTIONS, SAVE|PROTECTED, F_MODE_AD);
+}
+
+varargs int FindPotion(string s)
+{
+  object po = previous_object();
+  int pnum = POTIONMASTER->HasPotion(po);
+  int flag = 1;
+
+  if ( QueryProp(P_TRANK_FINDEN) && IS_WIZARD(ME) )
+  {
+    return ReportPotion(s, pnum);
+  }
+
+  if ( QueryProp(P_KILLS) )
+    return 0;
+
+  if ( !potionrooms || !sizeof(potionrooms) ||
+       !(POTIONMASTER->InList(po, potionrooms, known_potionrooms)) )
+    flag=0;
+
+  if ( pnum < 0 || !flag )
+    return 0;
+
+  if ( query_input_pending(ME) || query_editing(ME) )
+  {
+    tell_object(ME,
+      "Jetzt haettest Du fast einen Zaubertrank gefunden. Du solltest den\n"
+      "Editor/das More verlassen und es dann noch einmal versuchen!\n");
+    return 1;
+  }
+
+  // Hier der Ausbau der ZTs fuer Geister, wobei das natuerlich in der
+  // Geisterschlossquest immer noch gehen sollte.
+  object env = environment(ME);
+  string fn = old_explode(object_name(env), "#")[0];
+  if ( QueryProp(P_GHOST) && fn[0..24] != "/d/wald/bertram/gschloss/" )
+  {
+    tell_object(ME,"Als Geist einen Zaubertrank? Hier nicht!\n");
+    return 1;
+  }
+  log_file("ARCH/POTIONS", sprintf("%s  %s in %s\n", dtime(time()),
+    capitalize(getuid()), object_name(po)));
+
+  return ReportPotion(s, pnum);
+}
+
+static int ReportPotion(string s, int pnum)
+{
+  if (stringp(s) && sizeof(s))
+    tell_object(ME, s);
+  else
+    tell_object(ME, "Du findest einen Zaubertrank, den Du sofort trinkst.\n");
+
+  SelectWhich(pnum);
+
+  return 1;
+}
+
+static int SelectWhich(int pnum)
+{
+  list=({"Intelligenz","Kraft","Geschicklichkeit","Ausdauer"});
+  if (QueryRealAttribute(A_INT)>=20) list-=({"Intelligenz"});
+  if (QueryRealAttribute(A_STR)>=20) list-=({"Kraft"});
+  if (QueryRealAttribute(A_DEX)>=20) list-=({"Geschicklichkeit"});
+  if (QueryRealAttribute(A_CON)>=20) list-=({"Ausdauer"});
+  if (!sizeof(list)) {
+    heal_self(10000000);
+    tell_object(ME, "Der Trank hat Dich geheilt.\n");
+    log_file("ARCH/POTIONS",
+       sprintf("  %s: Heiltrank (noch %d)\n",
+         capitalize(getuid()), sizeof(potionrooms)-1));
+
+    potionrooms -= ({pnum});
+    known_potionrooms -= ({pnum});
+    save_me(1);
+    return 1;
+  }
+  if ( sizeof(list)==1 )
+    return raise(list[0], pnum);
+  ask_question(pnum);
+  return 1;
+}
+
+static int ask_question(int pnum)
+{
+  int i;
+
+  tell_object(ME, "Deine Attribute sehen so aus:\n\n"
+    +sprintf("Intelligenz     : %2d (%+d)\n",
+         QueryRealAttribute(A_INT),QueryAttributeOffset(A_INT))
+    +sprintf("Kraft           : %2d (%+d)\n",
+         QueryRealAttribute(A_STR),QueryAttributeOffset(A_STR))
+    +sprintf("Geschicklichkeit: %2d (%+d)\n",
+         QueryRealAttribute(A_DEX),QueryAttributeOffset(A_DEX))
+    +sprintf("Ausdauer        : %2d (%+d)\n",
+         QueryRealAttribute(A_CON),QueryAttributeOffset(A_CON))
+    );
+
+  tell_object(ME,
+    "\nWas moechtest Du erhoehen? Du hast folgende Moeglichkeiten:\n");
+  for (i=0; i<sizeof(list); i++)
+    tell_object(ME, sprintf("%d) %s\n", i+1, list[i]));
+
+  input_to("get_answer", INPUT_PROMPT,
+    sprintf("\nBitte gib jetzt eine Zahl (1-%d) an: ", i), pnum);
+
+  return 1;
+}
+
+static int get_answer(string erg, int pnum)
+{
+  int num = to_int(erg);
+
+  if ( num > 0 && num <= sizeof(list) )
+    return raise(list[num-1], pnum);
+
+  tell_object(ME, "Deine Wahl war ungueltig. Bitte versuch's nochmal!\n\n");
+  return ask_question(pnum);
+}
+
+static int raise(string what, int pnum) {
+  string attr;
+
+  switch (what)
+  {
+    case "Geschicklichkeit": attr=A_DEX; break;
+    case "Intelligenz":      attr=A_INT; break;
+    case "Kraft":            attr=A_STR; break;
+    case "Ausdauer":         attr=A_CON; break;
+    default:                 return 0;
+  }
+
+  int yet=QueryRealAttribute(attr);
+  SetRealAttribute(attr, yet+1);
+  tell_object(ME,
+    sprintf("Deine %s hat sich von %d auf %d erhoeht.\n", what, yet, yet+1));
+  log_file("ARCH/POTIONS",
+     sprintf("  %s: %s %d->%d (noch %d)\n",
+       capitalize(getuid()), capitalize(attr),
+       yet, yet+1, sizeof(potionrooms)-1));
+
+  // Wenn die Property gesetzt ist, wird nicht versucht, den gefundenen Trank
+  // aus der ZT-Liste des Magiers auszutragen. Der Nebeneffekt, dass
+  // existierende, angeschlossene ZTs auch nicht mehr aus der ZT-Liste von
+  // testenden Magiern ausgetragen werden, wird dabei in Kauf genommen.
+  if ( !QueryProp(P_TRANK_FINDEN) )
+  {
+    potionrooms       -= ({ pnum });
+    known_potionrooms -= ({ pnum });
+    save_me(1);
+  }
+  return 1;
+}
+
+static mixed _query_potionrooms()
+{
+  return copy(Set(P_POTIONROOMS, potionrooms));
+}
+
+static mixed _query_known_potionrooms()
+{
+  return copy(Set(P_KNOWN_POTIONROOMS, known_potionrooms));
+}
+
+int AddKnownPotion(int nr)
+{
+  if (!previous_object() ||
+      object_name(previous_object()) != "/room/orakel")
+    return -1; // Keine Berechtigung
+
+  if (member(known_potionrooms, nr) != -1)
+    return -2; // Nummer bereits eingetragen
+  else
+  {
+    known_potionrooms += ({ nr });
+    return 1;
+  }
+}
+
+int RemoveKnownPotion(int nr)
+{
+  if (!previous_object() ||
+      object_name(previous_object()) != "/room/orakel")
+    return -1; // Keine Berechtigung
+
+  if (member(known_potionrooms, nr) == -1)
+    return -2; // Nummer nicht eingetragen
+  else
+  {
+    known_potionrooms -= ({ nr });
+    return 1;
+  }
+}
diff --git a/std/player/protocols/gmcp.c b/std/player/protocols/gmcp.c
new file mode 100644
index 0000000..b951daa
--- /dev/null
+++ b/std/player/protocols/gmcp.c
@@ -0,0 +1,785 @@
+// MorgenGrauen MUDlib
+//
+// gmcp.c -- Verwaltung von GMCP im Spielerobjekt
+//
+// $Id$
+
+#pragma strong_types,save_types
+#pragma range_check
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic
+
+#include <regexp.h>
+#include <telnet.h>
+
+#define NEED_PROTOTYPES
+#include <player/base.h>
+#include <thing/properties.h>
+#include <living/attributes.h>
+#include <player/gmcp.h>
+#include <thing/description.h>
+#include <living/description.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <new_skills.h>
+#include <rooms.h>
+#include <tls.h>
+
+inherit "/secure/telnetneg-structs.c";
+
+struct gmcp_mod_s {
+  string id;        // Name des GMCP-Moduls (z.B. "MG.Char")
+  int version;      // Version des aktivierten moduls
+  closure sendcl;   // Handler zum Senden (lfun-closure)
+  closure recvcl;   // Handler zum Empfangen (lfunc-closure)
+  mixed data;       // optional data of the module
+};
+
+nosave mapping gmcpdata;
+/* Struktur:
+   Jedes Modul hat einen Schluessel im Toplevel, worunter ggf. seine Daten
+   abgelegt sind. Die Daten sind eine struct gmcp_mod_s. Der Schluessel ist
+   der Modulname OHNE Version.
+   */
+#define NEED_PROTOTYPES
+#include "/secure/telnetneg.h"
+#undef NEED_PROTOTYPES
+
+//#define __GMCP_DEBUG__ 1
+// Low priority debug messages
+#define GMCP_DEBUG(pre,msg,prio) if (interactive(this_object()) \
+                            && gmcpdata) \
+                          GMCP_debug(pre,msg,prio);
+// higher priority error messages
+#define GMCPERROR(msg) if (interactive(this_object()) \
+                       && gmcpdata) \
+                     GMCP_debug("ERROR",msg,10);
+
+
+// **************** API nach Aussen folgt ab hier ********************
+
+// Wird vom Spielerobjekt gerufen, wenn sich Daten am Charakter veraendert
+// haben, die gesendet werden sollten.
+// Dies ist eigentlich nur ein Wrapper, der die Daten an den Handler eines
+// Moduls weitergibt, welches vom Client aktiviert wurde. Hierzu kommen zur
+// Zeit 2 in Frage: MG.Char (bevorzugt) und Char (minimaler Support).
+/*protected*/ int GMCP_Char(mapping data) {
+
+  if (!mappingp(gmcpdata)) return 0;
+
+  // Als erstes schauen, ob der Client MG.Char aktiviert hat.
+  struct gmcp_mod_s mod = gmcpdata["MG.char"];
+  if (structp(mod) && closurep(mod->sendcl))
+  {
+    funcall(mod->sendcl, data);
+    return 1;
+  }
+  // Dann noch das Modul char pruefen. Das ist aber ziemlich eingeschraenkt
+  // und es gibt hoffentlich nicht viele Clients, die es benutzen.
+  // (Aardwolf-Modul)
+  mod = gmcpdata["char"];
+  if (structp(mod) && closurep(mod->sendcl))
+  {
+    funcall(mod->sendcl, data);
+    return 1;
+  }
+  // Dann noch das Modul Char pruefen. Das ist aber ziemlich eingeschraenkt
+  // und es gibt hoffentlich nicht viele Clients, die es benutzen.
+  // (IRE-Modul)
+  mod = gmcpdata["Char"];
+  if (structp(mod) && closurep(mod->sendcl))
+  {
+    funcall(mod->sendcl, data);
+    return 1;
+  }
+  return 0;
+}
+
+/*protected*/ int GMCP_Channel(string msg, string channel, string sender) {
+  if (!mappingp(gmcpdata)) return 0;
+  // comm.channel Modul aktiv?
+  struct gmcp_mod_s mod = gmcpdata["comm.channel"];
+  if (structp(mod) && closurep(mod->sendcl))
+  {
+    funcall(mod->sendcl, (["chan":channel, "player": sender,
+                           "msg": msg]) );
+    return 1;
+  }
+  return 0;
+}
+
+/*protected*/ int GMCP_Room() {
+  if (!mappingp(gmcpdata)) return 0;
+  // MG.room Modul aktiv?
+  struct gmcp_mod_s mod = gmcpdata["MG.room"];
+  if (structp(mod) && closurep(mod->sendcl))
+  {
+    funcall(mod->sendcl, 0);
+    return 1;
+  }
+  return 0;
+}
+
+// **************** Ab hier folgen eher die Lowlevel-Dinge ***********
+private void GMCP_debug(string pre, string msg, int prio) {
+  struct gmcp_mod_s mod = gmcpdata["Core"];
+  if (mod && (mod->data)["Debug"] >= prio)
+    tell_object(this_object(), sprintf("GMCP %s: %s\n",pre,msg));
+}
+
+private void GMCP_send(string cmd, mixed data)
+{
+  GMCP_DEBUG("GMCP_send",sprintf("%s %O",cmd,data), 30);
+  send_telnet_neg_str(sprintf("%c%c%s %s", SB, TELOPT_GMCP,
+                              cmd, json_serialize(data)), 1);
+}
+
+private void GMCP_unregister_module(string mod)
+{
+  int version;
+  // Wenn nicht "mod version" Schema, ignorieren
+  if (sscanf(mod, "%s %d", mod, version) != 2)
+      return;
+
+  if (mod=="Core") // darf nicht abgeschaltet werden.
+      return;
+
+  m_delete(gmcpdata, mod);
+}
+
+private void GMCP_register_module(string modname)
+{
+  int version;
+  GMCP_DEBUG("register_module(): trying ... ",modname, 20);
+  // Wenn nicht "mod version" Schema, ignorieren
+  if (sscanf(modname, "%s %d", modname, version) != 2)
+      return;
+
+//  GMCP_DEBUG("register_module()",modname + " v" + version);
+
+  // Modul (ggf. mit anderer Version) bereits aktiv?
+  struct gmcp_mod_s mod = gmcpdata[modname];
+  if (structp(mod)) {
+    // wenn gleicher Name und Version, wird nix gemacht, bei anderer Version
+    // wird ein neuer Handler eingetragen und die Daten geloescht.
+    // Wenn nicht-existierende Modul/Version-Kombi angefordert wird, ist das
+    // Modul hinterher aus.
+    if (mod->id == modname && mod->version == version)
+      return;
+    else
+      m_delete(gmcpdata,modname);
+  }
+
+  // Das GMCP-Modul ist nur verfuegbar, wenn es zu der Kombination aus mod und
+  // version einen Handler zum Senden gibt...
+  // Der Handler ist: GMCP_<mod>_v<version>_send, aber in <mod> werden alle
+  // "." durch "_" ersetzt.
+  string replacedname = regreplace(modname, "\\.", "_", RE_GLOBAL);
+  closure sendcl = symbol_function(sprintf("GMCPmod_%s_v%d_send",
+                      replacedname, version),
+                   this_object());
+  if (!sendcl)
+    return;
+  // Diese Closure darf 0 sein. Dann findet keine Behandlung von vom Client
+  // gesendeten Kommandos statt. Was fuer die meisten Module auch in Ordnung
+  // ist, da sie dem Client keine Kommandos anbieten.
+  closure recvcl = symbol_function(sprintf("GMCPmod_%s_v%d_recv",
+                      replacedname, version),
+                   this_object());
+
+  GMCP_DEBUG("register_module()",modname+" erfolgreich registriert.",10);
+
+  mod = (<gmcp_mod_s> id: modname, version : version,
+         sendcl : sendcl, recvcl: recvcl, data: ([]) );
+  gmcpdata[modname] = mod;
+
+  // Zum schluss noch den Senden-handler mal rufen, damit der mal alle
+  // verfuegbaren daten sendet.
+  funcall(mod->sendcl, 0);
+}
+
+// Handler fuer das Core Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_Core_v1_recv(string cmd, mixed args)
+{
+  struct gmcp_mod_s mod = gmcpdata["Core"];
+  mapping data = mod->data;
+
+/*  if (!mod)
+  {
+    GMCPERROR("Command %s for disabled module ignored.");
+    return;
+  }
+  */
+  GMCP_DEBUG("GMCPmod_Core_v1: ", cmd, 20);
+
+  switch (cmd)
+  {
+    case "Core.Hello":
+      if (mappingp(args))
+        data["Hello"] = (["client": args["client"],
+                          "version": args["version"] ]);
+      break;
+    case "Core.Supports.Set":
+      if (pointerp(args))
+      {
+        // Alte Module abschalten/loeschen
+        foreach(string m : data["Supports"])
+          GMCP_unregister_module(m);
+        data["Supports"] = args;
+        // Versuchen, die neuen Module zu registrieren
+        foreach(string m : args)
+          GMCP_register_module(m);
+      }
+      else
+          GMCP_DEBUG("GMCPmod_Core_v1: ",
+              "Data for Core.Supports.Set is no array", 5);
+      break;
+    case "Core.Supports.Add":
+      if (!pointerp(data["Supports"]))
+        data["Supports"] = ({});
+      if (pointerp(args))
+      {
+        foreach(string m: args)
+          GMCP_register_module(m);
+        data["Supports"] += args;
+      }
+      break;
+    case "Core.Supports.Remove":
+      if (!pointerp(data["Supports"]))
+        break;
+      if (pointerp(args))
+      {
+        foreach(string m: args)
+          GMCP_unregister_module(m);
+        data["Supports"] -= args;
+      }
+      break;
+    case "Core.Supports.KeepAlive":
+      break;  // this is ignored by us.
+    case "Core.Ping":
+      if (intp(args))
+        data["Ping"] = args;
+      // send a ping back
+      GMCP_send("Core.Ping",0);
+      break;
+    case "Core.Debug":
+      if (intp(args) && args >= 0)
+        data["Debug"] = args;
+      break;
+    default:
+      GMCPERROR(sprintf("Unknown GMCP Core cmd %s with args %O",
+            cmd, args));
+      break;
+  }
+}
+
+// Handler fuer das Core Modul von GMCP
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_Core_v1_send(mapping data)
+{
+  // Zur Zeit nix, spaeter mal Core.Goodbye.
+}
+
+
+// Handler fuer das MG.Char Modul
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_char_v1_send(mapping data)
+{
+  mapping squeue = m_allocate(5,0);
+  struct gmcp_mod_s mod = gmcpdata["MG.char"];
+  // mod->data fungiert hier auch als Cache der Daten. Die muss man naemlich
+  // leider immer alle senden, nicht nur die geaenderten.
+  if (!mappingp(data))
+  {
+    // Alle verfuegbaren Informationen senden...
+    mod->data = m_allocate(6);
+    m_add(mod->data, "MG.char.base",
+              ([P_NAME: Name(WER),
+                P_GUILD: QueryProp(P_GUILD),
+                P_PRESAY: QueryProp(P_PRESAY),  // TODO
+                P_TITLE: QueryProp(P_TITLE),
+                "wizlevel": query_wiz_level(this_object()),
+                P_RACE: QueryProp(P_RACE)]) );  // TODO
+    m_add(mod->data,"MG.char.vitals", 
+              ([P_HP: QueryProp(P_HP),
+                P_SP: QueryProp(P_SP),
+                P_POISON: QueryProp(P_POISON) ]) );
+    m_add(mod->data,"MG.char.maxvitals",
+              ([P_MAX_HP: QueryProp(P_MAX_HP),
+                P_MAX_SP: QueryProp(P_MAX_SP),
+                P_MAX_POISON: QueryProp(P_MAX_POISON) ]) );
+    m_add(mod->data,"MG.char.attributes",
+              ([ A_STR: QueryAttribute(A_STR),
+                 A_INT: QueryAttribute(A_INT),
+                 A_DEX: QueryAttribute(A_DEX),
+                 A_CON: QueryAttribute(A_CON) ]) );
+    m_add(mod->data,"MG.char.info",
+              ([P_LEVEL: QueryProp(P_LEVEL),
+                P_GUILD_LEVEL: QueryProp(P_GUILD_LEVEL),
+                P_GUILD_TITLE: QueryProp(P_GUILD_TITLE) ]) );
+    m_add(mod->data,"MG.char.wimpy",
+              ([P_WIMPY: QueryProp(P_WIMPY),
+                P_WIMPY_DIRECTION: QueryProp(P_WIMPY_DIRECTION) ]) );
+    m_add(squeue,"MG.char.base");
+    m_add(squeue,"MG.char.vitals");
+    m_add(squeue,"MG.char.maxvitals");
+    m_add(squeue,"MG.char.attributes");
+    m_add(squeue,"MG.char.info");
+    m_add(squeue,"MG.char.wimpy");
+    // dies wird direkt gesendet, weil es nicht gespeichert werden muss. (wird
+    // nur beim Start des Moduls gesendet).
+    GMCP_send("MG.char.infoVars", ([
+          P_LEVEL: "Spielerstufe", P_GUILD_LEVEL: "Gildenstufe",
+          P_GUILD_TITLE: "Gildentitel" ]) );
+  }
+  else
+  {
+    // nur die in data enthaltenen senden.
+    // jetzt erstmal alles aus data so sortieren, wie es gesendet werden
+    // muss... *seufz*
+    foreach(string key, mixed val : data)
+    {
+      switch(key)
+      {
+        case P_HP:
+        case P_SP:
+        case P_POISON:
+          (mod->data)["MG.char.vitals"] += ([key: val]);
+          m_add(squeue,"MG.char.vitals");
+          break;
+        case P_MAX_HP:
+        case P_MAX_SP:
+        case P_MAX_POISON:
+          (mod->data)["MG.char.maxvitals"] += ([key: val]);
+          m_add(squeue,"MG.char.maxvitals");
+          break;
+        case P_NAME:
+          (mod->data)["MG.char.base"] += ([key: Name(WER)]);
+          m_add(squeue,"MG.char.base");
+          break;
+        case P_RACE:
+        case P_PRESAY:
+        case P_TITLE:
+        case P_GUILD:
+          (mod->data)["MG.char.base"] += ([key: val]);
+          m_add(squeue,"MG.char.base");
+          break;
+        case A_DEX:
+        case A_STR:
+        case A_CON:
+        case A_INT:
+          (mod->data)["MG.char.attributes"] += ([key: val]);
+          m_add(squeue,"MG.char.attributes");
+          break;
+        case P_LEVEL:
+        case P_GUILD_LEVEL:
+        case P_GUILD_TITLE:
+          (mod->data)["MG.char.info"] += ([key: val]);
+          m_add(squeue,"MG.char.info");
+          break;
+        case P_WIMPY:
+        case P_WIMPY_DIRECTION:
+          (mod->data)["MG.char.wimpy"] += ([key: val]);
+          m_add(squeue,"MG.char.wimpy");
+          break;
+      }
+    }
+  }
+  GMCP_DEBUG("GMCPmod_MG_char_v1_send()",
+      sprintf("Data ready: %O, Sendqueue: %O",mod->data, squeue),50);
+
+  // Jetzt die squeue senden...
+  foreach(string key : squeue)
+  {
+    GMCP_send(key, (mod->data)[key]);
+  }
+}
+
+// Handler fuer das MG.Char Modul
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_char_v1_recv(string cmd, mixed args)
+{
+  // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+  GMCP_DEBUG("GMCPmod_MG_Char_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+
+/*
+// Handler fuer das MG.Room Modul von GMCP
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_MG_Room_v1_send(mapping data)
+{
+}
+
+// Handler fuer das Room Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_Room_v1_recv(string cmd, mixed args)
+{
+  // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+  GMCP_DEBUG("GMCPmod_MG_Room_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+*/
+
+// Recv Handler fuer das comm.channel Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_comm_channel_v1_recv(string cmd, mixed args)
+{
+  GMCP_DEBUG("GMCPmod_comm_channel_v1_recv",
+      "Client-Kommando ignoriert: "+cmd,20);
+}
+
+// Send Handler fuer das comm.channel Modul von GMCP
+protected void GMCPmod_comm_channel_v1_send(mapping data)
+{
+  // Ganz simpel: einfach raussenden...
+  // Core uebergibt beim Einschalten 0 als data. Dieses modul muss aber beim
+  // Eisnchalten nix machen. Also nur ignorieren.
+  if (mappingp(data))
+    GMCP_send("comm.channel", data);
+}
+
+// Recv Handler fuer das MG.room Modul von GMCP
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_MG_room_v1_recv(string cmd, mixed args)
+{
+  GMCP_DEBUG("GMCPmod_MG_room_v1_recv",
+      "Client-Kommando ignoriert: "+cmd,20);
+}
+
+// Send Handler fuer das comm.channel Modul von GMCP
+protected void GMCPmod_MG_room_v1_send(mapping data)
+{
+  // Bekommt immer 0 als <data> uebergeben und sucht sich die Daten aus dem
+  // Raum zusammen.
+
+  // Baeh. Warum wird das denn ohne Env gerufen. :-(
+  if (!environment())
+    return;
+
+  // Blind gibt es auch per GMCP nix.
+  if (CannotSee(1))
+    return;
+
+  int restr = environment()->QueryProp(P_MAP_RESTRICTIONS);
+
+  if (restr & MR_NOINFO)
+    return; // gar keine info senden.
+
+  // Anmerkung: int_short() waere cool. Dummerweise uebertraegt das auch
+  // sichtbare Ausgange und Objekte. Insofern: geht nicht.
+  data = ([
+      P_SHORT: process_string(environment()->QueryProp(P_INT_SHORT)||"")+".",
+      "domain": environment()->QueryProp(P_DOMAIN) || "unbekannt",
+      ]);
+
+  // sichtbare Ausgaenge ausgeben
+  mixed hide = environment()->QueryProp(P_HIDE_EXITS);
+  if (hide && !pointerp(hide))
+      data["exits"] = ({});   // alle verstecken
+  else
+  {
+      // Query() verwenden, damit sowohl normale als auch Special Exits
+      // kommen... Die Summe von beiden wuerde auch gehen, aber dann hat man
+      // zwei unnoetige Filter in den Querymethoden. Hngl.
+      mapping exits = environment()->Query(P_EXITS, F_VALUE) || ([]);
+      if (pointerp(hide))
+        data["exits"] = m_indices(exits) - hide;
+      else
+        data["exits"] = m_indices(exits);
+  }
+
+  if (restr & MR_NOUID)
+    data["id"] = "";
+  else
+    data["id"] = hash(TLS_HASH_MD5, object_name(environment()));
+
+  GMCP_send("MG.room.info", data);
+}
+
+
+// Handler fuer das "char" Modul von GMCP (Modul von Aardwolf)
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_char_v1_send(mapping data)
+{
+  mapping squeue = m_allocate(4,0);
+  struct gmcp_mod_s mod = gmcpdata["char"];
+  // mod->data fungiert hier auch als Cache der Daten. Die muss man naemlich
+  // leider immer alle senden, nicht nur die geaenderten.
+  if (!mappingp(data))
+  {
+    // Alle verfuegbaren Informationen senden...
+    mod->data = m_allocate(4);
+    m_add(mod->data, "char.base", (["name": query_real_name(),
+                                 "race": QueryProp(P_RACE)]) );
+    m_add(mod->data,"char.vitals", (["hp": QueryProp(P_HP),
+                                  "mana": QueryProp(P_SP)]) );
+    m_add(mod->data,"char.stats", ([ "str": QueryAttribute(A_STR),
+                               "int": QueryAttribute(A_INT),
+                               "dex": QueryAttribute(A_DEX),
+                               "con": QueryAttribute(A_CON) ]) );
+    m_add(mod->data,"char.status", (["level": QueryProp(P_LEVEL) ]) );
+    m_add(squeue,"char.base");
+    m_add(squeue,"char.vitals");
+    m_add(squeue,"char.stats");
+    m_add(squeue,"char.status");
+  }
+  else
+  {
+    // nur die in data enthaltenen senden.
+    // jetzt erstmal alles aus data so sortieren, wie es gesendet werden
+    // muss... *seufz*
+    foreach(string key, mixed val : data)
+    {
+      switch(key)
+      {
+        case P_HP:
+          (mod->data)["char.vitals"] += (["hp": val]);
+          m_add(squeue,"char.vitals");
+          break;
+        case P_SP:
+          (mod->data)["char.vitals"] += (["mana": val]);
+          m_add(squeue,"char.vitals");
+          break;
+        case P_NAME:
+        case P_RACE:
+          (mod->data)["char.base"] += ([key: val]);
+          m_add(squeue,"char.base");
+          break;
+        case A_DEX:
+        case A_STR:
+        case A_CON:
+        case A_INT:
+          (mod->data)["char.stats"] += ([key: val]);
+          m_add(squeue,"char.stats");
+          break;
+        case P_LEVEL:
+          (mod->data)["char.status"] += ([key: val]);
+          m_add(squeue,"char.status");
+          break;
+      }
+    }
+  }
+  GMCP_DEBUG("GMCPmod_char_v1_send()",
+      sprintf("Data ready: %O, Sendqueue: %O",mod->data, squeue),50);
+
+  // Jetzt die squeue senden...
+  foreach(string key : squeue)
+  {
+    GMCP_send(key, (mod->data)[key]);
+  }
+}
+
+// Handler fuer das "char" Modul von GMCP (Modul von Aardwolf)
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_char_v1_recv(string cmd, mixed data)
+{
+  // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+  GMCP_DEBUG("GMCPmod_char_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+
+
+// Handler fuer das "Char" Modul von GMCP (Modul von IRE)
+// Gerufen, wenn Daten zu senden sind.
+protected void GMCPmod_Char_v1_send(mapping data)
+{
+  mapping squeue = m_allocate(4,0);
+  struct gmcp_mod_s mod = gmcpdata["Char"];
+  // mod->data fungiert hier auch als Cache der Daten. Die muss man naemlich
+  // leider immer alle senden, nicht nur die geaenderten.
+  if (!mappingp(data))
+  {
+    // Alle verfuegbaren Informationen senden...
+    mod->data = m_allocate(4);
+    m_add(mod->data,"Char.Vitals", (["hp": QueryProp(P_HP),
+                                     "mp": QueryProp(P_SP),
+                                     "maxhp": QueryProp(P_MAX_HP),
+                                     "maxmp": QueryProp(P_MAX_SP) ]) );
+    m_add(mod->data,"Char.Status", (["level": QueryProp(P_LEVEL),
+                                     "guild": QueryProp(P_GUILD) ]) );
+    m_add(squeue,"Char.Vitals");
+    m_add(squeue,"Char.Status");
+    // dies wird direkt gesendet, weil es nicht gespeichert werden muss. (wird
+    // nur beim Start des Moduls gesendet).
+    GMCP_send("Char.StatusVars", ([
+          "level": "Spielerstufe", "guild": "Gilde" ]) );
+  }
+  else
+  {
+    // nur die in data enthaltenen senden.
+    // jetzt erstmal alles aus data so sortieren, wie es gesendet werden
+    // muss... *seufz*
+    foreach(string key, mixed val : data)
+    {
+      switch(key)
+      {
+        case P_HP:
+          (mod->data)["Char.Vitals"] += (["hp": val]);
+          m_add(squeue,"Char.Vitals");
+          break;
+        case P_SP:
+          (mod->data)["Char.Vitals"] += (["mp": val]);
+          m_add(squeue,"Char.Vitals");
+          break;
+        case P_MAX_HP:
+          (mod->data)["Char.Vitals"] += (["maxhp": val]);
+          m_add(squeue,"Char.Vitals");
+          break;
+        case P_MAX_SP:
+          (mod->data)["Char.Vitals"] += (["maxmp": val]);
+          m_add(squeue,"Char.Vitals");
+          break;
+        case P_LEVEL:
+        case P_GUILD:
+          (mod->data)["Char.Status"] += ([key: val]);
+          m_add(squeue,"Char.Status");
+          break;
+      }
+    }
+  }
+  GMCP_DEBUG("GMCPmod_Char_v1_send()",
+      sprintf("Data ready: %O, Sendqueue: %O",mod->data, squeue),50);
+
+  // Jetzt die squeue senden...
+  foreach(string key : squeue)
+  {
+    GMCP_send(key, (mod->data)[key]);
+  }
+}
+
+// Handler fuer das "char" Modul von GMCP (Modul von Aardwolf)
+// Gerufen bei Empfang von Kommandos vom Client.
+protected void GMCPmod_Char_v1_recv(string cmd, mixed args)
+{
+  // dieses Modul bietet dem Client keine Kommandos an, daher ignorieren.
+  GMCP_DEBUG("GMCPmod_Char_v1_recv","Client-Kommando ignoriert: "+cmd,20);
+}
+
+
+// Handler, der von telnetneg.c gerufen wird.
+private void _std_re_handler_gmcp(struct telopt_s opt, int action,
+                                  int *optargs)
+{
+  switch(action)
+  {
+    case LOCALON:
+      // super!
+      GMCP_DEBUG("recv:", "LOCALON",10);
+      gmcpdata = ([]);
+      opt->data = gmcpdata;   // daten auch dort ablegen.
+      // Coremodule in der Version 1 registrieren (es gibt nur eine).
+      GMCP_register_module("Core 1");
+#ifdef __GMCP_DEBUG__
+      GMCPmod_Core_v1_recv("Core.Debug",30);
+#endif
+      break;
+    case LOCALOFF:
+      // alles abschalten und Daten loeschen
+      GMCP_DEBUG("recv:", "LOCALOFF",10);
+      opt->data = 0;
+      gmcpdata = 0;
+      break;
+    case REMOTEON:
+    case REMOTEOFF:
+      // Huch. Auf Clientseite ist GMCP eigentlich nie an. Ignorieren...
+      GMCP_DEBUG("recv:", "Huh? REMOTE state changed?",50);
+      break;
+    case SB:
+      // Der eigentlich interessante Fall... GMCP-Kommandos
+      if (!mappingp(gmcpdata)) return; // GMCP wohl nicht eingeschaltet...
+      string cmd;
+      mixed args;
+      string payload=to_string(optargs);
+      GMCP_DEBUG("recv", payload,10);
+      if (sscanf(payload,"%s %s", cmd, args) != 2) {
+        // ist vermutlich ein Kommando ohne daten (oder Muell)
+        cmd = payload;
+        //args = 0;
+      }
+      else
+      {
+        string err=catch(args = json_parse(args);nolog);
+        if (err)
+        {
+          printf("\nFehler beim Parsen einer GMCP-Nachricht: %s. "
+                 "Nachricht war: '%s'\n"
+                 "Befehl: '%s', Argument: '%s'\n\n",err,payload,cmd,args||"");
+          return;
+        }
+      }
+      GMCP_DEBUG("recv", sprintf("Command: %s, Data: %O", cmd, args),20);
+
+      string *cmdparts = explode(cmd, ".");
+      struct gmcp_mod_s mod;
+      string modname;
+      // versuch, ein Modul fuer das Kommando zu finden. Anfangen mit der
+      // Annahme, dass bis zum letzten Punkt der Modulname geht und dann
+      // in jedem case einen Punkt kuerzer werdend.
+      switch(sizeof(cmdparts))
+      {
+        case 4:
+          modname = implode(cmdparts[0..2],".");
+          GMCP_DEBUG("trying modname... ", modname, 20 );
+          if (member(gmcpdata, modname)) {
+            mod = gmcpdata[modname];
+            funcall(mod->recvcl, cmd, args);
+            break;
+          }
+          // Fall-through!
+        case 3:
+          modname = implode(cmdparts[0..1],".");
+          GMCP_DEBUG("trying modname... ", modname, 20);
+          if (member(gmcpdata, modname)) {
+            mod = gmcpdata[modname];
+            funcall(mod->recvcl, cmd, args);
+            break;
+          }
+          // Fall-through!
+        case 2:
+          modname = implode(cmdparts[0..0],".");
+          GMCP_DEBUG("trying modname... ", modname, 20);
+          if (member(gmcpdata, modname)) {
+            mod = gmcpdata[modname];
+            funcall(mod->recvcl, cmd, args);
+            break;
+          }
+          // Hier ists jetzt nen Fehler.
+          GMCPERROR(sprintf("Unknown GMCP module for cmd %s",cmd));
+          break;
+        default:
+          // zuviele oder zuwenig . ;-)
+          GMCPERROR(sprintf("Illegal GMCP cmd %s with args %O",
+                cmd, args));
+          break;
+      }
+      // sbdata brauchen wir eigentlich nicht mehr.
+      opt->re_wishes->sbdata = 0;
+      break;
+  } // switch (action)
+}
+
+// wird von base.c nach Konnektierung gerufen.
+// Darf aber erst gerufen werden, wenn das Spielerobjekt fertig initialisiert
+// und eingelesen ist.
+protected void startup_telnet_negs()
+{
+  // evtl. war es ein reconnect, dann steht in gmcp noch alter kram drin. Der
+  // muss weg, koennte ja auch sein, dass der Client (jetzt) kein GMCP
+  // mehr
+  // will.
+  gmcpdata = 0;
+
+  // Hack besonderer Sorte: GMCP soll lokal eingeschaltet sein. Auf
+  // Clientseiten ist es laut Protokoll nicht vorgesehen, daher duerfen
+  // (sollten?) wir kein DO an den Client senden. Wir brauchen aber einen
+  // remote handler, um die Wuensche vom Client zu verarbeiten. Daher erstmal
+  // nur den local handler binden (und gleichzeitig negotiation anstossen) und
+  // dann direkt danach den remote handler auch binden (ohne erneute
+  // negotiation zu starten). Achja und wir nehmen die gleiche Funktion als
+  // Handler fuer remote und lokal.
+  bind_telneg_handler(TELOPT_GMCP, 0, #'_std_re_handler_gmcp, 1);
+  bind_telneg_handler(TELOPT_GMCP, #'_std_re_handler_gmcp,
+                                   #'_std_re_handler_gmcp, 0);
+}
+
diff --git a/std/player/quests.c b/std/player/quests.c
new file mode 100644
index 0000000..aae566d
--- /dev/null
+++ b/std/player/quests.c
@@ -0,0 +1,244 @@
+// MorgenGrauen MUDlib
+//
+// player/quests.c -- quest handler
+//
+// $Id: quests.c 9142 2015-02-04 22:17:29Z Zesstra $
+
+// Dieses Modul enhaelt die Quest-spezifischen Teile der Playerobjekte.
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <player/life.h>
+#include <player/quest.h>
+#include <thing/properties.h>
+#include <player/base.h>
+#include <living/life.h>
+#undef NEED_PROTOTYPES
+
+#include "/secure/questmaster.h"
+#include <wizlevels.h>
+#include <daemon.h>
+#include <language.h>
+#include <mail.h>
+#include <defines.h>
+#include <new_skills.h>
+#include <properties.h>
+#include <events.h>
+
+mixed quests;
+
+int QueryQuest(string questname);
+// local properties prototype
+static mixed _query_quests();
+static int   _query_questpoints();
+
+protected void create() {
+  Set(P_QUESTS, NOSETMETHOD, F_SET_METHOD);
+  Set(P_QUESTS, quests = ([]), F_VALUE);
+  Set(P_QUESTS, SECURED, F_MODE);
+  Set(P_QP, SAVE, F_MODE);
+  Set(P_QP, SECURED, F_MODE);
+}
+
+varargs int GiveQuest(string questname, string message) {
+  mixed *quest = QM->QueryQuest(questname);
+
+  // Questname ungueltig
+  if (!quest||!pointerp(quest)||quest==({}))
+    return GQ_KEY_INVALID;
+  // Unbefugter Zugriff auf deaktivierte Quest
+  if (!quest[6]&&!IS_ARCH(this_interactive()))
+    return GQ_IS_INACTIVE;
+  // Unbefugter Zugriff
+  if (member(quest[2], load_name(previous_object()))==-1 &&
+      !IS_ARCH(this_interactive()))
+    return GQ_ILLEGAL_OBJ;
+
+  // Gilde wird in jedem Fall informiert.
+  string guild=GUILD_DIR+QueryProp(P_GUILD);
+  if (find_object(guild) || file_size(guild+".c")>-1)
+    catch( call_other(guild, "NotifyGiveQuest", ME, questname);publish );
+
+  // Quest bereits gesetzt
+  if (QueryQuest(questname))
+    return GQ_ALREADY_SET;
+  AddExp(quest[1]);
+  quests += ([ questname : quest[0]; time() ]);
+  force_save();
+  // Event ausloesen
+  EVENTD->TriggerEvent(EVT_LIB_QUEST_SOLVED,([
+             E_OBJECT: ME,
+             E_PLNAME: getuid(ME),
+             E_ENVIRONMENT: environment(),
+             E_QUESTNAME: questname,
+             E_QP_GRANTED: quest[0] ]) );
+
+  if (message && message!="") {
+    if (message!="__silent__") {
+      message=implode(explode(message,"@@name@@"),
+          capitalize(query_real_name()));
+    }
+    else {
+      message="";
+    }
+  }
+  else
+    message=capitalize(query_real_name())
+      +" hat gerade ein Abenteuer bestanden: "+ questname+"\n";
+  if(message!="")
+    catch(QM->Channel(message);publish);
+  catch(QM->SendMail(questname, quest, ME);publish);
+  return OK;
+}
+
+int DeleteQuest(string questname) {
+  // Quest ist nicht gesetzt
+  if(!QueryQuest(questname))
+    return DQ_NOT_SET;
+
+  mixed *quest = QM->QueryQuest(questname);
+  // Questname ungueltig
+  if (!quest||!pointerp(quest)||quest==({}))
+    return DQ_KEY_INVALID;
+  // Unbefugter Zugriff
+  if (!IS_ARCH(this_interactive()))
+    return DQ_ILLEGAL_OBJ;
+  AddExp(-quest[1]);
+  m_delete(quests, questname);
+  force_save();
+  return OK;
+}
+
+int QueryQuest(string questname) {
+  int dummy;
+
+  // Gaeste haben keine Quests.
+  if( sscanf( getuid(), "gast%d", dummy ) == 1 )
+    return QQ_GUEST;
+  // Uebergebener Parameter "questname" ungueltig oder leer?
+  if(!questname || !stringp(questname) || questname == "")
+    return QQ_KEY_INVALID;
+  // Questname ist tatsaechlich in der Questliste enthalten? Alles klar!
+  if ( member(quests, questname) )
+    return OK;
+  // Ansonsten war der Name wohl ungueltig.
+  return QQ_KEY_INVALID;
+}
+
+int ModifyQuestTime(string qname, int when) {
+  if ( process_call() )
+    return -1;
+
+  // Nur EM+ oder der Tagebuchmaster duerfen die Werte aendern.
+  if ( !IS_ARCH(this_interactive()) &&
+    load_name(previous_object())!="/d/wald/leusel/quest/objs/tagebuch-master")
+    return -1;
+
+  // Questliste ist unerwartet kein Mapping.
+  if ( !mappingp(quests) )
+    return -2;
+
+  // Kein Questname angegeben, oder Spieler hat diese Quest ueberhaupt nicht
+  // geloest.
+  if ( !stringp(qname) || !member(quests, qname) )
+    return -3;
+
+  // Der Tagebuchmaster setzt Eintraege ggf. auf 0, wenn er keine Daten
+  // findet, und EM+ wollen Eintraege auf -1 setzen koennen, um das Einlesen
+  // der Daten noch einmal zu ermoeglichen, d.h. diese Werte sind zusaetzlich
+  // zu gueltigen Zeitwerten erlaubt.
+  if ( !intp(when) || when < -1 || when > time() )
+    return -4;
+
+  // Neuen Wert eintragen.
+  quests[qname,1] = when;
+  return 1;
+}
+
+int QueryQuestTime(string qname) {
+  return quests[qname,1];
+}
+
+// Konvertiert Datenstruktur von quests in ein Mapping mit folgendem Aufbau:
+// quests = ([ "questname" : Abenteuerpunkte; Zeit_des_Questabschlusses, ])
+protected void updates_after_restore(mixed newflag) {
+  // Ganz frischer Spieler? Dann keine Behandlung erforderlich.
+  if ( newflag )
+    return;
+  // Wenn die Questliste noch kein Mapping ist, Konvertierung anstossen.
+  if ( !mappingp(quests) ) {
+    // Wenn noch keine Quests eingetragen sind, Leermapping eintragen.
+    if ( !sizeof(quests) ) {
+      quests = ([:2]);
+      return;
+    }
+    // Vorsichtshalber Leereintraege rausziehen.
+    quests -= ({({})});
+    // Liste der Questzeiten aus dem Spieler auslesen. Wenn nicht vorhanden,
+    // Array mit -1-Elementen passender Laenge erzeugen.
+    // -1 an allen Stellen eintragen, wo bisher keine Daten vorliegen.
+    // Diese werden dann vom Questtagebuch durch die Daten ersetzt.
+    int *qtimes = QueryProp("questtime")||allocate(sizeof(quests), -1);
+    // Falls die Questliste laenger ist als die Zeitenliste, wird letztere
+    // um die fehlende Laenge in Form von -1-eintraegen ergaenzt, unter der
+    // Annahme, dass die fehlenden Eintraege bisher lediglich nicht
+    // eingelesen wurden. Im umgekehrten Fall werden alle Zeiten verworfen,
+    // da dieser Fall eintritt, wenn einem Spieler eine Quest ausgetragen
+    // wurde und die Listen in der Vergangenheit nicht synchron gehalten
+    // wurden.
+    if ( sizeof(qtimes) < sizeof(quests) ) {
+      qtimes += allocate(sizeof(quests)-sizeof(qtimes), -1);
+    }
+    if ( sizeof(qtimes) > sizeof(quests) ) {
+      qtimes = allocate(sizeof(quests), -1);
+    }
+    // Questdaten und Questzeiten zusammenpferchen. Ergibt folg. Mapping:
+    // temp = ([ ({Questname1, QP1}) : Questzeit, ... ])
+    mapping temp = mkmapping(quests, qtimes);
+    quests = m_allocate(sizeof(quests),2);
+    foreach(mixed qdata, int qtime : temp) {
+      quests += ([ qdata[0] : qdata[1]; qtime ]);
+    }
+    if (QueryProp("questtime")) {
+      SetProp("questtime",0);
+      Set("questtime", SAVE, F_MODE_AD);
+    }
+  }
+}
+
+// **** local property methods
+static int _query_questpoints() {
+  int qp;
+
+  if ( !mappingp(quests) || !sizeof(quests) ) {
+    return 0;
+  }
+
+  closure qqp = symbol_function("QueryQuestPoints", QM);
+  // Die aktuell gueltigen Abenteuerpunkte aus dem Questmaster holen und
+  // die Questliste damit aktualisieren. qp wird als Referenz mit uebergeben
+  // damit das Additionsergebnis auch nach dem Durchlauf ausserhalb der
+  // Closure verfuegbar ist.
+  // Falls Abenteuerpunkte < 0 existieren, wird die entsprechende Quest
+  // aus der Liste ausgetragen.
+  walk_mapping(quests,
+    function void (string qname, int qpoints, int qtime, int sum)
+    {
+      qpoints = funcall(qqp, qname);
+      if (qpoints<0)
+        m_delete(quests,qname);
+      else
+        sum += qpoints;
+    }, &qp);
+
+  Set(P_QP, qp);
+  return qp;
+}
+
+static mixed _query_quests() {
+  return copy(quests);
+}
diff --git a/std/player/reputation.c b/std/player/reputation.c
new file mode 100644
index 0000000..9fb3d17
--- /dev/null
+++ b/std/player/reputation.c
@@ -0,0 +1,73 @@
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <wizlevels.h>
+#include <reputation.h>
+
+private mapping reputations = ([ ]);
+
+/*
+ * Argumente:
+ * repid = Reputation-ID im Repmaster
+ * value = Wert um den die Reputation geaendert werden soll,
+ *         positiv oder negativ
+ * silent = Keine Std-Meldung ausgeben
+ *
+ * Return:
+ * REP_RET_WRONGARGS = Falsche Argumente
+ * REP_RET_INACTIVE = Rep inaktiv (kann derzeit nicht geaendert werden)
+ * REP_RET_INVALIDUID = Unzulaessie UID
+ * REP_RET_ALREADYMAX = Rep bereits maximum / minimum
+ * REP_RET_INVALIDREP = Reputation nicht vorhanden
+ *
+ * REP_RET_SUCCESS = Reputation wurde veraendert
+ * REP_RET_SUCCESSCUT = Reputation wurde auf Min / Max veraendert
+ */
+public varargs int ChangeReputation(string repid, int value, 
+                                          int silent) {
+  string uid, changemsg; int newval; mapping rep;
+  
+  if(!intp(value) || !value || !stringp(repid) || !sizeof(repid)) 
+    return REP_RET_WRONGARGS;
+  if(!mappingp(rep = REPMASTER->GetReputationData(repid)))
+    return REP_RET_INVALIDREP;
+  if(!(rep["flags"] & REP_FLAG_ACTIVE))
+    return REP_RET_INACTIVE;
+  if(REPMASTER->CheckValidUid(repid, previous_object()) < 1)
+    return REP_RET_INVALIDUID;
+  if(reputations[repid] >= REP_MAXIMUM || reputations[repid] <= REP_MINIMUM) 
+    return REP_RET_ALREADYMAX;
+
+  if(reputations[repid] + value > REP_MAXIMUM) 
+    newval = reputations[repid] + value - REP_MAXIMUM;
+  else if(reputations[repid] - value < REP_MINIMUM)
+    newval = reputations[repid] + value + REP_MINIMUM;
+
+  if(!silent &&
+     stringp(changemsg = REPMASTER->GetDefaultChangeMsg(repid, 
+       newval || value)))
+    tell_object(this_object(), changemsg);
+
+  reputations[repid] += newval || value;
+
+  return newval ? REP_RET_SUCCESSCUT : REP_RET_SUCCESS;
+}
+
+/*
+ * Argumente:
+ * repid = Reputation-ID im Repmaster
+ *
+ * Return:
+ * 0 = Reputation noch nicht veraendert / enthalten 
+ * !0 = Reputationswert
+ */
+public int GetReputation(string repid) { return reputations[repid]; }
+
+/*
+ * Return:
+ * Mappingkopie aller gespeicherten Reputationswert
+ */
+public mapping GetReputations() { return copy(reputations); }
diff --git a/std/player/restrictions.c b/std/player/restrictions.c
new file mode 100644
index 0000000..d71e14c
--- /dev/null
+++ b/std/player/restrictions.c
@@ -0,0 +1,130 @@
+// MorgenGrauen MUDlib
+//
+// player/restrictions.c -- container aspect of players
+//
+// $Id: restrictions.c 9020 2015-01-10 21:49:41Z Zesstra $
+
+// This is a simple container to put objects in. It defines all functions
+// which are necessary to describe an object which can be filled with
+// other things.
+//
+// It will support restrictions for volume, weight etc.
+//
+// The following properties are defined:
+// P_MAX_WEIGHT - maximum weight which container can carry
+// P_WEIGHT_CONTENTS - current contents
+// P_WEIGHT - builtin property: read->total weight, write->own weight
+//
+// Functions for manipulation of weight
+// MayAddWeight(weight) - Can <weight> be inserted?
+// AddWeight(weight) - Add an amount of <weight>
+//
+// IMPORTANT: unit should be interpreted as grams (g).
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/container/restrictions";
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <hook.h>
+#include <living/skills.h>
+#include <attributes.h>
+#undef NEED_PROTOTYPES
+#include <properties.h>
+#include <wizlevels.h>
+#include <container.h>
+#include <defines.h>
+#include <new_skills.h>
+
+//Liste von Objekten, in denen InsertNotify() gerufen wird, wenn etwas in den
+//Spieler bewegt wurde.
+nosave object *InsertHooks=({});
+
+// local properties prototypes
+static int _query_max_weight();
+static mixed _set_frog(mixed arg);
+
+void create()
+{
+  ::create();
+
+  Set(P_MAX_WEIGHT, NOSETMETHOD, F_SET_METHOD);
+  Set(P_MAX_WEIGHT, SECURED, F_MODE);
+  offerHook(H_HOOK_INSERT, 1);
+}
+
+// **** local property methods
+static int _query_max_weight() {
+  int str,val;
+  mixed ski;
+  
+  if (QueryProp(P_GHOST) && !IS_WIZARD(ME))
+    return 0;
+  str=QueryAttribute(A_STR);
+  ski = UseSkill(SK_CARRY, ([SI_SKILLARG : str ]));
+
+  if (!intp(ski))
+    ski = 0;
+  
+  if (str<0) {
+    val=9200+str*160+(int)ski;
+    if (val<3000) val=3000;
+    return val;
+  }
+  val = 9200+str*800+(int)ski;
+  if (val<3000)
+    val = 3000;
+  return val;
+}
+
+static mixed _set_frog(mixed arg) {
+  mixed res;
+  
+  res=Set(P_FROG,arg);
+  if (res)
+    SetProp(P_ATTRIBUTES_MODIFIER,({"#frosch",([A_STR:-30])}));
+  else
+    SetProp(P_ATTRIBUTES_MODIFIER,({"#frosch",0 }));
+  return res;
+}
+
+public void NotifyInsert(object ob, object oldenv)
+{
+  ::NotifyInsert(ob, oldenv);
+  // Alle Listener im neuen Hooksystem vom InsertHook informieren
+  HookFlow(H_HOOK_INSERT, ob);
+  // Alle Listener im alten InsertHook informieren
+  if (sizeof(InsertHooks))
+  {
+    foreach(object h: &InsertHooks)
+    {
+      if (h && environment(h) == ME)
+        h->InsertNotify(ob);
+      else
+        h=0;
+    }
+    InsertHooks-=({0}); // genullte Elemente loeschen
+  }
+}
+
+void AddInsertHook(object ob)
+{
+  if (member(InsertHooks,ob)!=-1 || environment(ob)!=this_object())
+    return;
+  InsertHooks+=({ob});
+}
+
+void RemoveInsertHook(object ob)
+{
+  InsertHooks-=({ob});
+}
+
+object *QueryInsertHooks()
+{
+  return InsertHooks;
+}
+
diff --git a/std/player/shadows/ark_hunger_shadow.c b/std/player/shadows/ark_hunger_shadow.c
new file mode 100644
index 0000000..42d6dff
--- /dev/null
+++ b/std/player/shadows/ark_hunger_shadow.c
@@ -0,0 +1,54 @@
+/* Der Shadow fuer den Hungerfluch */
+/* /d/ebene/ark/wolf/obj/hunger.c  */
+/* Shadow: /std/player/shadows/ark_hunger_shadow.c */
+/* Original: /d/ebene/ark/wolf/obj/hunger_obj.c */
+
+#pragma strong_types,save_types
+
+#include <defines.h>
+#include <properties.h>
+
+#define BS break_string
+
+object spieler;
+
+void Setzen(object sp)
+{
+ if (!objectp(sp) || !interactive(sp)) return destruct(this_object());
+ spieler=sp;
+ if (!shadow(sp,1)) destruct(this_object());
+}
+
+int _query_food() { return 0; }
+
+int _query_max_food() { return 0; }
+
+int eat_food(int strength, int testonly)
+{
+ if (strength==0) return 1;
+ if (strength>0)
+ {
+  strength=0;
+  if (spieler)
+  {
+    tell_object(spieler, BS("Du hast zwar einen tierischen Hunger, doch irgendwas sagt Dir, dass Du das, was "+
+                            "Du gerade essen willst, nicht mehr in Deinen Magen bekommst. Das ist ziemlich "+
+                            "gefaehrlich, hoffentlich verhungerst Du nicht !", 78));
+    return 0;
+  }
+ }
+ else
+ {
+  if (spieler)
+  {
+   tell_object(spieler, BS("Dein Hunger vertieft sich so nur, Du leidest Hoellenqualen.", 78));
+   spieler->reduce_hit_points(strength);
+  }
+  return 0;
+ }
+}
+
+void Loeschen() {
+    unshadow();
+    destruct(this_object());
+}
diff --git a/std/player/shadows/block_shadow.c b/std/player/shadows/block_shadow.c
new file mode 100644
index 0000000..f7a4333
--- /dev/null
+++ b/std/player/shadows/block_shadow.c
@@ -0,0 +1,47 @@
+#pragma strong_types,save_types
+
+#include <defines.h>
+#include <moving.h>
+#include <properties.h>
+#include <language.h>
+
+private nosave object pl;
+
+void create()
+{
+  if( IS_BLUE(ME) ) return;
+  shadow( PL, 1);
+  pl = PL;
+}
+
+int
+AddExp(int ep)
+{
+  object block;
+  int diff, lim;
+
+  block = present("\n block", pl);
+  lim = 30 + random(10);
+
+  if ( ep > lim &&				      // Mindestwert
+       previous_object() &&
+       ( previous_object() == pl ||		      // zB. GiveQuest()
+	 ( living(previous_object()) && 	      // Oder NPCs
+	  !query_once_interactive(previous_object())
+	 )
+       )
+     )
+  {
+    diff = block->Gutschreiben(ep-lim);
+    return pl->AddExp(lim+diff);
+  }
+  return pl->AddExp(ep);
+}
+
+void
+SeherHatGenug()
+{
+  unshadow();
+  destruct(this_object());
+}
+
diff --git a/std/player/shadows/morph_shadow.c b/std/player/shadows/morph_shadow.c
new file mode 100644
index 0000000..afc97c4
--- /dev/null
+++ b/std/player/shadows/morph_shadow.c
@@ -0,0 +1,598 @@
+/* -*- lpc -*- */
+//--------------------------------------------------------------------------
+//
+//   morph.c
+//
+//   (c) Troy (troy@mg.mud.de)
+//   Kopieren, Veraendern oder Weitergabe: na klar, immer zu, je schlimmer 
+//   um so besser
+//
+//   Objekt erstellt: 14.08.01, Troy
+//
+//   Dieser shadow implementiert generische Verwandlungen. Im Gegensatz oder
+//   in Ergänzung zum Tarnhelm sind diese nicht auf die Beschreibung
+//   beschränkt, sondern schlagen sich auch in anderen Properties nieder.
+//
+//--------------------------------------------------------------------------
+
+#include <moving.h>
+#include <properties.h>
+#include <wizlevels.h>
+
+//--------------------------------------------------------------------------
+
+#pragma strong_types,save_types
+
+//--------------------------------------------------------------------------
+
+varargs int remove( int silent );
+
+//--------------------------------------------------------------------------
+//
+//   Property-Einstellungen je nach Rasse
+//
+//--------------------------------------------------------------------------
+
+private mapping morph_properties;
+
+private object pl; // der schattierte Spieler
+
+//--------------------------------------------------------------------------
+//
+//   start_shadow( Spieler, Properties )
+//
+//   Startet das Shadowing von Spieler. Properties ist ein Mapping mit
+//   den zu ändernden Properties. Dort nicht vorhandene Properties werden
+//   zum Spieler durchgereicht. Es werden dort entweder einzelne Werte
+//   erwartet (Beispiel: ([ P_GENDER: MALE ])), die dann für alle Rassen
+//   gelten, oder closures, die dann ausgeführt werden unter Übergabe der
+//   Spielerrasse als Parameter oder aber Mappings mit den Rassennamen
+//   (Beispiel: ([ P_GENDER: ([ "Mensch": NEUTER, "Elf": FEMALE,
+//   "Zwerg": MALE, ... ]) ])). Ist eine Rasse in dem Rassenmapping nicht
+//   vorhanden, so wird das Property zum Spieler durchgereicht. Speziell
+//   behandelt werden P_IDS (siehe _query_ids()) und P_NAME (siehe
+//   _query_name()). 
+//
+//--------------------------------------------------------------------------
+int start_shadow( object _pl, mapping preset )
+{
+  if ( !clonep( this_object() ) )
+    return 1;
+  if ( !_pl || !query_once_interactive( _pl ) )
+    return remove();
+  pl = _pl;
+  morph_properties = deep_copy( preset );
+  shadow( pl, 1 );
+  return 1;
+}
+
+//--------------------------------------------------------------------------
+//
+//   stop_shadow()
+//
+//   Beendet das Shadowing und zerstört dieses Objekt
+//
+//--------------------------------------------------------------------------
+int stop_shadow( /* void */ )
+{
+  if ( !clonep( this_object() ) )
+    return 0;
+  unshadow();
+  return remove();
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_property( Property )
+//
+//   Generische Property-Maskierung. liefert aus morph_properties den zur
+//   Rasse des Trägers passenden Eintrag.
+//
+//--------------------------------------------------------------------------
+nomask static mixed _query_property( string prop )
+{
+  string race;
+
+  // Rasse holen.
+  if ( IS_LEARNER(pl) )
+    race = "Magier";
+  else // _query_race() ist notwendig, um closures zu umgehen.
+    race = pl->_query_race();
+
+  if ( member( morph_properties, prop ) == 0 )
+    return pl->Query( prop );
+  if ( closurep( morph_properties[ prop ] ) )
+    return funcall( morph_properties[ prop ], race );
+  if ( mappingp( morph_properties[ prop ] ) )
+  {
+    if ( member( morph_properties[ prop ], race ) == 0 )
+      return pl->Query( prop );
+    if ( closurep( morph_properties[ prop ][ race ] ) )
+      return funcall( morph_properties[ prop ][ race ] );
+
+    return morph_properties[ prop ][ race ];
+  }
+  return morph_properties[ prop ];
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_article()
+//
+//   Property-Maskierung für P_ARTICLE
+//
+//--------------------------------------------------------------------------
+int _query_article( /* void */ )
+{
+  return (int)_query_property( P_ARTICLE );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_average_size()
+//
+//   Property-Maskierung für P_AVERAGE_SIZE
+//
+//--------------------------------------------------------------------------
+int _query_average_size( /* void */ )
+{
+  return (int)_query_property( P_AVERAGE_SIZE );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_average_weight()
+//
+//   Property-Maskierung für P_AVERAGE_WEIGHT
+//
+//--------------------------------------------------------------------------
+int _query_average_weight( /* void */ )
+{
+  return (int)_query_property( P_AVERAGE_WEIGHT );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_body()
+//
+//   Property-Maskierung für P_BODY
+//
+//--------------------------------------------------------------------------
+int _query_body( /* void */ )
+{
+  return (int)_query_property( P_BODY );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_gender()
+//
+//   Property-Maskierung für P_GENDER
+//
+//--------------------------------------------------------------------------
+int _query_gender( /* void */ )
+{
+  return (int)_query_property( P_GENDER );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_hands()
+//
+//   Property-Maskierung für P_HANDS
+//
+//--------------------------------------------------------------------------
+mixed _query_hands( /* void */ )
+{
+  return _query_property( P_HANDS );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_ids()
+//
+//   Property-Maskierung für P_IDS - Nicht-Standard, da je nach Ursprungs-
+//   geschlecht des Spielers zusätzliche ids fällig werden. Ablauf: Es gibt
+//   5 Schritte, bei deren jeweiligem Versagen die ids des Spielers durch-
+//   gereicht werden:
+//   1. P_IDS ist im property-mapping vorhanden
+//   2. a) es ist ein string oder ein array: es wird an die ids des Spielers
+//         angehängt und zurückgegeben.
+//      b) es ist eine closure. Diese wird ausgeführt unter Übergabe der
+//         Spieler-ids, der Rasse des Spielers und der Geschlechter
+//         (dieses Objekts und des Spielers). Der Ergebniswert der closure
+//         wird direkt zurückgegeben.
+//      c) es ist ein mapping. Hier nehmen wir nun das übliche Rassennamen-
+//         mapping an -> 3.)
+//   3. Für die Rasse des Spielers wird ein Eintrag gesucht
+//      a) er ist ein string oder ein array: er wird an die ids des Spielers
+//         angehängt und zurückgegeben.
+//      b) er ist eine closure. Diese wird ausgeführt unter Übergabe der
+//         Spieler-ids, der Rasse des Spielers und der Geschlechter
+//         (dieses Objekts und des Spielers). Der Ergebniswert der closure
+//         wird direkt zurückgegeben.
+//      c) er ist ein mapping. Es wird angenommen, dass je Geschlecht DIESES
+//         Objekts ein Eintrag vorhanden ist. -> 4.)
+//   4. Für das Geschlecht dieses Objekts wird ein Eintrag gesucht
+//      a) er ist ein string oder ein array: er wird an die ids des Spielers
+//         angehängt und zurückgegeben.
+//      b) er ist eine closure. Diese wird ausgeführt unter Übergabe der
+//         Spieler-ids, der Rasse des Spielers und der Geschlechter
+//         (dieses Objekts und des Spielers). Der Ergebniswert der closure
+//         wird direkt zurückgegeben.
+//      c) er ist ein mapping. Es wird angenommen, dass je Geschlecht DES
+//         Spielers ein Eintrag vorhanden ist. -> 5.)
+//   5. Für das Geschlecht des Spielers wird ein Eintrag gesucht
+//      a) er ist ein string oder ein array: er wird an die ids des Spielers
+//         angehängt und zurückgegeben.
+//      b) er ist eine closure. Diese wird ausgeführt unter Übergabe der
+//         Spieler-ids, der Rasse des Spielers und der Geschlechter
+//         (dieses Objekts und des Spielers). Der Ergebniswert der closure
+//         wird direkt zurückgegeben.
+//
+//--------------------------------------------------------------------------
+mixed _query_ids( /* void */ )
+{
+  string race;
+  mixed ids;
+  int gender, sgender;
+
+  // Test 1.
+  ids = pl->Query( P_IDS );
+  if ( member( morph_properties, P_IDS ) == 0 )
+    return ids;
+
+  // Rasse holen.
+  if ( IS_LEARNER(pl) )
+    race = "Magier";
+  else // _query_race() ist notwendig, um closures zu umgehen.
+    race = pl->_query_race();
+
+  // Geschlechter holen
+  gender = _query_gender();
+  sgender = pl->Query( P_GENDER );
+
+  // Test 2.
+  // string? Dann einfach den normalen ids dazu, genauso mit array
+  if ( stringp( morph_properties[ P_IDS ] ) )
+    return ids + ({ morph_properties[ P_IDS ] });
+  if ( pointerp( morph_properties[ P_IDS ] ) )
+    return ids + morph_properties[ P_IDS ];
+  if ( closurep( morph_properties[ P_IDS ] ) )
+    return funcall( morph_properties[ P_IDS ], ids, race, gender, sgender );
+  // falls kein mapping, dann raus
+  if ( !mappingp( morph_properties[ P_IDS ] ) )
+    return ids;
+
+  // Test 3.
+  if ( member( morph_properties[ P_IDS ], race ) == 0 )
+    return ids;
+  if ( stringp( morph_properties[ P_IDS ][ race ] ) )
+    return ids + ({ morph_properties[ P_IDS ][ race ] });
+  if ( pointerp( morph_properties[ P_IDS ][ race ] ) )
+    return ids + morph_properties[ P_IDS ][ race ];
+  if ( closurep( morph_properties[ P_IDS ][ race ] ) )
+    return funcall( morph_properties[ P_IDS ][ race ], ids, race, gender, sgender );
+  // falls kein mapping, dann raus
+  if ( !mappingp( morph_properties[ P_IDS ][ race ] ) )
+    return ids;
+
+  // Test 4.
+  if ( member( morph_properties[ P_IDS ][ race ], gender ) == 0 )
+    return ids;
+  if ( stringp( morph_properties[ P_IDS ][ race ][ gender ] ) )
+    return ids + ({ morph_properties[ P_IDS ][ race ][ gender ] });
+  if ( pointerp( morph_properties[ P_IDS ][ race ][ gender ] ) )
+    return ids + morph_properties[ P_IDS ][ race ][ gender ];
+  if ( closurep( morph_properties[ P_IDS ][ race ][ gender ] ) )
+    return funcall( morph_properties[ P_IDS ][ race ][ gender ],
+		    ids, race, gender, sgender );
+  // falls kein mapping, dann raus
+  if ( !mappingp( morph_properties[ P_IDS ][ race ][ gender ] ) )
+    return ids;
+
+  // Test 5.
+  if ( member( morph_properties[ P_IDS ][ race ][ gender ], sgender ) == 0 )
+    return ids;
+  if ( stringp( morph_properties[ P_IDS ][ race ][ gender ][ sgender ] ) )
+    return ids + ({ morph_properties[ P_IDS ][ race ][ gender ][ sgender ] });
+  if ( pointerp( morph_properties[ P_IDS ][ race ][ gender ][ sgender ] ) )
+    return ids + morph_properties[ P_IDS ][ race ][ gender ][ sgender ];
+  if ( closurep( morph_properties[ P_IDS ][ race ][ gender ][ sgender ] ) )
+    return funcall( morph_properties[ P_IDS ][ race ][ gender ][ sgender ],
+		    ids, race, gender, sgender );
+
+  return ids;
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_is_morphed()
+//
+//   Property-Methode für "is_morphed"
+//
+//--------------------------------------------------------------------------
+int _query_is_morphed( /* void */ )
+{
+  return 1;
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_max_hands()
+//
+//   Property-Maskierung für P_MAX_HANDS
+//
+//--------------------------------------------------------------------------
+int _query_max_hands( /* void */ )
+{
+  return (int)_query_property( P_MAX_HANDS );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_mmsgin()
+//
+//   Property-Maskierung für P_MMSGIN
+//
+//--------------------------------------------------------------------------
+string _query_mmsgin( /* void */ )
+{
+  return (string)(_query_property( P_MMSGIN ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_mmsgout()
+//
+//   Property-Maskierung für P_MMSGOUT
+//
+//--------------------------------------------------------------------------
+string _query_mmsgout( /* void */ )
+{
+  return (string)(_query_property( P_MMSGOUT ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_msgin()
+//
+//   Property-Maskierung für P_MSGIN
+//
+//--------------------------------------------------------------------------
+string _query_msgin( /* void */ )
+{
+  return (string)(_query_property( P_MSGIN ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_msgout()
+//
+//   Property-Maskierung für P_MSGOUT
+//
+//--------------------------------------------------------------------------
+string _query_msgout( /* void */ )
+{
+  return (string)(_query_property( P_MSGOUT ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_name()
+//
+//   Property-Methode für P_NAME. Leider ist die player-shell so grottig,
+//   dass überall angenommen wird, QueryProp(P_NAME) liefere einen String :-|
+//   Vollständiges Property daher unter _query_name_full().
+//
+//--------------------------------------------------------------------------
+string _query_name( /* void */ )
+{
+  mixed prop;
+  prop = _query_property( P_NAME );
+  if ( stringp( prop ) )
+    return sprintf( prop, pl->Query( P_NAME ) );
+  if ( pointerp( prop ) )
+    return map( prop,
+		      lambda( ({ 'el, 's }),
+			      ({#'sprintf, 'el, 's}) ),
+		      pl->Query( P_NAME ) )[ WER ];
+  return pl->Query( P_NAME );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_name_full()
+//
+//   Property-Methode für "name_full".
+//
+//--------------------------------------------------------------------------
+mixed _query_name_full( /* void */ )
+{
+  mixed prop;
+  prop = _query_property( P_NAME );
+  if ( stringp( prop ) )
+    return sprintf( prop, pl->Query( P_NAME ) );
+  if ( pointerp( prop ) )
+    return map( prop,
+		      lambda( ({ 'el, 's }),
+			      ({#'sprintf, 'el, 's}) ),
+		      pl->Query( P_NAME ) );
+  return pl->Query( P_NAME );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_presay()
+//
+//   Property-Maskierung für P_PRESAY
+//
+//--------------------------------------------------------------------------
+string _query_presay( /* void */ )
+{
+  return (string)(_query_property( P_PRESAY ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_race()
+//
+//   Property-Maskierung für P_RACE
+//
+//--------------------------------------------------------------------------
+string _query_race( /* void */ )
+{
+  return (string)(_query_property( P_RACE ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_racestring()
+//
+//   Property-Maskierung für P_RACESTRING
+//
+//--------------------------------------------------------------------------
+string* _query_racestring( /* void */ )
+{
+  return (string*)_query_property( P_RACESTRING );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_size()
+//
+//   Property-Maskierung für P_SIZE
+//
+//--------------------------------------------------------------------------
+int _query_size( /* void */ )
+{
+  return (int)_query_property( P_SIZE );
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_title()
+//
+//   Property-Maskierung für P_TITLE
+//
+//--------------------------------------------------------------------------
+string _query_title( /* void */ )
+{
+  return (string)(_query_property( P_TITLE ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   _query_weight()
+//
+//   Property-Maskierung für P_WEIGHT
+//
+//--------------------------------------------------------------------------
+int _query_weight( /* void */ )
+{
+  return (int)(_query_property( P_WEIGHT ) || "");
+}
+
+//--------------------------------------------------------------------------
+//
+//   id( Text, Level)
+//
+//   Die Identifizierung spinnt mit P_NAME-Arrays
+//
+//--------------------------------------------------------------------------
+varargs int id( string str, int lvl )
+{
+  string plname;
+
+  if ( pl->QueryProp( P_GHOST ) )
+    if ( str == "geist" )
+      return 1;
+    else if ( ( sscanf( str, "geist von %s", plname ) == 1 ) &&
+	      pl->id( plname ) )
+      return 1;
+    else if ( ( sscanf( str, "geist %s", plname ) == 1 ) &&
+	      pl->id( plname ) )
+      return 1;
+
+  return pl->id( str, lvl );
+}
+
+//--------------------------------------------------------------------------
+//
+//   long()
+//
+//   Die Langbeschreibung im Spieler hadert mit dem Geschlecht NEUTER...
+//
+//--------------------------------------------------------------------------
+varargs string long( /* void */ )
+{
+  string slong;
+  slong = pl->long();
+
+  if ( _query_gender() == NEUTER )
+  {
+    string *along;
+    int i;
+
+    // alle Er-s und er-s suchen...
+    along = regexplode( slong, "\\<[Ee]r\\>" );
+    // ... und das r durch ein s ersetzen.
+    for ( i = 1 ; i < sizeof( along ) ; i += 2 )
+      along[ i ][ 1 ] = 's';
+    slong = implode( along, "" );
+  }
+
+  return slong;
+}
+
+//--------------------------------------------------------------------------
+//
+//   short()
+//
+//   Die Kurzbeschreibung im Spieler hat ein Problem mit P_NAME-Arrays
+//
+//--------------------------------------------------------------------------
+string short( /* void */ )
+{
+  mixed names;
+  string answer;
+  string title;
+
+  if ( pl->QueryProp( P_INVIS ) )
+    if ( interactive( previous_object() ) &&
+	 IS_LEARNING( previous_object() ) )
+      return "(" + pl->Query( P_NAME ) + ") \n";
+    else
+      return (string)0;
+
+  names = _query_name_full();
+  if ( stringp( names ) )
+    names = ({ names, names, names, names });
+
+  if ( pl->QueryProp( P_GHOST ) )
+    answer = "Der Geist " + pl->QueryArticle( WESSEN, 0 ) + names[ WESSEN ];
+  else
+    answer = pl->QueryArticle( WER, 0 ) + names[ WER ];
+  if ( ( title = pl->QueryProp( P_TITLE ) ) &&
+       ( title != "" ) )
+     answer += " " + title;
+  if ( !interactive( pl ) )
+     answer += " (netztot)";
+  return capitalize( answer ) + ".\n";
+}
+
+//--------------------------------------------------------------------------
+//
+//   remove( Schnauze )
+//
+//   aufräumen
+//
+//--------------------------------------------------------------------------
+varargs int remove( int silent )
+{
+  destruct( this_object() );
+  return 1;
+}
+
+//--------------------------------------------------------------------------
+//   -- ENDE --
diff --git a/std/player/shadows/pony.c b/std/player/shadows/pony.c
new file mode 100644
index 0000000..8d3de8d
--- /dev/null
+++ b/std/player/shadows/pony.c
@@ -0,0 +1,59 @@
+#pragma strong_types,save_types
+
+#include <defines.h>
+#include <moving.h>
+#include <properties.h>
+#include <language.h>
+
+// 
+// Da der Shadow sich nicht im Player befindet, wird ein
+// Zusatzobjekt benoetigt.
+// 7.10.97 - Die Hook sollte in den Verzeichnissen der Benniequest
+//           zu finden sein. - Rumata
+
+#define HOOK "d/wald/kassandra/bennie/obj/pony_hook"
+
+static object pl;
+
+void create()
+{
+ if( IS_BLUE(ME) ) return;
+  shadow( PL, 1);
+ pl=PL;
+ //tell_object(this_player(),"Du besteigst das Pony samt Schatten.\n");
+  clone_object( HOOK ); // Steuerung des shadows ueber die hook
+}
+
+string _query_title()
+{ 
+ if( pl->QueryProp(P_GENDER) == FEMALE )
+     return "die Ponyreiterin";
+ return "der Ponyreiter";
+}
+
+string _query_msgin() { return "reitet herein"; }
+string _query_msgout() { return "reitet"; }
+
+varargs int move(mixed dest, int method, string dir, string textout,string textin)
+{
+ if( !(method & M_NOCHECK) && dest->QueryProp(P_INDOORS) )
+ {
+  write( "Das Pony weigert sich, dorthin zu traben.\n" );
+  return 1;
+ }
+ return pl->move( dest, method, dir, textout, textin );
+}
+
+int _inventory(string str)
+{
+ if( !environment() || set_light(0)>0 )
+  write( "Du sitzt auf einem Pony.\n" );
+ return pl->_inventory(str);
+}
+
+void absteigen() {
+  unshadow();
+  destruct(ME);
+}
+
+int QueryReiter() { return 1; }
diff --git a/std/player/shadows/sizetest.c b/std/player/shadows/sizetest.c
new file mode 100644
index 0000000..1268911
--- /dev/null
+++ b/std/player/shadows/sizetest.c
@@ -0,0 +1,57 @@
+#include <defines.h>
+#include <moving.h>
+#include <properties.h>
+#include <language.h>
+
+#define SPIELER "nefis"
+#define DEBUG(x)  if (find_player("zesstra"))\
+            tell_object(find_player("zesstra"),\
+		                      "Nefis P_SIZE: "+x+"\n")
+#define LOG(x) log_file("zesstra/P_SIZE.log", x)
+
+private nosave object pl;
+
+void create()
+{
+  if (!clonep() ) return;
+  pl = find_player(SPIELER) || find_netdead(SPIELER);
+  if (!objectp(pl)) {
+      DEBUG("Playerobjekt nicht gefunden.");
+      destruct(ME);
+      return;
+  }
+  shadow( pl, 1);
+  DEBUG(sprintf("%O shadowed by: %O\n", pl, shadow(pl, 0)));
+}
+
+int _set_size(int sz) {
+  LOG(sprintf("[%s] Aenderung von P_SIZE in %O auf %O,\nCallerstack: %O\n\n",
+	strftime("%c",time()), pl, sz, caller_stack()));
+  return pl->Set(P_SIZE, sz, F_VALUE);
+}
+
+public varargs mixed Set( string name, mixed Value, int Type, int extern ) {
+  if (name == P_SIZE) {
+  LOG(sprintf("[%s] Aenderung von P_SIZE in %O auf %O (Typ: %O) [Set()!]"
+	",\nCallerstack: %O\n\n",
+	        strftime("%c",time()), pl, Value, Type, caller_stack()));
+  }
+  return pl->Set(name, Value, Type, extern);
+}
+
+int stop() {
+  unshadow();
+  destruct(ME);
+  return 1;
+}
+
+void reset() {
+  if (!objectp(pl)) 
+    stop();
+}
+
+int remove() {
+  stop();
+  return 1;
+}
+
diff --git a/std/player/shadows/tarnhelm_shadow.c b/std/player/shadows/tarnhelm_shadow.c
new file mode 100644
index 0000000..63ea744
--- /dev/null
+++ b/std/player/shadows/tarnhelm_shadow.c
@@ -0,0 +1,140 @@
+#pragma strong_types,save_types
+
+#include <defines.h>
+#include <properties.h>
+
+#define WEG() destruct(this_object())
+
+object player;
+int gender;
+string desc;
+
+void _tarn_turn_on(object pl,string txt,int gen)
+{
+  if (!objectp(pl)||!interactive(pl)) return WEG();
+  if (!stringp(txt)||txt=="") return WEG();
+  if (gen<0||gen>2) return WEG();
+  player=pl;
+  desc=capitalize(txt);
+  gender=gen;
+  shadow(pl,1);
+}
+
+int special_verb()
+{
+  string verb;
+  
+  verb=query_verb();
+  if (!verb||verb=="") return 0;
+  if (verb[0]=='-') return 1;
+  if (verb=="ruf"||verb=="rufe"||verb=="teile"||verb=="teil"||verb=="mruf"||
+      verb=="mrufe"||verb=="erzaehl"||verb=="erzaehle") return 1;
+  return(0); // non-void funktion, Zesstra
+}
+
+int _query_gender()
+{
+  return gender;
+}
+
+string _query_name()
+{
+  if (!special_verb())
+    return capitalize(desc);
+  return player->Query(P_NAME);
+}
+
+string _query_short()
+{
+  return capitalize(player->name());
+}
+
+string _query_long()
+{
+  string str;
+
+  str=player->name();
+  return capitalize(str)+" ist "+str+" ist "+str+".\n";
+}
+
+string* _query_ids()
+{
+  return player->Query("ids")+({lower_case(desc)});
+}
+
+string _query_race()
+{
+  return desc;
+}
+
+void _tarn_turn_off()
+{
+  unshadow();
+  destruct(this_object());
+}
+
+int _query_article()
+{
+  if (!special_verb())
+    return 1;
+  return(0); // non-void funktion, Zesstra
+}
+
+string _query_presay()
+{
+  return "";
+}
+
+string _query_title()
+{
+  return "";
+}
+
+void Defend(int dam,mixed dam_type,mixed spell,object enemy)
+{
+  object o;
+
+  if (!query_once_interactive(previous_object()))
+    player->Defend(dam, dam_type, spell, enemy);
+  else
+    previous_object()->StopHuntFor(player);
+  if ((o=present("\ntarnhelm",player)))
+        o->DoUnwear();
+  if (this_object()) destruct(this_object());
+}
+
+int Kill(object ob)
+{
+  object o;
+
+  if ((o=present("\ntarnhelm",player)))
+      o->DoUnwear();
+  if (this_object()) destruct(this_object());
+  return(0); // non-void funktion, Zesstra
+}
+
+int InsertEnemy(object ob)
+{
+  object o;
+
+  if (!query_once_interactive(ob))
+    player->InsertEnemy(ob);
+  else
+    ob->StopHuntFor(player);
+  if ((o=present("\ntarnhelm",player)))
+      o->DoUnwear();
+  if (this_object()) destruct(this_object());
+  return 0;
+}
+
+string short()
+{
+  if (old_explode(object_name(previous_object()),"#")[0]=="/obj/werliste")
+    return capitalize(geteuid(player)+" verkleidet als "+player->short());
+  return player->short();
+}
+
+string QueryDisguise()
+{
+  return desc; 
+}
diff --git a/std/player/shadows/zaubersh.c b/std/player/shadows/zaubersh.c
new file mode 100644
index 0000000..a19cecc
--- /dev/null
+++ b/std/player/shadows/zaubersh.c
@@ -0,0 +1,220 @@
+#pragma strong_types,save_types
+
+#include <properties.h>
+
+object caster;
+string*ids,name;
+int gender,article,plural;
+string lgdesc;
+string min,mout,mmin,mmout;
+mixed hands;
+
+void Initialize(object _caster,
+                string*_ids,
+                string _name,
+                int _gender,
+                int _article,
+                int _plural)
+{ if(!objectp(_caster)    ||
+     !interactive(_caster)||
+     !stringp(_name)      ||
+     _name==""            ||
+     _gender<0            ||
+     _gender>2)
+  { destruct(this_object());
+    return;
+  }
+  caster=_caster;
+  ids=_ids;
+  name=_name;
+  gender=_gender;
+  article=_article;
+  plural=_plural;
+  lgdesc=0;
+  shadow(caster,1);
+}
+
+void SetLongDesc(string txt)
+{ if(!stringp(txt)||txt=="")
+    return;
+  lgdesc=txt;
+}
+
+string* _query_ids()
+{ return caster->Query(P_IDS)+ids;
+}
+
+// nicht alle Verben mit veraendertem Aussehen
+int special_verb()
+{ string verb;
+  verb=query_verb();
+  if(!stringp(verb)||verb=="")
+    return 0;
+  if(verb[0]=='-')
+    return 1;
+  if(member(({"ruf","rufe","mruf","mrufe",
+              "teil","teile","erzaehl","erzaehle"}),verb)!=-1)
+    return 1;
+  return 0;
+}
+
+string _query_name()
+{ if(!special_verb())
+    return name;
+  return capitalize(caster->Query(P_NAME));
+}
+
+string _query_short()
+{ if(!special_verb())
+    return caster->Name();
+  return caster->Query(P_SHORT);
+}
+
+string _query_long()
+{ if(!special_verb())
+  { string str;
+    if(lgdesc)
+      return lgdesc;
+    str=caster->name();
+    return break_string(capitalize(str)+" ist "+str+" ist "+str+".",78);
+  }
+  return caster->Query(P_LONG);
+}
+
+int _query_gender()
+{ if(!special_verb())
+    return gender;
+  return caster->Query(P_GENDER);
+}
+
+int _query_article()
+{ if(!special_verb())
+    return article;
+  return caster->Query(P_ARTICLE);
+}
+
+int _query_plural()
+{ if(!special_verb())
+    return plural;
+  return caster->Query(P_PLURAL);
+}
+
+string _query_race()
+{ if(!special_verb())
+    return name;
+  return caster->Query(P_RACE);
+}
+
+varargs int remove(int silent) {
+  unshadow();
+  destruct(this_object());
+  return 1;
+}
+
+void stop_shadow()
+{ 
+  remove();
+}
+
+string _query_presay()
+{ return"";
+}
+
+string _query_title()
+{ return"";
+}
+
+string _set_msgin(string val)
+{ return min=val;
+}
+
+string _query_msgin()
+{ return min;
+}
+
+string _set_msgout(string val)
+{ return mout=val;
+}
+
+string _query_msgout()
+{ return mout;
+}
+
+string _set_mmsgin(string val)
+{ return mmin=val;
+}
+
+string _query_mmsgin()
+{ return mmin;
+}
+
+string _set_mmsgout(string val)
+{ return mmout=val;
+}
+
+string _query_mmsgout()
+{ return mmout;
+}
+
+mixed _set_hands(mixed val)
+{ return hands=val;
+}
+
+mixed _query_hands()
+{ return hands;
+}
+
+varargs int Defend(int dam,mixed dam_type,mixed spell,object enemy)
+{ object ob;
+  if(!enemy ||  // Silvana 26.1.2002
+     (!query_once_interactive(previous_object())&&
+      !query_once_interactive(enemy)))
+    return caster->Defend(dam,dam_type,spell,enemy);
+  else
+  { enemy->StopHuntFor(caster);
+    caster->StopHuntFor(enemy);
+  }
+  if(objectp(ob=present("zauberer\nshadow",caster)))
+    ob->remove();
+  if(this_object())
+    remove();
+  return 0;
+}
+
+int Kill(object enemy)
+{ object ob;
+  if(!query_once_interactive(enemy))
+    return caster->Kill(enemy);
+  if(objectp(ob=present("zauberer\nshadow",caster)))
+    ob->remove();
+  if(this_object())
+    remove();
+  return 0;
+}
+
+int InsertEnemy(object enemy)
+{ object ob;
+  if(!query_once_interactive(enemy))
+    return caster->InsertEnemy(enemy);
+  else {
+    enemy->StopHuntFor(caster);
+    caster->StopHuntFor(enemy);
+  }
+  if(objectp(ob=present("zauberer\nshadow",caster)))
+    ob->remove();
+  if(this_object())
+    remove();
+  return 0;
+}
+
+string short()
+{ if(load_name(previous_object()) == "/obj/werliste")
+    return capitalize(geteuid(caster))+" verkleidet als "+caster->short();
+  return caster->short();
+}
+
+varargs string long()
+{ if(lgdesc)
+    return lgdesc;
+  return caster->long();
+}
diff --git a/std/player/skills.c b/std/player/skills.c
new file mode 100644
index 0000000..503da97
--- /dev/null
+++ b/std/player/skills.c
@@ -0,0 +1,621 @@
+// MorgenGrauen MUDlib
+//
+// player/skills.c -- Spielerskills
+//
+// $Id: skills.c 8809 2014-05-08 19:52:48Z Zesstra $
+
+//
+// 2003-01-20: Nun Zooks Baustelle
+//
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/living/skills";
+
+#include <combat.h>
+#include <new_skills.h>
+#include <properties.h>
+#include <break_string.h>
+#include <wizlevels.h>
+
+#define NEED_PROTOTYPES
+#include <player/base.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+
+// Dieses Mapping speichert die deaktivierten Skills der einzelnen Gilden
+// Diese werden in den Gilden ueber P_GUILD_DEACTIVATED_SKILL gesetzt.
+nosave mapping deactivated_skills = ([]);
+
+// Flag fuer den Kompatibilitaetsmodus des Kampfs (Emulation von
+// 2s-Alarmzeit). Wenn != 0 speichert das Flag gleichzeitig die Zeit des
+// letzten Heartbeats. Auf diese Zeit wird der Startpunkt eines Spellfatigues
+// ggf. zurueckdatiert. (max. eine Sekunde)
+int spell_fatigue_compat_mode;
+
+// Ein create() fuer das Mapping
+
+protected void create()
+{
+  mapping act;
+
+  ::create();
+
+  // Wir holen die Gilden aus dem Gildenmaster 
+  foreach(string guild:
+      (string *)call_other(GUILDMASTER,"QueryProp",P_VALID_GUILDS))
+  {
+    if(catch(act=call_other("/gilden/"+guild,"QueryProp",
+        P_GUILD_DEACTIVATE_SKILLS); publish ))
+        log_file("WEAPON_SKILLS", sprintf ("%s: Gilde nicht ladbar: "
+              +"TP: %O, TI: %O, PO: %O, Gilde: %s\n", dtime(time()),
+              this_player(), this_interactive(), previous_object(), guild));
+    else if (act) // wenn act, ins Mapping aufnehmen.
+        deactivated_skills+=([guild:act]);
+  }
+  // keine echte Prop mehr, Save-Modus unnoetig.
+  Set(P_NEXT_SPELL_TIME,SAVE,F_MODE_AD);
+
+  Set(P_SKILLSVERSION, SAVE|SECURED, F_MODE_AS);
+}
+
+// Das Mapping kann man auch abfragen
+
+public mapping GetDeactivatedSkills()
+{
+    return copy(deactivated_skills);
+}
+
+// Funktion, die sagt ob ein ANY-Skill deaktiviert ist.
+public int is_deactivated_skill(string sname,string guild)
+{
+        if (deactivated_skills[guild])        
+                return deactivated_skills[guild][sname];
+        return 0;
+}
+
+
+// Funktion fuer die Waffenskills
+// traegt die allg. Waffenskills ein. Wird ggf. von FixSkills() gerufen.
+// (Das Eintragen bedeutet nicht, dass die aktiv sind! Aber bei Gildenwechsel
+// werden sie nicht eingetragen).
+private void set_weapon_skills() {
+
+  if (QuerySkillAbility(FIGHT(WT_SWORD))<=0)                
+      ModifySkill(FIGHT(WT_SWORD),([SI_SKILLABILITY:0]),150,"ANY");    
+  if (QuerySkillAbility(FIGHT(WT_AXE))<=0)                
+      ModifySkill(FIGHT(WT_AXE),([SI_SKILLABILITY:0]),150,"ANY");          
+  if (QuerySkillAbility(FIGHT(WT_SPEAR))<=0)        
+      ModifySkill(FIGHT(WT_SPEAR),([SI_SKILLABILITY:0]),150,"ANY");          
+  if (QuerySkillAbility(FIGHT(WT_WHIP))<=0)
+      ModifySkill(FIGHT(WT_WHIP),([SI_SKILLABILITY:0]),150,"ANY");
+  if (QuerySkillAbility(FIGHT(WT_KNIFE))<=0)                
+      ModifySkill(FIGHT(WT_KNIFE),([SI_SKILLABILITY:0]),150,"ANY");          
+  if (QuerySkillAbility(FIGHT(WT_CLUB))<=0)                
+      ModifySkill(FIGHT(WT_CLUB),([SI_SKILLABILITY:0]),150,"ANY");          
+  if (QuerySkillAbility(FIGHT(WT_STAFF))<=0)
+      ModifySkill(FIGHT(WT_STAFF),([SI_SKILLABILITY:0]),150,"ANY");
+} 
+
+// initialisiert die Skills fuer Spieler (momentan: allg. Waffenskills setzen
+// und P_SKILLS_VERSION)
+protected void InitSkills() {
+  mapping ski;
+  // schonmal initialisiert?
+  if (mappingp(ski=Query(P_NEWSKILLS, F_VALUE)) && sizeof(ski))
+    return;
+
+  // allg. Waffenskills aktivieren
+  set_weapon_skills();
+
+  // Version setzen
+  SetProp(P_SKILLSVERSION, CURRENT_SKILL_VERSION);
+  Set(P_SKILLSVERSION, SAVE|SECURED, F_MODE_AS);
+}
+
+// Updated Skills aus Version 0 und 1 heraus.
+private void FixSkillV1(string skillname, mixed sinfo) {
+  // alte Skills auf mappings normieren
+  if (intp(sinfo)) {
+    sinfo = ([SI_SKILLABILITY: sinfo ]);
+  }
+  // Eine Reihe von Daten werden geloescht, da die Daten aus der
+  // Gilde/Spellbook frisch kommen sollten und sie nicht spieler-individuell
+  // sind: SI_CLOSURE (wird onthefly korrekt neu erzeugt), SI_SKILLARG,
+  // SI_NUMBER_ENEMIES, SI_NUMBER_FRIENDS, SI_DISTANCE, SI_WIDTH, SI_DEPTH,
+  // SI_TESTFLAG, SI_ENEMY, SI_FRIEND.
+  // Ausserdem sind alle SP_* im toplevel falsch, die muessen a) in SI_SPELL
+  // und sollten b) nicht im Spieler gespeichert werden, sondern von
+  // Gilden/Spellbook jeweils frisch kommen.
+  // all dieses Zeug landete in alten Spielern im Savefile.
+  if (mappingp(sinfo))
+    sinfo -= ([SI_CLOSURE, SI_SKILLARG, SI_NUMBER_ENEMIES, SI_NUMBER_FRIENDS,
+             SI_DISTANCE, SI_WIDTH, SI_DEPTH, SI_TESTFLAG, SI_ENEMY,
+             SI_FRIEND, SP_NAME, SP_SHOW_DAMAGE, SP_REDUCE_ARMOUR,
+             SP_PHYSICAL_ATTACK, SP_RECURSIVE, SP_NO_ENEMY,
+             SP_NO_ACTIVE_DEFENSE, SP_GLOBAL_ATTACK ]);
+  else
+  {
+    tell_object(this_object(),sprintf(
+      "\n**** ACHTUNG - FEHLER ***\n" 
+      "Deine Skills enthalten einen defekten Skill %O:\n"
+      "Bitte lass dies von einem Erzmagier ueberpruefen.\n",
+      skillname));
+  }
+}
+
+// Updatet und repariert ggf. Skillmappings in Spielern
+protected void FixSkills() {
+
+  // nur bei genug rechenzeit loslegen
+  if (get_eval_cost() < 750000) {
+    call_out(#'FixSkills, 1);
+    return;
+  }
+  // wenn gar keine Skills da (?): InitSkills() rufen.
+  mapping allskills = Query(P_NEWSKILLS, F_VALUE);
+  if (!mappingp(allskills) || !sizeof(allskills)) {
+      InitSkills();
+      return;
+  }
+
+  // Die Fallthroughs in diesem switch sind voll Absicht!
+  switch(QueryProp(P_SKILLSVERSION)) {
+    // bei Version 0 und 1 das gleiche tun
+    case 0: // von 0 auf 1
+    case 1: // von 1 auf 2
+      foreach(string gilde, mapping skills: allskills) {
+        if (!stringp(gilde)) {
+          // sollte nicht vorkommen - tat aber... *seufz*
+          m_delete(skills, gilde);
+          continue;
+        }
+        walk_mapping(skills, #'FixSkillV1);
+      }
+      // allg. Waffenskills aktivieren, einige alte Spieler haben die noch
+      // nicht.
+      set_weapon_skills();
+      // Speicherflag fuer die Versionsprop muss noch gesetzt werden.
+      Set(P_SKILLSVERSION, SAVE|SECURED, F_MODE_AS);
+      // Version ist jetzt 2.
+      SetProp(P_SKILLSVERSION, 2);
+      // Fall-through
+   case 2:
+      // gibt es noch nicht, nichts machen.
+      //SetProp(P_SKILLSVERSION, 3);
+      // Fall-through, ausser es sind zuwenig Ticks da!
+      if (get_eval_cost() < 750000)
+        break; 
+  }
+  // Falls noch nicht auf der aktuellen Version angekommen, neuer callout
+  if (QueryProp(P_SKILLSVERSION) < CURRENT_SKILL_VERSION)
+      call_out(#'FixSkills, 2);
+}
+
+protected void updates_after_restore(int newflag) {
+  //Allgemeine Waffenskills aktivieren, wenn noetig
+  // Wird nun von InitSkills bzw. FixSkills uebernommen, falls noetig.
+  if (newflag) {
+    InitSkills();
+  }
+  else if (QueryProp(P_SKILLSVERSION) < CURRENT_SKILL_VERSION) {
+    // Falls noetig, Skills fixen/updaten. *grummel*
+    FixSkills();
+  }
+  // Prop gibt es nicht mehr. SAVE-Status loeschen. 
+  Set(P_GUILD_PREVENTS_RACESKILL,SAVE,F_MODE_AD);
+}
+
+// Standardisierte Nahkampf-Funktion fuer alle Nahkampf-Waffenarten
+protected mapping ShortRangeSkill(object me, string sname, mapping sinfo) 
+{ 
+  int val, w;
+  object enemy;
+  
+  if (!mappingp(sinfo) || !objectp(sinfo[P_WEAPON]))
+    return 0;
+
+  w = ([WT_KNIFE : 8,
+        WT_SWORD : 5,
+        WT_AXE   : 4,
+        WT_SPEAR : 6,
+        WT_CLUB  : 1,
+        WT_WHIP  : 9,
+        WT_STAFF : 7])[sinfo[P_WEAPON]->QueryProp(P_WEAPON_TYPE)];
+      
+
+  val = sinfo[SI_SKILLABILITY]*(sinfo[P_WEAPON]->QueryProp(P_WC)*
+                                (w*QueryAttribute(A_DEX)+
+                                 (10-w)*QueryAttribute(A_STR))/700)
+        /MAX_ABILITY;
+
+  if (val > 85) {
+    log_file("WEAPON_SKILLS", sprintf("%s: Zu hoher Schaden von: "
+    +"TO: %O, TI: %O, PO: %O, val: %d, A_DEX: %d, A_STR: %d, "
+                                   +"P_WEAPON: %O, P_WC: %d\n", dtime(time()),
+                                   this_object(), this_interactive(), 
+                                   previous_object(), val, 
+                                   QueryAttribute(A_DEX),
+                                   QueryAttribute(A_STR), sinfo[P_WEAPON],
+                                   sinfo[P_WEAPON]->QueryProp(P_WC)));
+    val = 85;
+  }
+
+  /*
+    Der zusätzliche Schaden der allgemeinen Waffenskills berechnet
+    sich wie folgt: 
+
+    sinfo[SI_SKILLABILITY)* (P_WC * ( X ) / 800) / MAX_ABILITY
+
+    Dabei beruecksichtigt X je nach Waffentyp in unterschiedlicher
+    Gewichtung die Werte fuer Geschicklichkeit und Staerke. 
+
+     X == 
+
+       Messer   : 8*A_DEX + 2*A_STR
+       Schwert  : 5*A_DEX + 5*A_STR
+       Axt      : 4*A_DEX + 6*A_STR 
+       Speer    : 6*A_DEX + 4*A_STR
+       Keule    : 1*A_DEX + 9*A_STR
+       Peitsche : 9*A_DEX + 1*A_STR
+  */
+
+  sinfo[SI_SKILLDAMAGE]+=val;
+
+
+  /* Lernen: Wird immer schwieriger, nur bei jedem 20. Schlag im Schnitt,
+   * und nur dann, wenn der Gegner auch XP gibt. */
+  if (random(MAX_ABILITY+1)>sinfo[SI_SKILLABILITY] && !random(10))
+  {
+         enemy=sinfo[SI_ENEMY];
+         if (objectp(enemy) && (enemy->QueryProp(P_XP)>0))
+         {
+           object ausbilder;
+        //         log_file("humni/log_wurm","Haut: %s und zwar %s, mit xp %d\n",geteuid(this_object()),to_string(enemy),enemy->QueryProp(P_XP));
+            LearnSkill(sname, random(5), 150);
+        // Gibt es einen Ausbilder?
+        if (QueryProp(P_WEAPON_TEACHER) && 
+                        ausbilder=find_player(QueryProp(P_WEAPON_TEACHER)))
+          {
+            // Ist der Ausbilder anwesend?
+            if (present(ausbilder,environment()))
+              {
+              // Ausbilder und Azubi muessen dieselbe Waffe haben.
+              //string wt_aus,wt_azu;
+              object waf_aus,waf_azu;
+
+              waf_azu=QueryProp(P_WEAPON);
+              waf_aus=call_other(ausbilder,"QueryProp",P_WEAPON);
+
+              //wt_azu=call_other(waf_azu,"QueryProp",P_WEAPON_TYPE);
+              //wt_aus=call_other(waf_aus,"QueryProp",P_WEAPON_TYPE);
+              //if (wt_azu==wt_aus)
+              if (objectp(waf_aus) && objectp(waf_azu) &&
+                  (string)waf_aus->QueryProp(P_WEAPON_TYPE)
+                     == (string)waf_azu->QueryProp(P_WEAPON_TYPE)) 
+                {
+                // Bonus von bis zu 5 Punkten
+                //log_file("humni/log_azubi",
+                  // sprintf("Azubi %O und Ausbilder %O : Waffentypen %s und %s, gelernt\n",this_object(),
+                    //   ausbilder, wt_azu, wt_aus));
+                LearnSkill(sname,random(6),150);
+                }
+          }
+        }
+    }
+  }
+
+  /* 
+     Die Schwierigkeit liegt bei 150, so dass 
+     ein Lvl. 1 Spieler maximal 15% Skill 
+     usw...
+     lernen kann. (Genaue Tabelle in /std/living/skills bei LimitAbility)
+  */         
+
+  return sinfo;
+}
+
+
+// Standardisierte Fernkampf-Funktion fuer alle Fernkampf-Waffenarten
+
+// *** noch deaktiviert ***
+
+protected mapping LongRangeSkill(object me, string sname, mapping sinfo, int dam) 
+{ int abil,val;
+
+  if (!mappingp(sinfo) || !dam || !objectp(sinfo[P_WEAPON]) ||
+      (sinfo[P_WEAPON]->QueryProp(P_SHOOTING_WC))<5)
+    return 0;
+
+  abil=sinfo[SI_SKILLABILITY]+sinfo[OFFSET(SI_SKILLABILITY)]; 
+  val=dam*abil/MAX_ABILITY;
+  val=val/2+random(val/2+1);
+  val=(val*QuerySkillAttribute(SA_DAMAGE))/100;
+  sinfo[SI_SKILLDAMAGE]+=val;
+
+  if (random(MAX_ABILITY+1)>sinfo[SI_SKILLABILITY] && random(50)==42)
+    LearnSkill(sname, 1, 150);
+
+  return sinfo;
+}
+
+
+
+// Die einzelnen Waffenskills rufen dann nur die Standard-Funktion auf.
+
+protected mapping StdSkill_Fight_axe(object me, string sname, mapping sinfo)
+{
+  return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_club(object me, string sname, mapping sinfo)
+{
+  return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_knife(object me, string sname, mapping sinfo)
+{
+  return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_spear(object me, string sname, mapping sinfo)
+{
+  return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_sword(object me, string sname, mapping sinfo) 
+{
+  return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_whip(object me, string sname, mapping sinfo)
+{
+  return ShortRangeSkill(me, sname, sinfo);
+}
+
+protected mapping StdSkill_Fight_staff(object me, string sname, mapping sinfo)
+{
+  return ShortRangeSkill(me, sname, sinfo);
+}
+
+
+
+
+// Die Fernwaffenskills sind Munitionsabhaengig
+
+// *** noch deaktiviert ***
+
+protected mapping StdSkill_Shoot_arrow(object me, string sname, mapping sinfo)
+{
+  return LongRangeSkill(me, sname, sinfo, 40);
+}
+
+protected mapping StdSkill_Shoot_bolt(object me, string sname, mapping sinfo)
+{ 
+  return LongRangeSkill(me, sname, sinfo, 40);
+}
+
+protected mapping StdSkill_Shoot_dart(object me, string sname, mapping sinfo)
+{
+  return LongRangeSkill(me, sname, sinfo, 20);
+}
+
+protected mapping StdSkill_Shoot_stone(object me, string sname, mapping sinfo)
+{
+  return LongRangeSkill(me, sname, sinfo, 40);
+}
+
+protected mixed _query_localcmds() {
+    return ({ ({"spruchermuedung","enable_spell_fatigue_compat",0,0}) });
+}
+
+
+
+// *** Kompatibilitaetsmodus fuer Spellfatigues (Emulation 2s-Alarmzeit) ***
+
+/** Speichert eine Spellfatigue von <duration> Sekunden fuer <key>.
+ * Ist hier nur fuer den Spellfatigue-Compat-mode.
+ * <key> darf 0 sein und bezeichnet das globale Spellfatigue.
+ * Rueckgabewert: Ablaufzeit der gesetzten Sperre
+                  -1, wenn noch eine nicht-abgelaufene Sperre auf dem <key> lag.
+                  0, wenn duration 0 ist.
+ */
+public varargs int SetSpellFatigue(int duration, string key) {
+
+  // 0 sollte nie eine Sperre bewirken, auch nicht im compat mode.
+  if (!duration) return 0;
+
+  if (spell_fatigue_compat_mode) {
+    // Spell-Fatigues auf HBs synchronisieren (2s-Alarmzeit-Emulation).
+    // Aufrunden auf ganzzahlige Vielfache von __HEART_BEAT_INTERVAL__
+    if (duration % __HEART_BEAT_INTERVAL__)
+        ++duration;
+
+    // Startpunkt des Delay soll Beginn der aktuellen Kampfrunde (HB-Zyklus)
+    // sein, ggf. um max. eine Sekunde zurueckdatieren.
+    // (spell_fatigue_compat_mode hat die Zeit des letzten HB-Aufrufs)
+    // Falls durch irgendein Problem (z.B. sehr hohe Last), der letzte HB
+    // laenger als 1s zurueckliegt, funktioniert das natuerlich nicht, aber
+    // bei fatigue+=spell_fatigue_compat_mode kann der Spieler zuviel Zeit
+    // einsparen.
+    if (time() > spell_fatigue_compat_mode)
+        --duration;  //1s zurueckdatieren
+  }
+
+  return ::SetSpellFatigue(duration, key);
+}
+
+/** Befehlsfunktion fuer Spieler um den Spellfatigue-Kompatibilitaetsmodus
+ * umzuschalten.
+ */
+public int enable_spell_fatigue_compat(string cmd) {
+  if (QueryProp(P_LAST_COMBAT_TIME) + 600 > time()) {
+    write(break_string(
+      "Im Kampf oder kurz nach einem Kampf kannst Du nicht zwischen "
+      "alter und neuer Spruchermuedung umschalten.\n"
+      "Momentan benutzt Du die "
+      + (spell_fatigue_compat_mode ? "alte (ungenauere)" : "neue (normale)")
+      + " Spruchermuedung.",78,0,BS_LEAVE_MY_LFS));
+    return 1;
+  }
+
+  if (cmd=="alt") {
+    spell_fatigue_compat_mode=time();
+    write(break_string(
+      "Alte Spruchermuedung wurde eingeschaltet. Alle Ermuedungspausen "
+      "zwischen Spruechen werden auf Vielfache von 2s aufgerundet und "
+      "beginnen in der Regel am Anfang Deiner Kampfrunde."));
+    return 1;
+  }
+  else if (cmd=="neu" || cmd=="normal") {
+    spell_fatigue_compat_mode=0;
+    write(break_string(
+      "Normale Spruchermuedung wurde eingeschaltet. Alle Ermuedungspausen "
+      "zwischen Spruechen werden sekundengenau berechnet."));
+    return 1;
+  }
+
+  notify_fail(break_string(
+      "Moechtest Du die alte oder die neue Spruchermuedung?\n"
+      "Momentan benutzt Du die "
+      + (spell_fatigue_compat_mode ? "alte (ungenauere)" : "neue (normale)")
+      + " Spruchermuedung.",78,0,BS_LEAVE_MY_LFS));
+  return 0;
+}
+
+/** Speichert die Zeit des letztes Heartbeats.
+ */
+protected void heart_beat() {
+  if (spell_fatigue_compat_mode)
+    spell_fatigue_compat_mode = time();
+}
+
+static int _set_guild_level(int num)
+{ string gilde;
+  mapping levels;
+
+  if ( !(gilde=QueryProp(P_GUILD)) )
+    return 0;
+
+  if ( !mappingp(levels=Query(P_GUILD_LEVEL)) )
+    levels=([]);
+
+  levels[gilde]=num;
+  Set(P_GUILD_LEVEL,levels);
+  GMCP_Char( ([P_GUILD_LEVEL: num]) );
+
+  return num;
+}
+
+static int _query_guild_level()
+{ string  gilde;
+  mapping levels;
+
+  if ( !(gilde=QueryProp(P_GUILD)) )
+    return 0;
+
+  if ( !mappingp(levels=Query(P_GUILD_LEVEL)) )
+      return 0;
+
+  return levels[gilde];
+}
+
+static string _set_guild_title(string t)
+{ string gilde;
+  mapping titles;
+
+  if ( !(gilde=QueryProp(P_GUILD)) )
+    return 0;
+
+  if ( !mappingp(titles=Query(P_GUILD_TITLE)) )
+    titles=([]);
+
+  titles[gilde]=t;
+  Set(P_GUILD_TITLE,titles);
+  GMCP_Char( ([P_GUILD_TITLE: t]) );
+  return t;
+}
+
+static string _query_guild_title()
+{ string gilde,t;
+  object g;
+  mapping titles;
+
+  if ( !(gilde=QueryProp(P_GUILD)) )
+    return 0;
+
+  if ( !mappingp(titles=Query(P_GUILD_TITLE)) )
+    titles=([]);
+
+  t=titles[gilde];
+  if ( !t && query_once_interactive(this_object())
+      && objectp(g=find_object("/gilden/"+gilde)) )
+  {
+    g->adjust_title(this_object());
+    SetProp(P_TITLE,0);
+
+    if ( !mappingp(titles=Query(P_GUILD_TITLE)) )
+      return 0;
+
+    t=titles[gilde];
+  }
+
+  return t;
+}
+
+
+static string _set_guild(string gildenname)
+{ object pre;
+
+  if (!objectp(pre=previous_object()))
+    return 0;
+
+  if ( pre!=this_object() // Das Lebewesen selber darf die Gilde setzen,
+      && object_name(pre)!=GUILDMASTER  // der Gildenmaster auch
+      && (!this_player()
+          || this_player() != this_interactive()
+          || !IS_ARCH(this_player())
+         )
+      )
+    return 0;
+
+  Set(P_GUILD,gildenname);
+  GMCP_Char( ([P_GUILD: gildenname]) );
+  return gildenname;
+}
+
+static string _query_guild()
+{ string res;
+
+  if ( !(res=Query(P_GUILD)) && query_once_interactive(this_object()) )
+  {
+    // Spieler, die keiner Gilde angehoeren, gehoeren zur Abenteurergilde
+    if ( !(res=QueryProp(P_DEFAULT_GUILD)) )
+      return DEFAULT_GUILD;
+    else
+      Set(P_GUILD,res);
+    return res;
+  }
+
+  return res;
+}
+
+static string _query_title()
+{ string ti;
+
+  if ( stringp(ti=Query(P_TITLE)) )
+    return ti;
+
+  return QueryProp(P_GUILD_TITLE);
+}
+
+static string _set_title(string t)
+{
+  Set(P_TITLE, t, F_VALUE);
+  GMCP_Char( ([P_TITLE: t]) );
+  return t;
+}
+
diff --git a/std/player/soul.c b/std/player/soul.c
new file mode 100644
index 0000000..438f2c6
--- /dev/null
+++ b/std/player/soul.c
@@ -0,0 +1,3218 @@
+// MorgenGrauen MUDlib
+//
+// player/soul.c -- Die Seele des Spielers
+//
+// $Id: soul.c 9527 2016-03-12 11:37:54Z Arathorn $
+
+// Letzte Aenderung vom 08.09.95  Wargon
+
+// Set TabStop to 2 characters
+
+/* Version 1.41 MG, September 95
+   - Bei "frage" und "antworte" Test auf P_PERM_STRING fuer Sprachflueche.
+   - bugfix bei "schweige" (nahm keine Adverbien an)
+   */
+
+/* Version 1.4 MG, August 95
+   - Hilfefunktion eingebaut, siehe auch soulhelp.c
+   - einige kleinere Aenderungen erst jetzt durch eine neue Version gewuerdigt
+   - neue Verben, wie eigentlich fast immer :>
+   - typos und bugs gefixed (und neue eingebaut...)
+   - Funktion zur Abfrage von Adverbien von aussen eingebaut (Hallo, Anthea :>)
+   - so schlimm kann das doch nicht sein, einen TabStop von 2 zu nehmen, fuer
+     dieses eine file, alles andere zerlegt meine Formatierung immer so :<
+   - koenntet ihr bitte mal oben das "Letzte Aenderung" aendern, wenn ihr
+     irgendwo was aendert?
+   */
+
+/* Version 1.35 MG, Dezember 94
+   - Verben
+   - Aenderung des Parsings der quoted adverbs
+   - rknuddel ignorieren wird vom normalen ignore uebernommen
+   */
+
+/* Version 1.32 MG, Juli 94
+   - einige Verben
+   - ignorieren des rknuddel
+   */
+
+/* Version 1.31 MG, Mai 94
+   - einige Verben
+   */
+
+/* Version 1.3 MG, Mai 94
+   - quoted adverbs GEHEN jetzt
+   - ParseRest / ParseRemote neu geschrieben
+   */
+
+/* Version 1.21 MG, April 94
+   - quoted adverbs
+   */
+
+/* Danke an Angus fuer den Text von "liebe" */
+
+/* Version 1.2 MG, Januar 94
+   - Umstellung der Feelings von vielen kleinen Funktionen auf eine grosse,
+     damit der Funktionsoverhead wegfaellt.
+   - neue Ausgabe der Adverbien, mit more und nur noch eines pro Zeile
+   - mal wieder neue Verben.
+   - und das neue Standardadverb "jofi" :)
+   */
+
+/* Version 1.1 MG, November 93
+   Aenderungen:
+   - Ich habe "alle(n)" eingebaut. Die Verwaltung steht, man kann jetzt
+     Verben die Moeglichkeit "alle" geben (Bsp. "wink alle").
+   - inzwischen wieder einige Verben mehr, aber muss ich das noch
+     erwaehnen?
+   - (HASS) Rumata hat mein schoenes System fuer die Ausgabestrings
+     verkompliziert. Statt &a steht da jetzt z.B. @@adverb@@, was in
+     einer weiteren unnoetigen Funktion resultierte. Naja.
+   Highlander ryn Tahar
+   */
+
+/*
+   Ha! Ich nehme jetzt die erste ganze Versionsnummer fuer mich in Anspruch :)
+   So, heute ist's geschafft (Ich auch...). Ich bin fertig - ich mach nur
+   noch neue Verben rein, wenn Ideen kommen.
+   Gegeben an Dienstag, dem 22. Juni 1993 im heimatlichen Horst.
+   Highlander ryn Tahar.
+   P.S.: Kopiere sich das Ding, wer will - unter folgenden Bedingungen:
+   - Den Goettern von MorgenGrauen und
+   - Highlander@TAPPMud     Bescheid sagen und ausserdem
+   *seufz* nehmt Highlander hier in MorgenGrauen!! -HrT
+   Ha! Ihr koennt wieder TAPPMud nehmen :>  Aber sagt's ruhig in MG. -HrT
+   - entweder den ganzen Schwall hier drinlassen oder mich mit einem
+   neuen Text erwaehnen.
+   Das Ganze unter der Voraussetzung, dass ueberhaupt mal jemand eine deutsche
+   Seele braucht und sie nicht selber schreiben will :-) (ersparts euch lieber)
+   Highlander ryn Tahar.
+
+   **Arbeit:
+   Einbau von Adverbien, andere Reaktionen bei Geistern als "Wie bitte?",
+   einige neue Verben, einige alte Verben rausgeschmissen.
+   Weil es Probleme mit dem autoloading der playeradverbs gab, wurde
+   ausserdem die soul vom Objekt zum inheritfile fuer die playershell.
+
+   **Ideen  zum Weitermachen:
+   - (verb) alle, z.B. tritt alle   -- Moeglichkeit eingebaut
+   - Geisterverben mit Adverb
+
+   Version 1.0 fuer MorgenGrauen    Highlander Mai/Juni 93
+   */
+
+
+/* Hier ist sie nun, die DEUTSCHE version der soul,
+   viel Spass damit.
+   Poietix  Mai 1992
+   Vers.: 0.4 fuer JRMud
+   P.S. bitte meckert nicht dran rum , sondern verbessert und
+   erweitert sie.
+
+   Olpp November 1992
+   Vers.: 0.5 fuer MorgenGrauen
+
+   He, Olpp, schreibt man nicht die neueste Aenderung UEBER die alten?
+   *grins*, Highlander
+   */
+#pragma strong_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define SOULHELP "/std/player/soulhelp"
+#define LF "\n"
+#define NOT_SELF 1
+#define NOT_DEAD 1
+
+#define QPP QueryPossPronoun
+#define RETURN return _notify_fail
+#define Return return 0||_notify_fail  // netter Trick, muss ich mir merken -HrT
+#define GHOSTCHECK(sel,oth,vic) if (ghost()) {  write(sel); say(oth,who||ME);\
+                                  if (vic) who->Message(vic); return 1;  }
+#define HELPCHECK(x) if (str && (str=="-h" || str=="-?" || str=="/h" \
+         || str=="/?" || str=="hilfe"))\
+                       { More(SOULHELP->Help(x)); return 1; }
+
+// "schau an" als nicht-Untersuchung. Klappt aber anscheinend nicht, weil
+// ich nicht gegen den GD ankomme. Also besser auskommentiert lassen.
+#ifdef SCHAU_AN
+#undef SCHAU_AN
+#endif
+
+// Anpiepsen mit Text. Im Moment erlaubt, bei Missfallen auskommentieren und
+// in der Hilfe auskommentieren.
+#ifdef WECKE
+#undef WECKE
+#endif
+
+#define WECKE
+
+#define NEED_PROTOTYPES
+#include <thing/description.h>
+#include <thing/properties.h>
+#include <player.h>
+#include <player/comm.h>
+#include <language.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+
+#include <defines.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <class.h>
+
+static object who, ofoo;
+static int for_all, flag, ifoo;
+mapping plr_adverbs;
+static string out_sel, out_vic, out_oth, adverb, sfoo;
+
+private void ParseAdverb(string *words);
+private string convert_string(string str);
+varargs mixed More(string str, int fflag, string returnto);
+string MatchAdverb(string str);
+
+mapping
+QueryStdAdverbs()  {
+  return ([
+    "unve" : "unverschaemt",
+    "gutg" : "gutgelaunt",
+    "gutm" : "gutmuetig",
+    "froh" : "froh",
+    "glue" : "gluecklich",
+    "wuet" : "wuetend",
+    "frec" : "frech",
+    "daem" : "daemonisch",
+    "boes" : "boese",
+    "ungl" : "ungluecklich",
+    "lang" : "langsam",
+    "schn" : "schnell",
+    "jamm" : "jammernd",
+    "freu" : "freundlich",
+    "shue" : "schuechtern",
+    "amue" : "amuesiert",
+    "aerg" : "aergerlich",
+    "aner" : "anerkennend",
+    "erst" : "erstaunt",
+    "bitt" : "bitter",
+    "brei" : "breit",
+    "vors" : "vorsichtig",
+    "char" : "charmant",
+    "kalt" : "kalt",
+    "verf" : "verfuehrerisch",
+    "zufr" : "zufrieden",
+    "tief" : "tief",
+    "verz" : "verzweifelt",
+    "drec" : "dreckig",
+    "vert" : "vertraeumt",
+    "uebe" : "ueberzeugt",
+    "frus" : "frustriert",
+    "stra" : "strahlend",
+    "hoff" : "hoffnungsvoll",
+    "unge" : "ungeduldig",
+    "unsi" : "unsinnigerweise",
+    "unsc" : "unschuldig",
+    "unwi" : "unwissend",
+    "iron" : "ironisch",
+    "wiss" : "wissend",
+    "gema" : "gemaechlich",
+    "sehn" : "sehnsuechtig",
+    "laut" : "laut",
+    "lieb" : "liebevoll",
+    "froe" : "froehlich",
+    "dank" : "dankbar",
+    "natu" : "natuerlich",
+    "gedu" : "geduldig",
+    "perf" : "perfekt",
+    "vers" : "verspielt",
+    "hoef" : "hoeflich",
+    "stol" : "stolz",
+    "frag" : "fragend",
+    "rupp" : "ruppig",
+    "trau" : "traurig",
+    "vera" : "veraechtlich",
+    "scha" : "schamlos",
+    "erns" : "ernst",
+    "schu" : "schuechtern",
+    "zaer" : "zaertlich",
+    "sanf" : "sanft",
+    "entg" : "entgeistert",
+    "heim" : "heimtueckisch",
+    "gela" : "gelangweilt",
+    "wild" : "wild",
+    "jofi" : "wie Jof, wenn er mal nicht idlet",
+  ]);
+}
+
+mapping
+QueryAdverbs() {
+  if (extern_call())
+    return deep_copy(plr_adverbs);
+  return plr_adverbs;
+}
+
+string
+MatchAdverb(string a)  {
+  ParseAdverb(explode(a," "));
+  return adverb;
+}
+
+// Verwaltungsroutinen
+
+static void
+add_soul_commands()  {
+  if (!plr_adverbs)
+    plr_adverbs=([]);
+  add_action("SoulComm", "", 1);
+}
+
+static int
+verben_liste()  {
+  More(SOULHELP->Help());
+  return 1;
+}
+
+#define ghost() QueryProp(P_GHOST)
+#define frog() QueryProp(P_FROG)
+#define capname() capitalize(name())
+#define gname() (ghost()?(frog()?"Der Geist eines Frosches"\
+			        :"Der Geist von "+capname())\
+		        :capname())
+
+varargs private void
+ParseRest(string arg, mixed extra)  {
+  string wer,wie,*words,quotea;
+  int num,bis;
+  who = for_all = adverb = 0;
+  if (!arg) return;
+  if (extra)
+    if (!pointerp(extra)) {
+      if (sscanf(arg, extra+" %s", wie)==1)
+      arg=wie;
+    }
+    else
+      for (bis=sizeof(extra),num=0; num<bis; num++)
+        if (sscanf(arg, extra[num]+" %s", wie)==1)
+          arg=wie;
+
+  if ((bis=strstr(arg, "/"))>=0)
+    quotea=arg[bis..],arg=arg[0..bis-1];
+  quotea=quotea||"",arg=arg||"";
+
+  words=explode(implode(explode(arg, ","), " und"), " ");
+  if (!sizeof(words)) return;
+  if (sizeof(words) && (words[0]=="alle" || words[0]=="allen"))
+    for_all=1,wer=words[0],words=words[1..];
+  if (!for_all)  {     /* noch kein Opfer */
+    wer=match_living(lower_case(words[0]));
+    if (stringp(wer)) who=present(wer, environment(ME));
+    if (!who) who=present(words[0], environment(ME));
+    if (who && who->QueryProp(P_INVIS)) who=0;
+    }
+  if (who && sizeof(words))
+    words=words[1..];  /* Opfer gefunden - wenn's eines gibt */
+  words+=explode(quotea, " ");
+  words-=({""});
+  if (sizeof(words)) ParseAdverb(words);
+}
+
+private int
+ParseRemote(string arg)  {
+  string wer,*words; 
+
+  adverb = 0; // Adverb vom letzten Mal keinesfalls wiederverwenden. ;-)
+
+  if (!stringp(arg) || !sizeof(arg)) return 0;
+  
+  words=explode(arg," ");
+
+  mixed liv = match_living(lower_case(words[0]));
+  if (stringp(liv))
+      who=find_player(liv);
+  
+  if (who) {
+    // Ziel ist ein Spieler.
+    if (!who->QueryProp(P_INVIS) || IS_WIZARD(ME))
+    {
+      // Spieler ist nicht Invis oder ich bin Magier.
+      string nam = (query_once_interactive(ME) ? getuid() : 
+	             lower_case(name(RAW)));
+      if (query_verb()[0..5]=="rknudd" &&
+	  who->TestIgnore(nam+".rknuddel") )
+      {
+        // ich oder das Kommando werde ignoriert.
+        write(who->Name(WER)+" ignoriert Deinen Knuddelversuch.\n");
+        return 1;
+      }
+    }
+    else
+      // Spieler ist invis und ich bin kein Magier.
+      who = 0;
+  }
+  // kein eingeloggter und sichtbarer Spieler. Vielleicht ein NPC? (BTW: kein
+  // else if, weil im if fuer Spieler oben who genullt werden kann und dann
+  // nochmal nach nem NPC gesucht werden soll.)
+  if (!who) {
+    wer = match_living(lower_case(words[0]));
+    if(stringp(wer)) 
+      who=present(wer,environment(ME));
+    if (!who) who=present(words[0], environment(ME));
+    if (who && who->QueryProp(P_INVIS)) who=0;
+  }
+
+  if (!who || sizeof(words)==1) return 0;
+  words=words[1..];
+  ParseAdverb(words);
+  return(0);
+}
+
+/**
+ Gibt den passenden Adverb-Text zu einem key zurueck
+ \param s Danach wird in der Adverbien-Liste gesucht
+ \param fuzzy 
+ \return Der gefundene Adverbientext oder 0
+ */
+varargs string GetPlayerAdverb( string s, int fuzzy ) {
+  int i, j; 
+  string *search_pattern,
+         *search_result, 
+          result;
+
+  // Erstmal gucken, ob der String direkt gefunden werden kann
+  // das geht am schnellsten
+  result = QueryStdAdverbs()[s] || plr_adverbs[s];
+
+  // Wenn noch kein Ergebnis gefunden, und man unscharf suchen will
+  if ( fuzzy && !result) {
+
+    // Suchmuster fuer das Intersect erzeugen
+    search_pattern=({s});
+
+    j = sizeof(s)-1;
+    for ( i=2;  i < j ;i++) {
+      search_pattern += ({s[0..<i]});
+    }
+
+    // Intersect zwischen allen bekannten Abkuerzungen und Search-Pattern
+    // erzeugen. Dieses wird dann gleichzeitig nach Laenge sortiert
+    // genauester Treffer = String mit groesster Laenge
+    search_result = sort_array(
+      (m_indices(QueryStdAdverbs()) | m_indices(plr_adverbs))&search_pattern, 
+         #'>);
+
+    // Adverb zum genauesten Treffer zurueckgeben
+    if (sizeof(search_result)) 
+      result = QueryStdAdverbs()[search_result[0]] || 
+               plr_adverbs[search_result[0]];
+  }
+
+  return result;
+}
+
+/**
+  Parst die Adverbienparameter fuer Verben und speichert die
+  passende Textausgabe in der globalen Variable "adverb"
+  \param words Array mit den zu parsenden Adverbien-Strings
+*/
+private void
+ParseAdverb(string *words)  {
+  int num,andsign,bis;
+  string qadv,*adv,cnt;
+
+  adv=({});
+  qadv=0;
+
+  bis=sizeof(words);
+  // Sonderfall Gequotetes Adverb (alles nach dem Quote) speichern und aus 
+  // Words rausschneiden.
+  for (num=0; num<bis; num++)
+    if (words[num][0..0]=="/")  {
+      words[num]=words[num][1..];
+      qadv=implode(words[num..], " ");
+      words=words[0..num-1];
+      break;
+    }
+
+  // Es kann sein, dass vor dem Quote noch eine und steht. Das wird jetzt auch 
+  // noch entfernt, muss aber spaeter wieder eingefuegt werden.
+  if (sizeof(words) && words[<1]=="und")  {
+    words=words[0..<2];
+    andsign=1;
+  }
+
+  // Weitersuchen?
+  if (bis=sizeof(words))
+    for (num=0; num<bis; num+=2)
+       adv+=({GetPlayerAdverb(words[num], 1)});
+  cnt=CountUp(adv-({0}));
+
+  // Ausgabe zusammenbauen
+  if (andsign)
+    adverb=CountUp((sizeof(adv) ? adv : ({}))+(qadv ? ({qadv}) : ({})));
+  else if (sizeof(cnt) && sizeof(qadv))
+    adverb = cnt + " " + qadv;
+  else if (sizeof(qadv))
+    adverb = qadv;
+  else if (sizeof(cnt))
+    adverb = cnt; 
+  if (adverb=="") adverb=0;
+}
+
+private mixed MixedOut(int casus)  {
+  object *envs,*vics;
+  string *names,out,aufz;
+  int count,msg;
+
+  for_all=0;
+  vics=({});
+  names=({});
+  envs=all_inventory(environment())-({this_player()});
+  if (!(count=sizeof(envs)))
+    RETURN("Nichts und niemand da. Schau Dich naechstes Mal besser um.\n");
+  for ( ; count--; )
+    if (living(envs[count]) && !envs[count]->QueryProp(P_INVIS))  {
+      vics+=({envs[count]});
+      names+=({envs[count]->name(casus)});
+    }
+  if (!sizeof(vics))
+    RETURN("Keiner da. Schau Dich naechstes Mal besser um.\n");
+  aufz=CountUp(names);
+  for (count=sizeof(vics); count--;)
+  {
+    out=implode(explode(out_vic, "@@alle@@"),aufz);
+        out = regreplace( out, "\\<"+vics[count]->name(casus)+"\\>",
+                          capitalize(vics[count]->QueryDu(casus)), 0 );
+
+    msg=vics[count]->ReceiveMsg(convert_string(out),MT_COMM,MA_EMOTE,
+                                0,this_object());
+    switch(msg)
+    {
+      case MSG_DELIVERED:
+      case MSG_BUFFERED:
+        break;
+      case MSG_IGNORED:
+      case MSG_VERB_IGN:
+      case MSG_MUD_IGN:
+        write(vics[count]->Name()+" ignoriert Dich oder diesen Befehl.\n");
+        break;
+      default:
+        write(vics[count]->Name()+" konnte Dich gerade nicht lesen.\n");
+    }
+  }
+  write(break_string(convert_string(implode(explode(out_sel,"@@alle@@"),aufz)
+    +LF), 78));
+  return 1;
+}
+
+varargs private int
+CheckLife(int no_self,int no_dead, string no_self_text, string no_dead_text)  {
+  if (who && living(who) && who!=this_player()) return 0;
+  if (no_self && who && who==this_player())  {
+    if (no_self_text)
+      write(no_self_text+LF);
+    else
+      write("Mach das mit anderen, nicht mit Dir selber.\n");
+    return 1;
+  }
+  if (who && !living(who) && no_dead)  {
+    if (no_dead_text)
+      write(no_dead_text+LF);
+    else
+      write("Das darfst Du nur mit Lebewesen.\n");
+    return 2;
+  }
+  if (!who)  {
+    write("Schau Dich erst mal um - das angegebene Objekt ist nicht da.\n");
+    return 3;
+  }
+  return(0); //non-void. Fall-through, alles OK.
+}
+
+private string
+convert_string(string str)  {
+  /* Ich bin unschuldig, ich hatte das viel einfacher und schoener :)
+     Rumata wollte das so ;)  -HrT                                     */
+  str = implode( explode( str, "@@name@@" ), capname() );
+  str = implode( explode( str, "@@gname@@" ), gname() );
+  str = implode( explode( str, "@@wer@@" ),
+    (who?capitalize(who->name(WER,2)||""):"" ));
+  str = implode( explode( str, "@@ wen@@" ),
+    (who?" "+who->name(WEN,2):""));
+  str = implode( explode( str, "@@ wem@@" ),
+    (who?" "+who->name(WEM,2):""));
+  str = implode( explode( str, "@@wen@@" ),
+    (who?who->name(WEN,2):""));
+  str = implode( explode( str, "@@wem@@" ),
+    (who?who->name(WEM,2):""));
+  str = implode( explode( str, "@@wessen@@" ),
+    (who?who->name(WESSEN,2):""));
+  str = implode( explode( str, "@@adverb@@" ),
+    (adverb?" "+adverb:"") );
+  return str;
+}
+
+private int
+FeelIt()  {
+  int msg, flg;
+
+  flg = MSGFLAG_SOUL;
+  if (query_verb() && (query_verb()[0..3]=="frag" || query_verb()[0..3]=="antw"))
+    flg |= MSGFLAG_SAY;
+  if (query_verb() && (query_verb()[0..5]=="rknudd" || query_verb()=="rwink"))
+    flg |= MSGFLAG_REMOTE;
+
+  // NPC haben keine TM-Hist (comm.c). Leider erben aber div. Magier die Soul
+  // (trotzdem sie in /std/player/ liegt) in ihren NPC... *fluch*
+  if (query_once_interactive(ME))
+    _recv(who, break_string(convert_string(out_sel),78), flg);
+  else
+    tell_object(ME, break_string(convert_string(out_sel),78));
+
+  if (out_vic && who)  {
+    if (query_once_interactive(who))  {
+      msg=who->Message( break_string(convert_string( out_vic ),78), flg);
+      if (msg==-1)
+        write(who->name()+" ignoriert Dich oder diesen Befehl.\n");
+    } else
+      tell_object(who,break_string(convert_string( out_vic ),78));
+  }
+  if (out_oth)
+  say( break_string(convert_string( out_oth ),78), ({who,this_player()}) );
+  out_sel=out_vic=out_oth=0;
+  return 1;
+}
+
+/**
+  Die Funktion stellt einen Hilfetext zur Verfuegung und listet die 
+  definierten Adverbien auf.
+  \param mine 0 = alle Adverbien, 
+              1=nur selbst definierte Adverbien 
+              2=nur die vom System bereitgestellten
+ */
+private int zeige_adverbs(int mine)  {
+  mapping adverb_list;
+  string out,s;
+
+  // Parameter auswerten
+  switch (mine){
+    case 1:
+      adverb_list=plr_adverbs;
+      out = "Du hast folgende Adverbien definiert:\n";
+      break;
+    case 2:
+      adverb_list=QueryStdAdverbs();
+      out = "Systemseitig sind folgende Adverbien definiert:\n";
+      break;
+    default:
+      adverb_list=QueryStdAdverbs()+plr_adverbs;
+      out = "Folgende Adverbien stehen Dir zur Verfuegung:\n";
+  }
+
+  out +="  Abk.    Adverb\n  ======  ======\n";
+
+  if ( sizeof(adverb_list) == 0) 
+    out += " keine.\n";
+  else
+    // Ueber alle Elemente der indizies der Adverbienliste gehen
+   foreach ( s : sort_array(m_indices(adverb_list), #'> ) ) {
+      out += break_string(adverb_list[s],78,
+                          sprintf("  %-6s  ",s),BS_INDENT_ONCE);
+    }
+
+  More(out+"\nWie diese Adverbien benutzt werden ist in <hilfe adverb> "
+    "beschrieben.\n");
+
+  return 1;
+}
+
+varargs static int
+SoulComm(string str, string _verb)  {
+  int t_g,t_n,flag;
+  string str1,str2,str3,*taenze,vb;
+  out_sel=out_vic=out_oth=who=0;
+//  if (this_interactive()!=ME) return 0;
+  if (interactive(ME)) str=_unparsed_args(); // NPCs haben das nicht :(
+  if (str=="") str=0;
+  vb=_verb||query_verb();
+  if (sizeof(vb)>1 && vb[<1]=='e' && vb!="noe") vb=vb[0..<2];
+  sfoo = 0;
+  switch (vb)  {
+    /**************** Aechzen ***************/
+    case "aechz":
+    HELPCHECK("aechz");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Aechze wie?\n");
+    out_sel="Du aechzt@@adverb@@.";
+    out_oth="@@gname@@ aechzt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Anschmiegen ***************/
+    case "schmieg":
+    HELPCHECK("schmieg");
+    ParseRest(str);
+    if (!who)
+      Return("An wen willst Du Dich anschmiegen?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Das geht doch nicht.",
+      "Nein, das macht keinen Spass. Lebt ja nicht mal."))
+        return 1;
+    out_sel="Du schmiegst Dich@@adverb@@ an@@ wen@@ an.";
+    out_vic="@@gname@@ schmiegt sich@@adverb@@ an Dich.";
+    out_oth="@@gname@@ schmiegt sich@@adverb@@ an@@ wen@@ an.";
+    return FeelIt();
+
+    /**************** Antworten ***************/
+    case "antwort":
+    HELPCHECK("antwort");
+    if (!str)
+      Return("Antworte [WEM] WAS?\n");
+    ParseRest(str);
+    if (!who)
+      str1=capitalize(str);
+    else
+      if (sscanf(str,"%s %s",str1,str1)!=2)
+        Return("Antworte was?\n");
+      else
+        str1=capitalize(str1);
+    out_sel="Du antwortest@@ wem@@: "+str1;
+    /* Sprachflueche beruecksichtigen -Wargon, 8. 9. 95 */
+    if (QueryProp(P_PERM_STRING))
+      str1 = call_other(QueryProp(P_PERM_STRING),"permutate_string",str1)||"";
+    if (who) out_vic="@@gname@@ antwortet Dir: "+str1;
+    out_oth="@@gname@@ antwortet@@ wem@@: "+str1;
+    return FeelIt();
+
+    /**************** Applaudieren ***************/
+    case "applaudier":
+    HELPCHECK("applaudier");
+    GHOSTCHECK("Deine Haende fahren durcheinander durch - war wohl nix.\n",
+      gname()+" will applaudieren, aber "+QPP(FEMALE,WER,PLURAL)
+        +" Haende sausen\ndurcheinander durch.\n", 0);
+    if (!str)  {
+      out_sel="Du applaudierst von ganzem Herzen.";
+      out_oth="@@name@@ gibt eine Runde Applaus.";
+    }
+    else  {
+      ParseRest(str);
+      if (for_all)  {
+        out_sel="Du applaudierst @@alle@@@@adverb@@.";
+        out_vic="@@name@@ applaudiert @@alle@@@@adverb@@.";
+        return MixedOut(WEM);
+      }
+      if (!who && !adverb)
+        Return("Applaudiere wem oder wie oder so aehnlich.\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Nein, das gehoert sich nicht.",
+        "Sachen wird hier nicht applaudiert, OK?"))
+          return 1;
+      out_sel="Du applaudierst@@ wem@@@@adverb@@.";
+      if (who) out_vic="@@name@@ applaudiert Dir@@adverb@@.";
+      out_oth="@@name@@ applaudiert@@ wem@@@@adverb@@.";
+    }
+    return FeelIt();
+
+    /**************** Argln ***************/
+    case "argl":
+    HELPCHECK("argl");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Argle wie?\n");
+    out_sel="Du arglst"+(adverb ? "@@adverb@@." : " ein wenig vor Dich hin.");
+    out_oth="@@gname@@ arglt"
+      +(adverb ? "@@adverb@@." : " ein wenig vor sich hin.");
+    return FeelIt();
+
+    /**************** Aufatmen ***************/
+    case "atm":
+    HELPCHECK("atm");
+    if (!str || sscanf(str,"%sauf",str1)!=1)
+      Return("Atme wie auf?\n");
+    ParseRest(str1);
+    out_sel="Du atmest"+(adverb ? "@@adverb@@" : " erleichtert")+" auf.";
+    out_oth="@@gname@@ atmet"+(adverb ? "@@adverb@@" : " erleichtert")+" auf.";
+    return FeelIt();
+
+    /**************** Begruessen ***************/
+    case "hallo":
+    case "hi":
+    case "begruess":
+    HELPCHECK("begruess");
+    ParseRemote(str);
+    if (!who)
+      Return("Wen willst Du begruessen?\n");
+    if (present(who, environment()))  {
+      out_sel="Du heisst @@wen@@@@adverb@@ willkommen.";
+      out_vic="@@gname@@ heisst Dich@@adverb@@ willkommen.";
+      out_oth="@@gname@@ heisst @@wen@@@@adverb@@ willkommen.";
+    }
+    else  {
+      out_sel="Du heisst @@wen@@@@adverb@@ aus der Ferne willkommen.";
+      out_vic="@@gname@@ heisst Dich@@adverb@@ aus der Ferne willkommen.";
+    }
+    return FeelIt();
+
+    /**************** Betasten ***************/
+    case "betast":
+    HELPCHECK("betast");
+    ParseRest(str);
+    if (!who)
+      Return("Begrabsche wen?\n");
+    out_sel="Du grabbelst@@adverb@@ an "+who->name(WEM)+" herum.";
+    out_vic="@@gname@@ grabbelt@@adverb@@ an Dir herum.";
+    out_oth="@@gname@@ grabbelt@@adverb@@ an "+who->name(WEM)+" herum.";
+    return FeelIt();
+
+    /**************** Bewundern ***************/
+    case "bewunder":
+    HELPCHECK("bewunder");
+    ParseRest(str);
+    if (!who)
+      Return("Bewundere wen?\n");
+    out_sel="Du bewunderst @@wen@@@@adverb@@.";
+    out_vic="@@gname@@ bewundert Dich@@adverb@@.";
+    out_oth="@@gname@@ bewundert @@wen@@@@adverb@@.";
+    return FeelIt();
+
+    /**************** Bibbern ***************/
+    case "bibber":
+    HELPCHECK("bibber");
+    if (ghost())
+      Return("Als Geist fuehlst Du keine Kaelte.\n");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Bibbere wie?\n");
+    out_sel="Du bibberst@@adverb@@ vor Kaelte.";
+    out_oth="@@name@@ bibbert@@adverb@@ vor Kaelte.";
+    return FeelIt();
+
+    /**************** Bohre Nase ***************/
+    case "bohr":
+    HELPCHECK("bohr");
+    ParseRest(str, ({"nase","in der nase","in nase"}));
+    if (str && str!="nase" && str!="in nase" && str!="in der nase" && !adverb)
+      Return("Bohre wie Nase?\n");
+    out_sel="Du bohrst@@adverb@@ in Deiner Nase.";
+    out_oth="@@gname@@ bohrt@@adverb@@ in der Nase.     Igitt! :)";
+    return FeelIt();
+
+    /**************** Brummeln ***************/
+    case "brummel":
+    HELPCHECK("brummel");
+    ParseRest(str);
+    out_sel="Du brummelst"
+      +(adverb ? "@@adverb@@." : (str ? " kaum verstaendlich: "+str+"." : "."));
+    out_oth="@@gname@@ brummelt"
+      +(adverb ? "@@adverb@@." : (str ? " kaum verstaendlich: "+str+"." : "."));
+    return FeelIt();
+
+    /**************** cls ***************/
+    case "cls":
+    HELPCHECK("cls");
+    write("");
+    return 1;
+
+    /**************** Daeumchendrehen ***************/
+    case "dreh":
+    HELPCHECK("dreh");
+    if (!str)
+      Return("Drehe was?\n");
+    if(strstr(str,"daeumchen")<0 && strstr(str,"daumen")<0)
+      Return("Drehe was?\n");
+    ParseRest(str,({"daeumchen","daumen"}));
+    out_sel="Du drehst@@adverb@@ Daeumchen.";
+    out_oth="@@gname@@ dreht@@adverb@@ Daeumchen.";
+    return FeelIt();
+
+    /**************** Danken ***************/
+    case "dank":
+    HELPCHECK("dank");
+    ParseRest(str);
+    if (!who)
+      Return("Bei wem willst Du Dich bedanken?\n");
+    if (CheckLife(NOT_SELF, NOT_DEAD,
+      "Leidest Du jetzt schon an Persoenlickeitsspaltung? Ne, ne...",
+      "Keine Reaktion. Ist wohl befriedigender, sich bei Lebewesen zu "
+        +"bedanken."))
+        return 1;
+    out_sel="Du bedankst Dich@@adverb@@ bei@@ wem@@.";
+    out_vic="@@gname@@ bedankt sich@@adverb@@ bei Dir.";
+    out_oth="@@gname@@ bedankt sich@@adverb@@ bei@@ wem@@.";
+    return FeelIt();
+
+    /**************** Denken ***************/
+    case "denk":
+    HELPCHECK("denk");
+    if (ghost())
+      Return("Womit willst Du denn denken? Du hast keine grauen Zellen...\n");
+//    ParseRest(str);
+    str2=old_explode(str||""," ")[0];
+    if (str
+    && (!adverb||((QueryStdAdverbs()[str2]||plr_adverbs[str2]))!=adverb))  {
+      out_sel="Du denkst   . o O ("+str+")";
+      out_oth="@@name@@ denkt   . o O ("+str+")";
+      out_vic="@@name@@ denkt   . o O ("+str+")";
+    }
+    else  {
+      out_sel="Du faengst@@adverb@@ an zu denken.\nKleine "
+        +"Rauchwoelkchen steigen auf...";
+      out_oth="@@name@@ faengt@@adverb@@ an zu denken.\nKleine "
+        +"Rauchwoelkchen steigen auf...";
+    }
+    return FeelIt();
+
+    /**************** Deuten ***************/
+    case "deut":
+    HELPCHECK("deut");
+    ParseRest(str,"auf");
+    if (for_all)  {
+      out_sel="Du deutest@@adverb@@ auf @@alle@@.";
+      out_vic="@@gname@@ deutet@@adverb@@ auf @@alle@@.";
+      return MixedOut(WEN);
+    }
+    if (!who)
+      Return("Auf wen oder was willst Du deuten?\n");
+    out_sel="Du deutest@@adverb@@ auf"
+      +(who==this_object()?" Dich." : "@@ wen@@.");
+    if (who != this_object()) out_vic="@@gname@@ deutet@@adverb@@ auf Dich.";
+    out_oth="@@gname@@ deutet@@adverb@@ auf"
+      +(who==this_object() ? " sich selber.": "@@ wen@@.");
+    return FeelIt();
+
+    /**************** Druecken ***************/
+    case "drueck":
+    HELPCHECK("drueck");
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du drueckst @@alle@@"+(adverb ? "@@adverb@@" : " zaertlich")
+        +" an Dich.";
+      out_vic="@@gname@@ drueckt @@alle@@"+
+        (adverb ? "@@adverb@@" : " zaertlich")+" an sich.";
+      return MixedOut(WEN);
+    }
+    if (!who)
+      Return("Wen willst Du denn druecken?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Das macht doch keinen Spass.",
+      "Ich druecke nur jemanden, nicht etwas."))
+        return 1;
+    GHOSTCHECK("Du willst "+who->name(WEN)+" an Dich druecken - nur hast Du "
+        +"schon\nwieder nicht daran gedacht, dass so was als Geist nicht "
+        +"geht.\n",
+      gname()+" will "+who->name(WEN)+" an sich druecken - hat aber\n"
+        +"mal wieder nicht an die Nachteile des Geisterdaseins gedacht.\n",
+      gname()+" will Dich an sich druecken - hat aber mal wieder\n"
+        +"nicht an die Nachteile des Geisterdaseins gedacht.\n");
+    out_sel="Du drueckst @@wen@@"+(adverb ? "@@adverb@@" : " zaertlich")
+      +" an Dich.";
+    out_vic="@@name@@ drueckt Dich"+(adverb ? "@@adverb@@" : " zaertlich")
+      +" an sich.";
+    out_oth="@@name@@ drueckt @@wen@@"+(adverb ? "@@adverb@@" : " zaertlich")
+      +" an sich.";
+    return FeelIt();
+
+    /**************** Entschuldige ***************/
+    case "entschuldig":
+    HELPCHECK("entschuldig");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Entschuldige Dich wie oder bei wem?\n");
+    out_sel="Du bittest"+(who ? " @@wen@@" : "")
+      +"@@adverb@@ um Entschuldigung.";
+    if (who) out_vic="@@gname@@ bittet Dich@@adverb@@ um Entschuldigung.";
+    out_oth="@@gname@@ bittet"+(who ? " @@wen@@" : "")
+      +"@@adverb@@ um Entschuldigung.";
+    return FeelIt();
+
+    /**************** Erbleichen ***************/
+    case "erbleich":
+    HELPCHECK("erbleich");
+    GHOSTCHECK("Ich weiss zwar nicht, wie Du das schaffst, aber Du wirst "
+        +"noch bleicher.\n",
+      break_string("Wie unwahrscheinlich das auch ist, aber "+gname()
+        +" schafft es tatsaechlich, noch bleicher zu werden.",78), 0 );
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Wie willst Du erbleichen?\n");
+    out_sel="Du erbleichst@@adverb@@.";
+    out_oth="@@name@@ erbleicht@@adverb@@.";
+    return FeelIt();
+
+    /**************** Erroeten ***************/
+    case "erroet":
+    HELPCHECK("erroet");
+    GHOSTCHECK("Du schaffst es nur bis zu einem blassen Rosa, aber immerhin.\n",
+      "Die Wangen des Geistes von "+capname()+" werden leicht rosa.\n", 0);
+    ParseRest(str);
+    if (!adverb && str)
+      Return("Erroete wie?\n");
+    out_sel="Deine Wangen gluehen@@adverb@@.";
+    out_oth="@@name@@ erroetet@@adverb@@.";
+    return FeelIt();
+
+    /**************** Erschrecken ***************/
+    case "erschreck":
+    case "erschrick":
+    if (!ghost())
+      Return("Du bist zu harmlos, Geist muesste man sein...\n");
+    HELPCHECK("erschreck");
+    ParseRest(str);
+    if (!who)
+      Return("Wen willst Du denn erschrecken?\n");
+    out_sel="Mit einem lauten BUH! erschreckst Du @@wen@@"
+      +(adverb ? "@@adverb@@." : " fuerchterlich.");
+    out_vic="BUH! Du zuckst vor Schreck zusammen. Muss dieser Geist von "
+      +"@@gname@@ Dich auch@@adverb@@ erschrecken.";
+    out_oth="BUH! @@gname@@ erschreckt @@wen@@"
+      +(adverb ? "@@adverb@@." : " fuerchterlich.");
+    return FeelIt();
+
+    /**************** Flippen ***************/
+    case "flipp":
+    HELPCHECK("flipp");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Ausflippen wollen wir also, so so. Und wie, wenn ich "
+        +"fragen darf?\n");
+    out_sel="Du flippst"+(adverb ? "@@adverb@@ aus." : " total aus.");
+    out_oth="@@gname@@ flippt"+(adverb ? "@@adverb@@ aus." : " total aus.");
+    return FeelIt();
+
+    /**************** Fluchen ***************/
+    case "fluch":
+    HELPCHECK("fluch");
+    GHOSTCHECK("Du faengst mangels Resonanzkoerper leise an zu fluchen.\n",
+      gname()+" faengt leise an zu fluchen. Laut kann er nicht,\n"
+        +"mangels Luft und Resonanzkoerper.\n", 0);
+    if (!str)  {
+      out_sel="Du fluchst lautstark.";
+      out_oth="@@name@@ faengt an, fuerchterlich zu fluchen.";
+    }
+    else  {
+      ParseRest(str);
+      if (!adverb)
+        Return("Wie willst Du fluchen?\n");
+      out_sel="Du fluchst@@adverb@@.";
+      out_oth="@@name@@ flucht auf einmal@@adverb@@.";
+    }
+    return FeelIt();
+
+    /**************** Fragen ***************/
+    case "frag":
+    HELPCHECK("frag");
+    if (!str)
+      Return("Frage wen was?\n");
+    ParseRest(str);
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Du faengst zu gruebeln an...",
+      "Frage jemand, der lebt."))
+        return 1;
+    if (who)
+      sscanf(str,"%s %s", str1,str1);
+    else
+      str1=str;
+    if (!str1)
+      Return("Frage "+who->name(WEN)+" WAS?\n");
+    str1=capitalize(str1);
+    if (str1[<1] != '?')
+      str1 += "?";
+    out_sel="Du fragst@@ wen@@: "+str1;
+    /* Sprachfluch beruecksichtigen -Wargon, 8. 9. 95 */
+    if (objectp(QueryProp(P_PERM_STRING)))
+      str1 = call_other(QueryProp(P_PERM_STRING), "permutate_string", str1)||"";
+    if (who) out_vic=(ghost() ? "Der Geist von " : /* IS_LEARNER(ME) ?
+      QueryProp(P_PRESAY)||"" : */ "")+capname()+" fragt Dich: "+str1;
+    out_oth=(ghost() ? "Der Geist von " : /* IS_LEARNER(ME) ?
+      QueryProp(P_PRESAY)||"" : */ "")+capname()+" fragt@@ wen@@: "+str1;
+    return FeelIt();
+
+    /**************** Freuen ***************/
+    case "freu":
+    HELPCHECK("freu");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Freue Dich wie?\n");
+    out_sel="Du "+(who ? "grinst @@wen@@ an und " : "")
+      +"freust Dich@@adverb@@.";
+    if (who) out_vic="@@gname@@ grinst Dich an und freut sich@@adverb@@.";
+    out_oth="@@gname@@ "+(who ? "grinst @@wen@@ an und " : "")
+      +"freut sich@@adverb@@.";
+    return FeelIt();
+
+    /**************** Furzen ***************/
+    case "furz":
+    HELPCHECK("furz");
+    GHOSTCHECK("Du laesst einen fahren - aber er riecht nach gar nix.\n",
+      gname()+" laesst einen fahren. Man riecht aber nix.\n", 0);
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Furze wie?\n");
+    out_sel="Du furzt"+(adverb ? "@@adverb@@." : " hemmungslos.");
+    out_oth="@@name@@ laesst@@adverb@@ einen Stinkefurz fahren.";
+    ofoo=clone_object("/items/furz");
+    ofoo->set_furzer(this_player());
+    ofoo->move(environment(this_player()));
+    return FeelIt();
+// DEBUG Furz testen!
+
+    /**************** Gaehnen ***************/
+    case "gaehn":
+    HELPCHECK("gaehn");
+    if (ghost())
+      Return("Als Geist wirst Du nicht muede - also nicht gaehnen.\n");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Wie willst Du gaehnen?\n");
+    if (!adverb)
+      out_sel="Kannst Du aber Dein(en) Mund/Maul/Schnabel weit aufreissen!";
+    else
+      out_sel="Du gaehnst@@adverb@@.";
+    out_oth="@@gname@@ gaehnt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Glucksen ***************/
+    case "glucks":
+    HELPCHECK("glucks");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Gluckse wie?\n");
+    out_sel="Du gluckst"+(adverb ? "@@adverb@@." : " wie ein Huhn.");
+    out_oth="@@gname@@ gluckst"+(adverb ? "@@adverb@@." : " wie ein Huhn.");
+    return FeelIt();
+
+    /**************** Gratulieren ***************/
+    case "gratulier":
+    case "beglueckwuensch":
+    HELPCHECK("gratulier");
+    ParseRest(str);
+    if (!who)
+      Return("Wem willst Du gratulieren?\n");
+    if (CheckLife(NOT_SELF, NOT_DEAD,
+      "Na, meinst Du nicht, dass Eigenlob stinkt?",
+      "Soll ich dem Ding vielleicht zum Totsein gratulieren? Nee nee."))
+        return 1;
+    out_sel="Du gratulierst @@wem@@@@adverb@@.";
+    out_vic="@@gname@@ gratuliert Dir@@adverb@@.";
+    out_oth="@@gname@@ gratuliert @@wem@@@@adverb@@.";
+    return FeelIt();
+
+    /**************** Grinsen ***************/
+    case "grins":
+    HELPCHECK("grins");
+    GHOSTCHECK("Als Du grinst, siehst Du regelrecht, wie die anderen eine "
+        +"Gaensehaut bekommen.\n",
+      "Du bekommst eine Gaensehaut, als der Geist von "+capname()
+        +" zu grinsen anfaengt.\n", 0);
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du grinst @@alle@@@@adverb@@ an.";
+      out_vic="@@name@@ grinst @@alle@@@@adverb@@ an.";
+      return MixedOut(WEN);
+    }
+    if (!who && !adverb && str)
+      Return("Grinsen - schoen und gut. Aber wen oder wie (an)grinsen?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Du grinst Dir was, aber so, dass es kein anderer sieht.",
+      "Nicht mal einen Spiegel darf man hier angrinsen, nur Lebewesen!"))
+        return 1;
+    out_sel="Du grinst@@ wen@@@@adverb@@"+(who ? " an" : "")+".";
+    if (who) out_vic="@@name@@ grinst Dich@@adverb@@ an.";
+    out_oth="@@name@@ grinst@@ wen@@@@adverb@@"+(who ? " an" : "")+".";
+    return FeelIt();
+
+    /**************** Gruebeln ***************/
+    case "gruebel":
+    case "gruebl":
+    HELPCHECK("gruebel");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Grueble wie?\n");
+    out_sel="Du gruebelst@@adverb@@ eine Weile vor Dich hin.";
+    out_oth="@@gname@@ gruebelt@@adverb@@ eine Weile vor sich hin.";
+    return FeelIt();
+
+    /**************** Grummeln ***************/
+    case "grummel":
+    case "grumml":
+    HELPCHECK("grummel");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Grummle wie?\n");
+    out_sel="Du grummelst@@adverb@@.";
+    out_oth="@@gname@@ grummelt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Grunzen ***************/
+    case "grunz":
+    HELPCHECK("grunz");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Grunze wie?\n");
+    out_sel="Du grunzt@@adverb@@.";
+    out_oth="@@gname@@ grunzt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Gucken ***************/
+    case "guck":
+    HELPCHECK("guck");
+    ParseRest(str);
+    if (!adverb)
+      Return("Gucke wie aus der Waesche?\n");
+    out_sel="Du guckst@@adverb@@ aus der Waesche.";
+    out_oth="@@gname@@ guckt@@adverb@@ aus der Waesche.";
+    return FeelIt();
+
+    /**************** Jammern ***************/
+    case "jammer":
+    HELPCHECK("jammer");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Wie willst Du jammern?\n");
+    out_sel="Du jammerst@@adverb@@.";
+    out_oth="@@gname@@ jammert@@adverb@@.";
+    return FeelIt();
+
+    /**************** Haetscheln ***************/
+    case "haetschel":
+    case "haetschl":
+    HELPCHECK("haetschel");
+    GHOSTCHECK("Du ueberlegst es Dir anders - mit Deinen durchlaessigen "
+        +"Haenden...",
+      gname()+" will anscheinend jemand haetscheln, ueberlegt\n"
+        +"es sich nach einem kurzen Blick auf seine Haende anders.\n", 0);
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du haetschelst @@alle@@@@adverb@@.";
+      out_vic="@@name@@ haetschelt @@alle@@@@adverb@@.";
+      return MixedOut(WEN);
+    }
+    if (!str || !who)
+      Return("Wen willst Du haetscheln?\n");
+    if (who && CheckLife(NOT_SELF, NOT_DEAD,
+      "Das sieht viel zu albern aus - Du laesst es bleiben.",
+      "Ist da ueberhaupt was zu haetscheln? Nein, da lebt doch nix."))
+        return 1;
+    out_sel="Du haetschelst@@ wen@@@@adverb@@.";
+    out_vic="@@name@@ haetschelt Dich@@adverb@@.";
+    out_oth="@@name@@ haetschelt@@ wen@@@@adverb@@.";
+    return FeelIt();
+
+    /**************** Hicksen ***************/
+    case "hicks":
+    HELPCHECK("hicks");
+    GHOSTCHECK("Hoppla! Dieser Hickser zieht Dich ganz schoen zusammen!\n",
+      gname()+" hat anscheinend Schluckauf.\n"
+        +"Und was fuer einen! Fuer einen Moment zieht es "+QueryPronoun(WEN)
+        +" ziemlich zusammen.\n", 0);
+    if (!str)  {
+      out_sel="Hicks!";
+      out_oth="@@name@@ muss hicksen. Wahrscheinlich zu viel Alkohol...";
+    }
+    else  {
+      ParseRest(str);
+      if (!adverb)
+        Return("Hickse wie?\n");
+      out_sel="Du hickst@@adverb@@.";
+      out_oth="@@name@@ hickst@@adverb@@.";
+    }
+    return FeelIt();
+
+    /**************** Huepfen ***************/
+    case "huepf":
+    HELPCHECK("huepf");
+    GHOSTCHECK("Du schwebst durch die Gegend.\n",
+      gname()+" schwebt durch die Gegend.\n", 0);
+    if (!str)  {
+      out_sel="B O I N G !! Du huepfst in der Gegend herum.";
+      out_oth="@@name@@ huepft in der Gegend herum.";
+    }
+    else  {
+      ParseRest(str);
+      if (!who && !adverb)
+        Return("Huepfe wie oder um wen oder wie oder was oder haeh?\n");
+      out_sel="Du huepfst@@adverb@@"+(who ? " um@@ wen@@" : "")+" herum.";
+      if (who) out_vic="@@name@@ huepft@@adverb@@ um Dich herum.";
+      out_oth="@@name@@ huepft@@adverb@@"+(who ? " um@@ wen@@" : "")+" herum.";
+    }
+    return FeelIt();
+
+    /**************** Husten ***************/
+    case "hust":
+    HELPCHECK("hust");
+    GHOSTCHECK("Du verstreust ein paar Geisterbazillen im Raum.\n",
+      gname()+" macht ufff, ufff und verteilt ein paar Geister-\n"
+        +"bazillen im Raum.\n", 0);
+    if (!str)  {
+      out_sel="Hust! Keuch! Halt dir doch wenigstens die Hand vor den Mund!";
+      out_oth="@@name@@ hustet sich fast die Seele aus dem Leib.";
+    }
+    else  {
+      ParseRest(str);
+      if (!who && !adverb)
+        Return("Wenn Du schon was hinter huste tippst, dann bitte was "
+          +"vernuenftiges!\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Dir selber koennen nur andere was husten.",
+        "Bitte huste nur Lebewesen was."))
+          return 1;
+      out_sel="Du hustest@@ wem@@@@adverb@@"+(who? " was" : "")+".";
+      if (who) out_vic="@@name@@ hustet Dir@@adverb@@was.";
+      out_oth="@@name@@ hustet@@ wem@@@@adverb@@"+(who? " was" : "")+".";
+    }
+    return FeelIt();
+
+    /**************** Jubeln ***************/
+    case "jubel":
+    case "jubl":
+    HELPCHECK("jubel");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Juble wie? Oder wem zu?\n");
+    out_sel="Du jubelst@@ wem@@@@adverb@@"+(who ? " zu." : ".");
+    if (who) out_vic="@@gname@@ jubelt Dir@@adverb@@ zu.";
+    out_oth="@@gname@@ jubelt@@ wem@@@@adverb@@"+(who ? " zu." : ".");
+    return FeelIt();
+
+    /**************** Keuchen ***************/
+    case "keuch":
+    HELPCHECK("keuch");
+    if (ghost())
+      Return("Als Geist strengt Dich nix an - also wird auch nicht "
+        +"gekeucht.\n");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Keuche wie?\n");
+    out_sel="Du keuchst"+(adverb ? "@@adverb@@." : " vor Anstrengung.");
+    out_oth="@@name@@ keucht"+(adverb ? "@@adverb@@." : " vor Anstrengung.");
+    return FeelIt();
+
+    /**************** Kichern ***************/
+    case "kicher":
+    HELPCHECK("kicher");
+    if (!str)  {
+      out_sel="Du kicherst. (Wie albern von Dir)";
+      out_oth="@@gname@@ gibt ein albernes Kichern von sich.";
+    }
+    else  {
+      ParseRest(str);
+      if (!who && !adverb)
+        Return("Das haut so nicht hin, gib vernuenftige Parameter.\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "In diesem Fall nimm bitte nur kicher.",
+        "Musst schon etwas Lebendes angeben, nichts Totes."))
+          return 1;
+      out_sel="Du kicherst@@adverb@@"+(who ? " hinter "+who->name(WESSEN)+
+        " Ruecken." : ".");
+      if (who)  out_vic="Jemand kichert@@adverb@@ hinter deinem Ruecken.";
+      out_oth="@@gname@@ kichert@@adverb@@"+(who ? " hinter "+who->name(WESSEN)
+        +" Ruecken." : ".");
+    }
+    return FeelIt();
+
+    /**************** Kitzeln ***************/
+    case "kitzel":
+    case "kitzl":
+    HELPCHECK("kitzel");
+    GHOSTCHECK("Mit Deinen immateriellen Fingern schaffst Du das nicht.\n",
+      gname()+" muss gerade feststellen, dass man mit\n"
+        +"immateriellen Fingern nicht kitzeln kann.\n", 0);
+    ParseRest(str);
+    if (!who)
+      Return("Wen willst Du kitzeln?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Du bist doch kein Masochist! Du laesst es bleiben.",
+      "Dinge sind so selten kitzlig. Lass es bleiben."))
+        return 1;
+    if (member(({"highlander","hobo"}), who->query_real_name())>-1)
+      switch (who->query_real_name())  {
+        case "highlander": str1="unter"; str2="Federn"; break;
+        case "hobo"      : str1="an";    str2="Kinn"; break;
+      }
+    else if (who->QueryProp(P_RACE))
+      switch (lower_case(who->QueryProp(P_RACE)))  {
+        case "drache" : str1="unter";str2="Schuppen";
+          t_g=FEMALE; t_n=PLURAL; break;
+        case "greif"  : str1="unter";str2="Federn";
+          t_g=FEMALE; t_n=PLURAL; break;
+        default       : str1="an"; str2="Kinn"; t_g=NEUTER; t_n=SINGULAR;
+      }
+    else  {
+      str1="an"; str2="Kinn"; t_g=NEUTER; t_n=SINGULAR;
+    }
+    if (getuid(who)=="trest" || getuid(who)=="woelkchen")  {
+      str1="an"; str2="Fuessen"; t_g=MALE; t_n=PLURAL;
+    }
+    out_sel="Du kitzelst@@ wen@@@@adverb@@ "+str1+" "+who->QPP(t_g,WEM,t_n)
+      +" "+str2+".\n@@wer@@ versucht, sich zu beherrschen, muss aber "
+      +"doch lachen.";
+    out_vic="@@name@@ kitzelt Dich@@adverb@@ "+str1+" Deine"
+      +(t_n ? "n" : (t_g==FEMALE ? "r" : "m"))+" "+str2
+      +".\nDu versuchst, Dich zu beherrschen, musst aber doch lachen.";
+    out_oth="@@name@@ kitzelt@@ wen@@@@adverb@@ "+str1+" "
+      +who->QPP(t_g,WEM,t_n)+" "+str2
+      +".\n@@wer@@ versucht, sich zu beherrschen, muss aber doch lachen.";
+    return FeelIt();
+
+    /**************** Klatschen ***************/
+    case "klatsch":
+    HELPCHECK("klatsch");
+    GHOSTCHECK("Deine Haende sausen durcheinander durch.\n",
+      gname()+" will in die Haende klatschen - aber sie\n"
+        +"sausen durcheinander durch.\n", 0);
+    ParseRest(str);
+    if (!adverb && str)
+      Return("Klatsche wie?\n");
+    out_sel="Du klatschst@@adverb@@ in die Haende.";
+    out_oth="@@name@@ klatscht@@adverb@@ in die Haende.";
+    return FeelIt();
+
+    /**************** Klopfen ***************/
+    case "klopf":
+    HELPCHECK("klopf");
+    if (!str||sscanf(str,"%s auf schulter",sfoo)!=1)
+      if (!str||sscanf(str,"%s auf die schulter",sfoo)!=1)
+        Return("Klopfe wie wem wieso was?\n");
+    if (ghost())
+      Return("Das geht leider nicht mit durchlaessigen Haenden.\n");
+    str=sfoo;
+    if (str=="") str=0;
+    ParseRest(str);
+    if (!who)
+      Return("Klopfe wem auf die Schulter?\n");
+    out_sel="Du klopfst @@wem@@@@adverb@@ auf die Schulter.";
+    out_vic="@@name@@ klopft Dir@@adverb@@ auf die Schulter.";
+    out_oth="@@name@@ klopft @@wem@@@@adverb@@ auf die Schulter.";
+    return FeelIt();
+
+    /**************** Knabbern ***************/
+    case "knabber":
+    HELPCHECK("knabber");
+    if (ghost())
+      Return("Sorry, aber dafuer fehlt Dir im Moment der noetige "
+        +"\"Biss\"...\n");
+    ParseRest(str);
+    if (!who)
+      Return("Knabbere wen an?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Du kommst nicht an dein eigenes Ohr ran...",
+      "Noe, noe, das schmeckt bestimmt nicht gut."))
+        return 1;
+    out_sel="Du knabberst@@adverb@@ an "+who->name(WESSEN)+" Ohr.";
+    out_vic="@@name@@ knabbert@@adverb@@ an Deinem Ohr.";
+    out_oth="@@name@@ knabbert@@adverb@@ an "+who->name(WESSEN)+" Ohr.";
+    return FeelIt();
+
+    /**************** Knicksen ***************/
+    case "knicks":
+    HELPCHECK("knicks");
+    GHOSTCHECK("Du knickst in der Mitte ab, kriegst Dich aber schnell wieder "
+        +"zusammen.\n",
+      gname()+" knick(s)t in der Mitte ab, kriegt sich aber\n"
+        +"zum Glueck schnell wieder zusammen.\n", 0);
+    if (!str) {
+      out_sel="Du machst einen anmutigen Knicks.";
+      out_oth="@@name@@ macht einen anmutigen Knicks.";
+    }
+    else  {
+      ParseRest(str,"vor");
+      if (for_all)  {
+        out_sel="Du knickst@@adverb@@ vor @@alle@@.";
+        out_vic="@@name@@ knickst@@adverb@@ vor @@alle@@.";
+        return MixedOut(WEM);
+      }
+      if (!who && !adverb)
+        Return("Knickse irgendwie oder vor jemandem.\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Wie willst Du das denn schaffen?",
+        "Vor Sachen wird hier nicht geknickst!"))
+          return 1;
+      out_sel="Du knickst@@adverb@@"+(who ? " vor" : "")+"@@ wem@@.";
+      if (who ) out_vic="@@name@@ knickst@@adverb@@ vor Dir.";
+      out_oth="@@name@@ knickst@@adverb@@"+(who ? " vor" : "")+"@@ wem@@.";
+    }
+    return FeelIt();
+
+    /**************** Knirschen ***************/
+    case "knirsch":
+    HELPCHECK("knirsch");
+    if (ghost())
+      Return("Du kannst mit nichts knirschen, so als Geist. Versuche doch "
+        +"dafuer\nmal, zu rasseln...\n");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Knirsche wie?\n");
+    switch (QueryProp(P_RACE))  {
+      case "greif"  : str1="dem Schnabel."; break;
+      case "sandtiger" : str1="den Fangzaehnen."; break;
+      case "drache" : str1="den Fangzaehnen."; break;
+      default       : str1="den Zaehnen.";
+    }
+    out_sel="Du knirschst@@adverb@@ mit "+str1;
+    out_oth="@@name@@ knirscht@@adverb@@ mit "+str1;
+    return FeelIt();
+
+    /**************** Knuddeln ***************/
+    case "knuddel":
+    case "knuddl":
+    HELPCHECK("knuddel");
+    if (ghost())
+      Return("Sorry, nicht als Geist.\n");
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du knuddelst @@alle@@@@adverb@@.";
+      out_vic="@@name@@ knuddelt @@alle@@@@adverb@@.";
+      return MixedOut(WEN);
+    }
+    if (!who)
+      Return("Knuddle wen?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Das bringt doch nix, lass es halt.",
+      "Du kannst soviel ich weiss ausser Lebewesen nur Teddys knuddeln."))
+        return 1;
+    out_sel="Du knuddelst@@ wen@@@@adverb@@.";
+    out_vic="@@name@@ knuddelt Dich@@adverb@@.";
+    out_oth="@@name@@ knuddelt@@ wen@@@@adverb@@.";
+    return FeelIt();
+
+    /**************** Knurren ***************/
+    case "knurr":
+    HELPCHECK("knurr");
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du knurrst @@alle@@@@adverb@@ an.";
+      out_vic="@@gname@@ knurrt @@alle@@@@adverb@@ an.";
+      return MixedOut(WEN);
+    }
+    if (str && !who && !adverb)
+      Return("Wen anknurren oder wie knurren?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Du knurrst in Dich hinein.",
+      "Reagiert nicht. Solltest wohl besser Lebwesen anknurren."))
+        return 1;
+    out_sel="Du knurrst@@ wen@@@@adverb@@"+(who ? " an." : ".");
+    if (who) out_vic="@@gname@@ knurrt Dich@@adverb@@ an.";
+    out_oth="@@gname@@ knurrt@@ wen@@@@adverb@@"+(who ? " an." : ".");
+    return FeelIt();
+
+    /****************  Knutschen ***************/
+    case "knutsch":
+    HELPCHECK("knutsch");
+    if (ghost())
+      Return("Das kannst Du als Geist leider nicht. Irgendwie fehlt Dir "
+        +"dazu das Herz.\n");
+    ParseRest(str);
+    if (!who)
+      Return("Knutsche wen ab?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Das geht nicht.",
+      "Igitt! Lieber nicht!"))
+        return 1;
+    out_sel="Du gibst @@wem@@@@adverb@@ einen RICHTIGEN Kuss.";
+    out_vic="@@name@@ gibt Dir@@adverb@@ einen tiefen und hingebungsvollen "
+      +"Kuss.\nDu schwebst im 7. Himmel.";
+    out_oth="@@name@@ gibt @@wem@@@@adverb@@ einen tiefen und "
+      +"hingebungsvollen Kuss.";
+    return FeelIt();
+
+    /**************** Kotzen ***************/
+    case "kotz":
+    HELPCHECK("kotz");
+    if (ghost())
+      Return("Ne, das ist eins von den Sachen, die als Geist nicht gehen.\n");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Kotze wie? Kotze auf wen?\n");
+    if (who && CheckLife(NOT_SELF,0,
+      "Igitt, nein danke."))
+        return 1;
+    if(!str) {
+      out_sel="Du kotzt ueber deine Schuhe.";
+      out_oth="@@name@@ verdreht die Augen und kotzt.";
+    }
+    else  {
+      out_sel="Du kotzt@@adverb@@"+(who ? " auf @@wen@@." : ".");
+      if (who) out_vic="@@name@@ kotzt@@adverb@@ auf Dich.";
+      out_oth="@@name@@ kotzt@@adverb@@"+(who ? " auf @@wen@@." : ".");
+    }
+    return FeelIt();
+
+    /**************** Kratzen ***************/
+    case "kratz":
+    HELPCHECK("kratz");
+    ParseRest(str);
+    if (who && (who!=this_player()))
+      Return("Das mach mal schoen nur mit Dir selber.\n");
+    if (str && !adverb)
+      Return("Wie willst Du Dich kratzen?\n");
+    out_sel="Du kratzt dich@@adverb@@ am Kopp.";
+    out_oth="@@gname@@ kratzt sich@@adverb@@ am Kopp.";
+    return FeelIt();
+
+    /**************** Krieche ***************/
+    case "kriech":
+    HELPCHECK("kriech");
+    ParseRest(str);
+    out_sel="Du kriechst"+(who ? " vor @@wem@@" : "")+"@@adverb@@ im Staub.";
+    if (who) out_vic="@@gname@@ kriecht@@adverb@@ vor Dir im Staub.";
+    out_oth="@@gname@@ kriecht"+(who ? " vor @@wem@@" : "")
+      +"@@adverb@@ im Staub.";
+    return FeelIt();
+
+    /**************** Kuessen ***************/
+    case "kuess":
+    HELPCHECK("kuess");
+    if (ghost())
+      Return("Als Geist kannst Du leider niemanden kuessen.\n");
+    ParseRest(str);
+    if (!who)
+      Return("Wen willst Du kuessen?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Da hast Du aber Schwierigkeiten... Du gibst es schliesslich auf.",
+      "Nix. Absolut nix. Kuesse lieber Lebewesen - die reagieren\n"
+        +"wenigstens (und sei es, dass sie Dich fressen...)."))
+        return 1;
+    out_sel="Du kuesst@@ wen@@@@adverb@@.";
+    out_vic="@@name@@ kuesst Dich@@adverb@@.";
+    out_oth="@@name@@ kuesst@@ wen@@@@adverb@@.";
+    FeelIt();
+    if (who->QueryProp(P_FROG)&&QueryProp(P_LEVEL)>who->QueryProp(P_LEVEL)) {
+      tell_room(environment(this_player()),"PLOPP!\n");
+      write("Huch! Du wirst auf einmal so gruen und klein und kriegst auf\n"
+        +"einmal furchtbar Hunger auf Fliegen und so...\n");
+      who->Message("Auf einmal wird die Welt um Dich wieder so klein, wie sie\n"
+        +" frueher mal war - und vor Dir sitzt ein kleiner gruener Frosch.\n");
+      say(who->name(WER)+" steht auf einmal da und schaut dumm aus der "
+        +"Waesche. Dafuer fehlt\njetzt seltsamerweise "+capname()
+        +". Die Gesamtzahl an kleinen gruenen\nFroeschen im Raum hat sich "
+        +"jedoch nicht geaendert...\n",({who,this_player()}));
+      who->SetProp(P_FROG,0);
+      SetProp(P_FROG,1);
+    }
+    return 1;
+
+    /**************** Kuscheln ***************/
+    case "kuschel":
+    case "kuschl":
+    HELPCHECK("kuschel");
+    GHOSTCHECK("Dazu bist Du als Geist viel zu kalt und gar "
+        +"schroecklich anzusehen.\n",
+      gname()+" scheint Anwandlungen zu haben, sich an jemand "
+        +"ankuscheln zu wollen.\nEntsetzt springen alle zurueck, weil "
+        +"dazu ist er doch zu kalt und schroecklich\nanzusehen.\n", 0);
+    ParseRest(str);
+    if (!who)
+      Return("An wen willst Du Dich ankuscheln?\n");
+    out_sel="Du kuschelst Dich@@adverb@@ an @@wen@@ an.";
+    out_vic="@@name@@ kuschelt sich@@adverb@@ an Dich an.";
+    out_oth="@@name@@ kuschelt sich@@adverb@@ an @@wen@@ an.";
+    return FeelIt();
+
+    /**************** Lachen ***************/
+    case "lach":
+    HELPCHECK("lach");
+    GHOSTCHECK("Du lachst mit hohler Stimme. Den Umstehenden (sind doch welche"
+        +" da, oder?)\nlaeuft es kalt den Ruecken runter.\n",
+      gname()+" lacht mit hohler Stimme.\nDir laeuft es eiskalt den Ruecken"
+        +" runter.\n", 0);
+    if (!str)  {
+      out_sel="Du brichst vor Lachen zusammen.";
+      out_oth="@@name@@ bricht vor Lachen zusammen.";
+    }
+    else  {
+      ParseRest(str);
+      if (for_all)  {
+        out_sel="Du lachst@@adverb@@ ueber @@alle@@.";
+        out_vic="@@name@@ lacht@@adverb@@ ueber @@alle@@.";
+        return MixedOut(WEN);
+      }
+      if (!who && !adverb)
+        Return("Lache wie, lache ueber wen?\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Lach Dich doch nicht selber aus - das machen schon andere...",
+        "Gelacht wird nur ueber Lebewesen (die koennen sich drueber aergern)."))
+          return 1;
+      out_sel="Du lachst@@adverb@@"+(who?" ueber@@ wen@@":"")+".";
+      if (who) out_vic="@@name@@ lacht@@adverb@@ ueber Dich.";
+      out_oth="@@name@@ lacht@@adverb@@"+(who?" ueber@@ wen@@":"")+".";
+    }
+    return FeelIt();
+
+    /**************** Laecheln ***************/
+    case "laechel":
+    case "laechl":
+    HELPCHECK("laechel");
+    if (ghost()) {
+      write("Du laechelst innerlich.\n");
+      return 1;
+    }
+    if(!str) {
+      out_sel="Du laechelst.";
+      out_oth="@@name@@ laechelt.";
+    }
+    else  {
+      ParseRest(str);
+      if (for_all)  {
+        out_sel="Du laechelst @@alle@@@@adverb@@ an.";
+        out_vic="@@name@@ laechelt @@alle@@@@adverb@@ an.";
+        return MixedOut(WEN);
+      }
+      if (!who && !adverb && str)
+        Return("Wie oder wen?\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Musst schon jemand anders anlaecheln.",
+        "Bitte ein Lebewesen anlaecheln."))
+          return 1;
+      out_sel="Du laechelst@@ wen@@@@adverb@@"+(who ? " an." : ".");
+      if (who) out_vic="@@name@@ laechelt Dich@@adverb@@ an.";
+      out_oth="@@name@@ laechelt@@ wen@@@@adverb@@"+(who ? " an." : ".");
+    }
+    return FeelIt();
+
+    /**************** Liebe ***************/
+    /* These lines appear Courtesy of Angus@MorgenGrauen. So long, and thanks */
+    /* for all the fish, errr, text, Angus :) */
+    case "lieb":
+    HELPCHECK("lieb");
+    if (ghost())
+      Return("Auf diese Freuden musst Du als Geist leider verzichten.\n");
+    ParseRest(str);
+    if (!who)
+      Return("Wen hast Du lieb?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Ja, ich weiss, Du magst Dich, aber das musst Du nicht zur Schau"
+        +"stellen.",
+      "Du entwickelst seltsame Neigungen, finde ich."))
+        return 1;
+    str1=(who->QueryProp(P_GENDER)==FEMALE ? "ihr" : "ihm");
+    /* old version:
+      out_sel="Du fluesterst @@wem@@@@adverb@@ liebevolle Worte ins Ohr.";
+      out_vic=gname()+" fluestert Dir@@adverb@@ liebevolle Worte ins Ohr.";
+      out_oth=gname()+" fluestert@@adverb@@ sanfte Worte zu @@wem@@.";
+    */
+    out_sel="Du schliesst die Augen, schmiegst Dich eng an @@wen@@ und gibst"
+      +LF+str1+" einen zaertlichen und leidenschaftlichen Kuss.\n"
+      +"Um Dich herum versinkt die Welt und Du glaubst, auf Wolken zu "
+      +"schweben.";
+    out_vic="@@name@@ drueckt Dich zaertlich an sich und gibt Dir\n"
+      +"einen zaertlichen und leidenschaftlichen Kuss. Du schliesst die\n"
+      +"Augen und traeumst ein wenig......Du schwebst auf Wolken direkt\n"
+      +"in den siebten Himmel.";
+    out_oth="Du schaust dezent weg, als sich @@name@@ und "+who->name()
+      +" verliebt in die Arme\nsinken.";
+    return FeelIt();
+
+    /**************** Loben ***************/
+    case "lob":
+    HELPCHECK("lob");
+    if (!str)
+      Return("Wen oder was willst Du loben?\n");
+    ParseRest(str);
+    if (who==ME)  {
+      ofoo=clone_object("/items/furz");
+      ofoo->set_furzer(this_player());
+      ofoo->set_eigenlob();
+      ofoo->move(environment(this_player()));
+      //DEBUG Furz zum Eigenlob patchen :>
+      out_sel="Du lobst Dich selber@@adverb@@. Die Folgen kennst Du ja...";
+      out_oth="@@gname@@ lobt sich selber@@adverb@@, mit den bekannten Folgen.";
+    } else if (who) {
+      out_sel="Du lobst @@wen@@@@adverb@@.";
+      out_vic="@@gname@@ lobt Dich@@adverb@@.";
+      out_oth="@@gname@@ lobt @@wen@@@@adverb@@.";
+    } else  {
+      out_sel="Du lobst "+str+".";
+      out_oth="@@gname@@ lobt "+str+".";
+    }
+    return FeelIt();
+
+    /**************** Moppern ***************/
+    case "mopper":
+    HELPCHECK("mopper");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Moppere wie?\n");
+    out_sel="Du mopperst@@adverb@@.";
+    out_oth="@@gname@@ moppert@@adverb@@.";
+    return FeelIt();
+
+    /**************** Mustern ***************/
+    case "muster":
+    HELPCHECK("muster");
+    ParseRest(str);
+    if (!who)
+      Return("Mustere wen?\n");
+    out_sel="Du musterst @@wen@@@@adverb@@.";
+    out_vic="@@gname@@ mustert Dich@@adverb@@.";
+    out_oth="@@gname@@ mustert @@wen@@@@adverb@@.";
+    return FeelIt();
+
+    /**************** Nicken ***************/
+    case "ja":
+    case "nick":
+    HELPCHECK("nick");
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du nickst @@alle@@@@adverb@@ zu.";
+      out_vic="@@gname@@ nickt @@alle@@@@adverb@@ zu.";
+      return MixedOut(WEM);
+    }
+    if (str && !who && !adverb)
+      Return("Nicke wie oder wem zu oder wem wie zu?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Du willst Dir selber zunicken? Lieber nicht, das sieht so albern aus.",
+      "Hm. Nix passiert. Von Lebewesen bekommt man meistens mehr Feedback."))
+        return 1;
+    out_sel="Du nickst@@ wem@@@@adverb@@"
+      +(who ? " zu." : (adverb ? "." : " zustimmend."));
+    if (who) out_vic="@@gname@@ nickt Dir@@adverb@@ zu.";
+    out_oth="@@gname@@ nickt@@ wem@@@@adverb@@"
+      +(who ? " zu." : (adverb ? "." : " zustimmend."));
+    return FeelIt();
+
+    /**************** Niesen ***************/
+    case "nies":
+    HELPCHECK("nies");
+    if (ghost())
+      Return("Du hast keine Nase mehr, in der es Dich jucken koennte...\n");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Niese wie?\n");
+    out_sel="Haaaaaa-tschi! Gesundheit!"+(adverb ? " Du niest@@adverb@@." : "");
+    out_oth="Haaaaaa-tschi! @@name@@ niest@@adverb@@.";
+    return FeelIt();
+
+    /**************** Ohrfeigen ***************/
+    case "gib":
+    HELPCHECK("gib");
+    if (!str)
+      Return("Gib wem was?\n");
+    if (sscanf( str,"%s ohrfeige",str1)==0)
+      return 0;
+    ParseRest(str, ({"ohrfeige", "eine ohrfeige"}));
+    if (for_all)  {
+      out_sel="Du verpasst @@alle@@@@adverb@@ eine Ohrfeige.";
+      out_vic="@@name@@ verpasst @@alle@@@@adverb@@ eine Ohrfeige.";
+      return MixedOut(WEM);
+    }
+    if (!who)
+      Return("Gib wem eine Ohrfeige?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Dazu sind Dir Deine Backen doch zu schade.",
+      "Du wirst doch nichts schlagen, was sich nicht wehren kann?"))
+        return 1;
+    GHOSTCHECK("Deine Hand saust mit voller Wucht durch dein Opfer durch!",
+      gname()+" will "+who->name(WEM)+" eine Ohrfeige geben - aber die Hand\n"
+        +"saust mit voller Wucht durch das Opfer durch!", 0);
+    out_sel="Du verpasst @@wem@@@@adverb@@ eine schallende Ohrfeige.";
+    out_vic="@@name@@ verpasst Dir@@adverb@@ eine Watsche, dass Dir Hoeren "
+      +"und Sehen vergeht.";
+    out_oth="@@name@@ verpasst @@wem@@@@adverb@@ eine schallende Ohrfeige.";
+    return FeelIt();
+
+    /**************** Pfeifen ***************/
+    case "pfeif":
+    HELPCHECK("pfeif");
+    GHOSTCHECK("Es kommt leider nur (nicht mal heisse) Luft, aber kein "
+        +"Pfiff.\n",
+      gname()+" spitzt den Mund und pustet angestrengt. Nichts passiert.\n", 0);
+    ParseRest(str, "nach");
+    if (str && !who && !adverb)
+      Return("Pfeife wie? Pfeife wem nach? Haeh?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Was willst Du denn damit ausdruecken? Das gibt fuer mich keinen Sinn.",
+      "Ich habe keine Lust dazu."))
+        return 1;
+    out_sel="Du pfeifst@@ wen@@@@adverb@@"
+      +(who ? " nach." : (adverb ? "." : " anerkennend."));
+    if (who) out_vic="@@name@@ pfeift Dir@@adverb@@ nach.";
+    out_oth="@@name@@ pfeift@@ wen@@@@adverb@@"
+      +(who ? " nach." : (adverb ? "." :" anerkennend."));
+    return FeelIt();
+
+    /**************** Philosophieren ***************/
+    case "philosophier":
+    HELPCHECK("philosophier");
+    ParseRest(str);
+    out_sel="Du philosophierst"+(adverb ? "@@adverb@@." :
+      (str ? " ueber "+str+"." : "."));
+    out_oth="@@gname@@ philosophiert"+(adverb ? "@@adverb@@." :
+      (str ? " ueber "+str+"." : "."));
+    return FeelIt();
+
+    /**************** (Nase) Putzen ***************/
+    case "putz":
+    HELPCHECK("putz");
+    if (ghost())
+      Return("Nix da zum Putzen, so nebuloes, wie Du bist.\n");
+    ParseRest(str, ({"nase", "die nase"}));
+    if (str && str!="nase" && !adverb)
+      Return("Putze Deine Nase wie?\n");
+    out_sel="Du putzt Deine Nase@@adverb@@.";
+    out_oth="@@name@@ putzt@@adverb@@ "+QPP(FEMALE,WEN)+" Nase.";
+    return FeelIt();
+
+    /**************** Quaken ***************/
+    case "quak":
+    HELPCHECK("quak");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Quake wie?\n");
+    sfoo="";
+    flag=QueryProp(P_FROG)&&!ghost();
+    for (t_g=0; t_g<=random(flag ? 4 : 2); t_g++)  {
+      sfoo+=(flag ? " Qu" : " kw");
+      for (t_n=0; t_n<=random(flag ? 10 : 5); t_n++)
+        sfoo+="aA"[random(1)..random(1)];
+      sfoo+="k";
+    }
+    if (!flag)
+      sfoo=lower_case(sfoo);
+    else
+      sfoo+="!";
+    out_sel="Du quakst"+(adverb ? "@@adverb@@" : (flag ? " aus voller Kehle"
+      : " in etwa wie ein Frosch"))+":"+sfoo;
+    out_oth="@@gname@@ quakt"+(adverb ? "@@adverb@@" : (flag ? " aus voller Kehle"
+      : " in etwa wie ein Frosch"))+":"+sfoo;
+    return FeelIt();
+
+    /**************** Quietschen ***************/
+    case "quietsch":
+    case "quiek":
+    HELPCHECK("quiek");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Quietsche wie?\n");
+    out_sel="Du quietschst"+(adverb ? "@@adverb@@." : " vergnuegt.");
+    out_oth="@@gname@@ quietscht"+(adverb ? "@@adverb@@." : " vergnuegt.");
+    return FeelIt();
+
+    /**************** Raeuspern ***************/
+    case "raeusper":
+    HELPCHECK("raeusper");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Hm? Wie meinen?\n");
+    out_sel="Du raeusperst Dich@@adverb@@.";
+    out_oth="@@gname@@ raeuspert sich@@adverb@@.";
+    return FeelIt();
+
+    /**************** Reiben ***************/
+    case "reib":
+    HELPCHECK("reib");
+    if (ghost())
+      Return("Du hast nichts zum Reiben, aber auch gar nichts.\n");
+    if (str && (sscanf(str,"%s die Augen",sfoo)==1 || sscanf(str,"%s Augen",sfoo)==1))
+      str=sfoo;
+    else if (str && (sscanf(str,"%s die Haende",sfoo)==1
+    ||sscanf(str,"%s Haende",sfoo)==1))  {
+      flag=2;
+      str=sfoo;
+    }
+    if (str=="") str=0;
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Reibe wie die "+(flag==2 ? "Haende" : "Augen")+"?\n");
+    out_sel="Du reibst Dir"+(adverb ? "@@adverb@@"
+      : (flag==2 ? " vergnuegt" : " muede"))+" die "
+      +(flag==2 ? "Haende." : "Augen.");
+    out_oth="@@name@@ reibt sich"+(adverb ? "@@adverb@@"
+      : (flag==2 ? " vergnuegt" : " muede"))+" die "
+      +(flag==2 ? "Haende." : "Augen.");
+    return FeelIt();
+
+    /**************** Rotfln ***************/
+    case "rotfl":
+    HELPCHECK("rotfl");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Rotfl wie?\n");
+    out_sel="Du rotflst@@adverb@@.";
+    out_oth="@@gname@@ rotflt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Ruelpsen ***************/
+    case "ruelps":
+    HELPCHECK("ruelps");
+    GHOSTCHECK("Ein leichter Windhauch entfaehrt Deinem Mund, mehr nicht.\n",
+      "Dem Mund des Geistes von "+capname()
+        +" entfaehrt ein leichtes Lueftchen.\n", 0);
+    if (!str)  {
+      out_sel="BOOOOEEERRRRPP!  Entschuldige dich gefaelligst!";
+      out_oth="@@name@@ ruelpst unanstaendig.";
+    }
+    else  {
+      ParseRest(str);
+      if (!adverb)  {
+        write("Ruelpse wie (schlimm genug, dass Du Dich nicht beherrschen "
+          +"kannst!)?\n");
+        return 1;
+      }
+      out_sel="Du ruelpst@@adverb@@. Schaem Dich!";
+      out_oth="@@name@@ ruelpst@@adverb@@ und wird nicht mal rot dabei.";
+    }
+    return FeelIt();
+  }
+
+  switch (vb)  {
+    /**************** Runzeln ***************/
+    case "runzel":
+    case "runzl":
+    HELPCHECK("runzel");
+    if (ghost())
+      Return("DAS geht als Geist nun wirklich nicht.\n");
+    ParseRest(str,"stirn");
+    if (str && !adverb)
+      Return("Runzle die Stirn wie?\n");
+    out_sel="Du runzelst@@adverb@@ die Stirn.";
+    out_oth="@@name@@ runzelt@@adverb@@ die Stirn.";
+    return FeelIt();
+
+    /**************** Sabbere ***************/
+    case "sabber":
+    HELPCHECK("sabber");
+    sfoo=ghost() ? "schleim" : "sabber";
+    ParseRest(str);
+    if (str && !adverb && !who)
+      Return("Sabber wie oder wen an?\n");
+    out_sel="Du "+sfoo+"st@@ wen@@@@adverb@@ "
+      +(who ? "an." : "auf den Boden.");
+    if (who) out_vic="@@gname@@ "+sfoo+"t Dich@@adverb@@ an.";
+    out_oth="@@gname@@ "+sfoo+"t@@ wen@@@@adverb@@ "
+      +(who ? "an." : "auf den Boden.");
+    return FeelIt();
+
+    /**************** Schaemen ***************/
+    case "schaem":
+    HELPCHECK("schaem");
+    ParseRest(str);
+    if (str && !adverb && lower_case(str)!="dich")
+      Return("Schaeme Dich wie?\n");
+    out_sel="Du schaemst Dich@@adverb@@.";
+    out_oth="@@gname@@ schaemt sich@@adverb@@.";
+    return FeelIt();
+
+#ifdef SCHAU_AN
+    /**************** Schau an ***************/
+    case "schau":
+    HELPCHECK("schau");
+    if (!str || old_explode(str, " ")[sizeof(old_explode(str, " "))]!="an")
+      return 0;
+    ParseRest(str, "an");
+    if (!who)
+      Return("Schau wen an?\n");
+    out_sel="Du schaust @@wen@@@@adverb@@ an.";
+    out_vic="@@gname@@ schaut Dich@@adverb@@ an.";
+    out_oth="@@gname@@ schaut @@wen@@@@adverb@@ an.";
+    return FeelIt();
+#endif
+
+    /**************** Schluchzen ***************/
+    case "schluchz":
+    HELPCHECK("schluchz");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schluchze wie?\n");
+    out_sel="Du schluchzt"+(adverb ? "@@adverb@@." : " herzzerreissend.");
+    out_oth="@@gname@@ schluchzt"
+      +(adverb ? "@@adverb@@." : " herzzerreissend.");
+    return FeelIt();
+
+    /**************** Schmollen ***************/
+    case "schmoll":
+    HELPCHECK("schmoll");
+    GHOSTCHECK("Du schwebst beleidigt in die Ecke.\n",
+      gname()+" schwebt beleidigt in die Ecke und schmollt.\n", 0);
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schmolle wie?\n");
+    out_sel="Du schmollst@@adverb@@.";
+    out_oth="@@name@@ geht in die Ecke und schmollt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Schmunzeln ***************/
+    case "schmunzel":
+    case "schmunzl":
+    HELPCHECK("schmunzel");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schmunzle wie?\n");
+    out_sel="Du schmunzelst@@adverb@@.";
+    out_oth="@@gname@@ schmunzelt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Schnalzen ***************/
+    case "schnalz":
+    HELPCHECK("schnalz");
+    ParseRest(str, ({"zunge","mit zunge", "mit der zunge"}));
+    out_sel="Du schnalzt@@adverb@@ mit der Zunge.";
+    out_oth="@@gname@@ schnalzt@@adverb@@ mit der Zunge.";
+    return FeelIt();
+
+    /**************** Schnauben ***************/
+    case "schnaub":
+    HELPCHECK("schnaub");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schnaube wie?\n");
+    out_sel="Du schnaubst"+(adverb ? "@@adverb@@." : " entruestet.");
+    out_oth="@@gname@@ schnaubt"+(adverb ? "@@adverb@@." : " entruestet.");
+    return FeelIt();
+
+    /**************** Schnaufen ***************/
+    case "schnauf":
+    HELPCHECK("schnauf");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schnaufe wie?\n");
+    out_sel="Du schnaufst"+(adverb ? "@@adverb@@." : " vor Anstrengung.");
+    out_oth="@@gname@@ schnauft"+(adverb ? "@@adverb@@." : " vor Anstrengung.");
+    return FeelIt();
+
+    /**************** Schnippen ***************/
+    case "schnipp":
+    case "schnipps":
+    HELPCHECK("schnipp");
+    GHOSTCHECK("Du schaffst es nicht, weil die Finger durcheinander durch "
+        +"gehen.\n",
+      gname()+" versagt beim Schnippen - die Finger\ngehen durcheinander "
+        +"durch.\n", 0);
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schnippe wie?\n");
+    out_sel="Du schnippst@@adverb@@ mit deinen Fingern.";
+    out_oth="@@name@@ schnippt@@adverb@@ mit den Fingern.";
+    return FeelIt();
+
+    /**************** Schnarchen ***************/
+    case "schnarch":
+    HELPCHECK("schnarch");
+    if (ghost())
+      Return("Ich glaube, da fehlen Dir irgendwie die physischen "
+        +"Voraussetzungen dazu.\n");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schnarche wie?\n");
+    out_sel=(str ? "Zzzzzzzzzzz..." : "Du schnarchst@@adverb@@.");
+    out_oth="@@name@@ schnarcht "+(str ? "@@adverb@@." : "laut.");
+    return FeelIt();
+
+    /**************** Schniefen ***************/
+    case "snief":
+    case "schnief":
+    HELPCHECK("schnief");
+    GHOSTCHECK("Du schniefst ganz leise.\n",
+      gname()+" schnieft ganz leise.\n", 0);
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schniefe wie?\n");
+    out_sel="Du schniefst@@adverb@@.";
+    out_oth="@@name@@ schnieft@@adverb@@.";
+    return FeelIt();
+
+    /**************** Schnurren ***************/
+    case "schnurr":
+    HELPCHECK("schnurr");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Wie willst Du schnurren?\n");
+    out_sel="MMMMIIIIIAAAAAAUUUUUUUU! Du schnurrst"
+      +(adverb ? "@@adverb@@." : " zufrieden.");
+    out_oth="@@gname@@ schnurrt"+(adverb ? "@@adverb@@." : " zufrieden.");
+    return FeelIt();
+
+    /**************** Schreien ***************/
+    case "schrei":
+    HELPCHECK("schrei");
+    GHOSTCHECK("AAAAIIIIIIIIIIIEEEEEEEEEEEEEEEEEEEEEEEEEE! Ja, nur Geister "
+        +"koennen so schreien!\n",
+      gname()+" schreit - das Blut gefriert fast in deinen Ader!\n", 0);
+    if (!str)  {
+      out_sel="AUUUAAAHHHHHH!!!!";
+      out_oth="@@name@@ schreit laut!";
+    }
+    else  {
+      ParseRest(str);
+      if (!who && !adverb)
+        Return("Schreien - wie denn? Oder wen denn anschreien?\n");
+      out_sel="Du schreist@@ wen@@@@adverb@@"+(who ? " an" : "")+".";
+      if (who) out_vic="@@name@@ schreit Dich@@adverb@@ an.";
+      out_oth="@@name@@ schreit@@ wen@@@@adverb@@"+(who? " an" : "")+".";
+    }
+    return FeelIt();
+
+    /**************** Schuetteln ***************/
+    case "schuettel":
+    case "schuettl":
+    HELPCHECK("schuettel");
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du schuettelst @@alle@@@@adverb@@ die Haende.";
+      out_vic="@@gname@@ schuettelt @@alle@@@@adverb@@ die Haende.";
+      return MixedOut(WEM);
+    }
+    if (str && !who && !adverb)
+      Return("Schuettle wie? Schuettle wem die Hand?\n");
+    if(!who) {
+      out_sel="Du schuettelst Dich@@adverb@@.";
+      out_oth="@@gname@@ schuettelt sich@@adverb@@.";
+    }
+    else  {
+      if (CheckLife(0,NOT_DEAD,
+        "", "Noe, das mach ich nur mit Lebewesen."))
+          return 1;
+      if (who == this_player())  {
+        out_sel="Du hebst"+(adverb ? "@@adverb@@" : " triumphierend")
+          +" Deine Haende ueber den Kopf und schuettelst sie.";
+        out_oth="@@gname@@ hebt"+(adverb ? "@@adverb@@" : " triumphierend")
+          +" die Haende ueber den Kopf\nund schuettelt sie.";
+      }
+      else  {
+        out_sel="Du schuettelst@@ wem@@@@adverb@@ die Haende.";
+        if (ghost()) out_sel+="\nNaja, Du versuchst es wenigstens - "
+          +"immer diese durchlaessigen Haende...";
+        out_vic="@@gname@@ schuettelt Dir@@adverb@@ die Haende.";
+        if (ghost()) out_vic+="\nNaja, Du versuchst es wenigstens - "
+          +"immer diese durchlaessigen Haende...";
+        out_oth="@@gname@@ schuettelt@@ wem@@@@adverb@@ die Haende.";
+        if (ghost()) out_oth+="\nNaja, Du versuchst es wenigstens - "
+          +"immer diese durchlaessigen Haende...";
+      }
+    }
+    return FeelIt();
+
+    /**************** Schweigen ***************/
+    case "schweig":
+    HELPCHECK("schweig");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schweige wie?\n");
+    out_sel="Du schweigst@@adverb@@.";
+    out_oth="@@gname@@ schweigt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Seufzen ***************/
+    case "seufz":
+    HELPCHECK("seufz");
+    GHOSTCHECK("Du seufzt geisterhaft.\n",
+      gname()+" seufzt geisterhaft. Naja, wie denn sonst?\n", 0);
+    ParseRest(str);
+    if (!adverb && str)
+      Return("Seufze wie?\n");
+    out_sel="Du seufzst@@adverb@@.";
+    out_oth="@@name@@ seufzt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Singen ***************/
+    case "sing":
+    HELPCHECK("sing");
+    if (!str) {
+      out_sel="Oh sole mio!";
+      out_oth="@@gname@@ singt irgendwas italienisches.";
+    }
+    else  {
+      ParseRest(str);
+      out_sel="Du singst@@adverb@@"+(adverb ? "." : " '"+capitalize(str)+"'.");
+      out_oth="@@gname@@ singt@@adverb@@"+(adverb ? "." : " '"
+        +capitalize(str)+"'.");
+    }
+    return FeelIt();
+
+    /**************** Sniffen ***************/
+    case "sniff":
+    HELPCHECK("sniff");
+    ParseRest(str);
+    if (str && !adverb && !who)
+      Return("Sniffe wie?\n");
+    out_sel="Du sniffst"+(who ? " @@wen@@" : "")
+      +(adverb ? "@@adverb@@" : " traurig")+(who ? " an." : ".");
+    if (who) out_vic="@@gname@@ snifft Dich"
+      +(adverb ? "@@adverb@@" : " traurig")+" an.";
+    out_oth="@@gname@@ snifft"+(who ? " @@wen@@" : "")
+      +(adverb ? "@@adverb@@" : " traurig")+(who ? " an." : ".");
+    return FeelIt();
+
+    /**************** Spucken ***************/
+    case "spuck":
+    HELPCHECK("spuck");
+    GHOSTCHECK("Du bringst nicht genug Spucke zusammen.\n",
+      gname()+" stellt gerade fest, dass man ohne Spucke nicht\n"
+        +"spucken kann.\n", 0);
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Spucke wen wie an?\n");
+    if (who && CheckLife(NOT_SELF,0,
+      "Hast Du Dich so schlecht benommen? Lass es lieber bleiben."))
+        return 1;
+    out_sel="Du spuckst@@ wen@@@@adverb@@ "+(who ? "an." : "auf den Boden.");
+    if (who) out_vic="@@name@@ spuckt Dich@@adverb@@ an.";
+    out_oth="@@name@@ spuckt@@ wen@@@@adverb@@ "
+      +(who ? "an." : "auf den Boden.");
+    return FeelIt();
+
+    /**************** Stampfen ***************/
+    case "stampf":
+    HELPCHECK("stampf");
+    ParseRest(str, "auf");
+    out_sel="Du stampfst@@adverb@@ mit dem Fuss auf.";
+    out_oth="@@gname@@ stampft@@adverb@@ mit dem Fuss auf.";
+    return FeelIt();
+
+    /**************** Starren ***************/
+    case "starr":
+    HELPCHECK("starr");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Starre wie bzw. wen an?\n");
+    if (who && CheckLife(NOT_SELF,0,
+      "Wie willst Du in Deine eigenen Augen starren? "
+        +"(Spiegel gelten nicht...)"))
+        return 1;
+    out_sel="Du starrst"+(!str ? " ins Leere." : (who ? "@@ wen@@" : "")
+      +(adverb ? "@@adverb@@" : " vertraeumt")+(who ? " an." : "."));
+    if (who) out_vic="@@gname@@ starrt"+(adverb ? "@@adverb@@" : " tief")
+      +" in Deine Augen.";
+    out_oth="@@gname@@ starrt"+(!str ? " ins Leere." : (who ? "@@ wen@@" : "")
+      +(adverb ? "@@adverb@@" : " vertraeumt")+(who ? " an." : "."));
+    return FeelIt();
+
+    /**************** Staunen ***************/
+    case "staun":
+    HELPCHECK("staun");
+    if (!str)  {
+      out_sel="Du bist erstaunt.";
+      out_oth="@@gname@@ ist erstaunt.";
+    }
+    else  {
+      ParseRest(str, "ueber");
+      if (!who && !adverb)
+        Return("Bla bla. Wenn Du nach staune noch was tippst, sollte "
+          +"das ein\nLebewesen sein.\n");
+      if (who == this_player())  {
+        out_sel="Du staunst@@adverb@@ ueber Dich selber.";
+        out_oth="@@gname@@ staunt@@adverb@@ ueber sich selber.";
+      }
+      else  {
+        out_sel="Du staunst@@adverb@@"+(who ? " ueber @@wen@@." : ".");
+        if (who) out_vic="@@gname@@ staunt@@adverb@@ ueber Dich.";
+        out_oth="@@gname@@ staunt@@adverb@@"+(who ? " ueber @@wen@@." : ".");
+      }
+    }
+    return FeelIt();
+
+    /**************** Stieren ***************/
+    case "stier":
+    HELPCHECK("stier");
+    GHOSTCHECK("Du stierst mit hohlem Blick in die Gegend.\n",
+      gname()+" stiert mit hohlem Blick in die Gegend.\n", 0);
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Stiere wie oder wen an?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Du kannst Dich nicht selber anstieren.",
+      "Bitte nur Lebewesen anstieren."))
+        return 1;
+    out_sel="Du stierst@@ wen@@@@adverb@@"
+      +(who ? " an." : " in der Gegend herum.");
+    if (who) out_vic="@@gname@@ stiert Dich@@adverb@@ an.";
+    out_oth="@@gname@@ stiert@@ wen@@@@adverb@@"
+      +(who ? " an." : " in der Gegend herum.");
+    return FeelIt();
+
+    /**************** Stimme zu ***************/
+    case "stimm":
+    HELPCHECK("stimm");
+    ParseRest(str, "zu");
+    if (str && !who && !adverb)
+      Return("Stimme wem zu?\n");
+    out_sel="Du stimmst@@ wem@@@@adverb@@ zu.";
+    if (who) out_vic="@@gname@@ stimmt Dir@@adverb@@ zu.";
+    out_oth="@@gname@@ stimmt@@ wem@@@@adverb@@ zu.";
+    return FeelIt();
+
+    /**************** Stoehnen ***************/
+    case "stoehn":
+    HELPCHECK("stoehn");
+    GHOSTCHECK("Du stoehnst schauderlich.\n",
+      gname()+" stoehnt schauderlich. Zum Glueck\nhast Du ziemlich "
+        +"gute Nerven.\n", 0);
+    ParseRest(str);
+    if (!adverb && str)
+      Return("Wie willst Du stoehnen?\n");
+    out_sel="Du stoehnst@@adverb@@.";
+    out_oth="@@name@@ stoehnt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Stossen ***************/
+    case "stoss":
+    HELPCHECK("stoss");
+    ParseRest(str);
+    if (!who)
+      Return("Stosse wen?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Was soll der Unsinn? Lass das!",
+      "Das gibt nur bei Lebewesen Sinn."))
+        return 1;
+    GHOSTCHECK("Dein Ellenbogen versinkt in "+who->name(WEM)+".\n",
+      gname()+" will "+who->name(WEM)+" in die Rippen stossen, aber "
+        +QPP(MALE,WER,PLURAL)+"\nEllenbogen verteilen keinen Stoss, "
+        +"sondern versinken.\n",
+      gname()+" will Dich in die Rippen stossen, aber "+QPP(MALE,WER,PLURAL)
+        +" Ellenbogen versinken.\n");
+    out_sel="Du stoesst@@ wen@@@@adverb@@ in die Rippen.";
+    out_vic="@@name@@ stoesst Dir@@adverb@@ in die Rippen.";
+    out_oth="@@name@@ stoesst@@ wen@@@@adverb@@ in die Rippen.";
+    return FeelIt();
+
+    /**************** Streicheln ***************/
+    case "streichel":
+    case "streichl":
+    HELPCHECK("streichel");
+    ParseRest(str);
+    if (!who)
+      Return("Streichle wen?\n");
+    if (for_all)  {
+      out_sel="Du streichelst @@alle@@@@adverb@@.";
+      out_vic="@@gname@@ streichelt @@alle@@@@adverb@@.";
+      return MixedOut(WEN);
+    }
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Lass Dich von anderen streicheln.",
+      "Ich streichle nur Lebewesen."))
+        return 1;
+    GHOSTCHECK("Du willst "+who->name(WEN,2)+" streicheln, aber Deine "
+      +"Haende koennen\nnichts beruehren.\n",
+    gname()+" will "+who->name(WEN,2)+" streicheln, aber diese\n"
+      +"Geisterhaende koennen halt nix beruehren...\n",
+    gname()+" will Dich streicheln, scheitert aber wie so oft an\n"
+      +"diesen dummen durchlaessigen Geisterhaenden.\n");
+    out_sel="Du streichelst @@wen@@@@adverb@@.";
+    out_vic="@@name@@ streichelt Dich@@adverb@@.";
+    out_oth="@@name@@ streichelt @@wen@@@@adverb@@.";
+    return FeelIt();
+
+    /**************** Stupsen ***************/
+    case "stups":
+    HELPCHECK("stups");
+    if (ghost())
+      Return("Das geht nicht ohne Ellenbogen,..\n");
+    ParseRest(str);
+    if (!who)
+      Return("Stupse wen an?\n");
+    out_sel="Du stupst @@wen@@@@adverb@@ an.";
+    out_vic="@@name@@ stupst Dich@@adverb@@ an.";
+    out_oth="@@name@@ stupst @@wen@@@@adverb@@ an.";
+    return FeelIt();
+
+    /**************** Stutzen ***************/
+    case "stutz":
+    HELPCHECK("stutz");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Stutze wie?\n");
+    out_sel="Du stutzt@@adverb@@.";
+    out_oth="@@gname@@ stutzt@@adverb@@.";
+    return FeelIt();
+
+    /**************** Taetscheln ***************/
+    case "taetschel":
+    case "taetschl":
+    HELPCHECK("taetschel");
+    ParseRest(str);
+    if (!who)
+      Return("Taetschle wen?\n");
+    if (CheckLife(NOT_SELF,NOT_DEAD,
+      "Das sieht zu doof aus, das mache ich nicht.",
+      "Ich taetschle nur Lebewesen."))
+        return 1;
+     GHOSTCHECK("Du willst "+who->name(WEN)+" taetscheln - aber Deine "
+        +"Haende gehen\nglatt durch den Kopf durch.\n",
+      gname()+" will "+who->name(WEN)+" den Kopf taetscheln, aber "
+        +"die Geister-\nhaende gehen glatt durch den Kopf durch.\n",
+      gname()+" will Deinen Kopf taetscheln, aber diese Geisterhaende "
+        +"gehen\nglatt durch Deinen Kopf durch - Du hast ein seltsames "
+        +"Gefuehl dabei.\n");
+    out_sel="Du taetschelst @@wem@@@@adverb@@ den Kopf.";
+    out_vic="@@name@@ taetschelt Dir@@adverb@@ den Kopf.";
+    out_oth="@@name@@ taetschelt @@wem@@@@adverb@@ den Kopf.";
+    return FeelIt();
+
+    /**************** Tanzen ***************/
+    case "tanz":
+    HELPCHECK("tanz");
+    GHOSTCHECK("Du tanzt den klassischen GeisterTanz (tm).\n",
+      gname()+" tanzt den klassischen GeisterTanz (tm).\n", 0);
+    if (!str) {
+      out_sel="Kommst Du Dir nicht irgendwie bloed vor? Du tanzt "
+        +"den Ententanz.";
+      out_oth="@@name@@ tanzt den Ententanz.";
+    }
+    else  {
+      taenze=({"Walzer","Polka","Rumba","Tango","Cha cha cha","Foxtrott",
+        "Mambo","Salsa","Slowfox","Breakdance","Pogo","Merengue",
+        "Rock'n'Roll","Ballett","Regentanz","Hexentanz"});
+      ParseRest(str,"mit");
+      if (!who)
+        Return("Mit wem willst Du tanzen?\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Mit Dir selber kannst Du nicht tanzen.",
+        "Keine Reaktion - will wahrscheinlich nicht tanzen."))
+          return 1;
+      ifoo=random(sizeof(taenze));
+      out_sel="Du tanzt@@adverb@@ mit @@wem@@ eine Runde "+taenze[ifoo]+".";
+      out_vic="@@name@@ reisst Dich an sich und tanzt@@adverb@@ eine Runde "
+        +taenze[ifoo]+" mit Dir.";
+      out_oth="@@name@@ schnappt sich @@wen@@ und die beiden tanzen"
+        +"@@adverb@@ eine Runde "+taenze[ifoo]+".";
+    }
+    return FeelIt();
+
+    /**************** Traeumen ***************/
+    case "traeum":
+    HELPCHECK("traeum");
+    if (!str)
+      Return("Traeume wovon oder von wem?\n");
+    ParseRest(str);
+    out_sel="Du traeumst"+(who ? "@@adverb@@ von @@wem@@."
+      : (adverb ? "@@adverb@@." : " "+str+"."));
+    if (who) out_vic="@@gname@@ traeumt@@adverb@@ von Dir.";
+    out_oth="@@gname@@ traeumt"+(who ? "@@adverb@@ von @@wem@@."
+      : (adverb ? "@@adverb@@." : " "+str+"."));
+    return FeelIt();
+
+    /**************** Treten (tritt) ***************/
+    case "tritt":
+    case "tret":
+    HELPCHECK("tritt");
+    if (!str)  {
+      GHOSTCHECK("Dein Fuss faehrt durch die beruehmte langvergessene "
+          +"unsichtbare Schildkroete\nhindurch.\n",
+        gname()+" will die beruehmte langvergessene unsichtbare\n"
+          +"Schildkroete treten, aber "+QPP(MALE,WER)
+          +" Fuss faehrt durch sie hindurch.\n", 0);
+      out_sel="Du trittst die beruehmte langvergessene unsichtbare "
+        +"Schildkroete.";
+      out_oth="@@gname@@ tritt die beruehmte langvergessene unsichtbare "
+        +"Schildkroete.";
+    }
+    else  {
+      ParseRest(str);
+      if (for_all)  {
+        out_sel="Du trittst @@alle@@@@adverb@@. Solltest Du nicht langsam "
+          +"an Flucht denken?";
+        out_vic="@@name@@ tritt @@alle@@@@adverb@@. Traut sich ganz "
+          +"schoen was!";
+        return MixedOut(WEN);
+      }
+      if (!who && !adverb)
+        Return("Wenn Du schon was nach tritt tippst, dann sag mir, wen "
+          +"oder wie ich das soll.\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Du schaffst es nicht, Dir selber in den Hintern zu treten.",
+        "Tote Sachen tritt man nicht auch noch!"))
+          return 1;
+      if (who)  {
+        out_sel="Du trittst@@ wen@@@@adverb@@.";
+        if (who) out_vic="@@gname@@ tritt Dich@@adverb@@.";
+        out_oth="@@gname@@ tritt@@ wen@@@@adverb@@.";
+      }
+      else  {
+        out_sel="Du trittst die beruehmte langvergessene unsichtbare "
+          +"Schildkroete@@adverb@@.";
+        out_oth="@@gname@@ tritt die beruehmte langvergessene unsichtbare "
+          +"Schildkroete\n@@adverb@@.";
+      }
+    }
+    return FeelIt();
+
+    /**************** Troesten ***************/
+    case "troest":
+    HELPCHECK("troest");
+    ParseRest(str);
+    if (!who)
+      Return("Wen willst Du troesten?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Trost musst Du bei jemand anders suchen.",
+      "Das Teil musst Du nicht troesten, das fuehlt nix."))
+        return 1;
+    out_sel="Du troestest@@ wen@@@@adverb@@.";
+    out_vic="@@gname@@ troestet Dich@@adverb@@.";
+    out_oth="@@gname@@ troestet@@ wen@@@@adverb@@.";
+    return FeelIt();
+
+    /**************** Umarmen ***************/
+    case "umarm":
+    HELPCHECK("umarm");
+    ParseRest(str);
+    if (!who)
+      Return("Wen willst Du umarmen?\n");
+    if (who && CheckLife(0,NOT_DEAD,0,"Bitte umarme nur Lebewesen."))
+      return 1;
+    if (ghost() && CheckLife(NOT_SELF,0,
+      "Du kannst Dich als Geist nicht selber waermen."))
+        return 1;
+    str1=who->QueryProp(P_NAME);
+    if(pointerp(str1))str1=(string)str1[0]; // Rumata
+    str2=who->QueryPronoun(WEN);
+    GHOSTCHECK("Du willst "+str1+" umarmen, aber Deine Arme gehen durch "
+        +str2+" durch.\n",
+      gname()+" will "+str1+" umarmen, aber "+QPP(MALE,WER,PLURAL)
+        +" Arme gehen\ndurch "+str2+" hindurch.\n",
+      gname()+" will Dich umarmen, aber "+QPP(MALE,WER,PLURAL)
+        +" Arme gehen durch Dich hindurch.\n");
+    if (for_all)  {
+      out_sel="Du umarmst @@alle@@@@adverb@@.";
+      out_vic="@@name@@ umarmt @@alle@@@@adverb@@.";
+      return MixedOut(WEN);
+    }
+    if (who==this_player())  {
+      out_sel="Du legst Deine Arme um Dich und waermst Dich "
+        +"ein bisschen selber.";
+      out_oth="@@name@@ legt "+QPP(MALE,WER,PLURAL)
+        +" Arme um sich und waermt sich ein bisschen selber.";
+    }
+    else  {
+      out_sel="Du umarmst@@ wen@@@@adverb@@.";
+      out_vic="@@name@@ umarmt Dich@@adverb@@.";
+      out_oth="@@name@@ umarmt@@ wen@@@@adverb@@.";
+    }
+    return FeelIt();
+
+    /**************** Verfluchen ***************/
+    case "verfluch":
+    HELPCHECK("verfluch");
+    if (!str)
+      Return("Wen oder was willst Du denn verfluchen?\n");
+    ParseRest(str);
+    if (!who)  {
+      out_sel="Du verfluchst "+str+".";
+      out_oth="@@gname@@ verflucht "+str+".";
+    }
+    else  {
+      if (who==this_player())
+        Return("Sich selber verflucht man besser nicht...\n");
+      if (!adverb)  {
+        flag=sscanf(str, "%s %s", str1,str2);
+        out_sel="Du verfluchst@@ wen@@"+(flag==2 ? " "+str2 : "")+".";
+        out_vic="@@gname@@ verflucht Dich"+(flag==2?" "+str2 : "")+".";
+        out_oth="@@gname@@ verflucht@@ wen@@"+(flag==2 ? " "+str2 : "")+".";
+      }
+      else  {
+        out_sel="Du verfluchst@@ wen@@@@adverb@@.";
+        out_vic="@@gname@@ verflucht Dich@@adverb@@.";
+        out_oth="@@gname@@ verflucht@@ wen@@@@adverb@@.";
+      }
+    }
+    return FeelIt();
+
+    /**************** Verneigen / Verbeugen ***************/
+    case "verneig":
+    case "verbeug":
+    HELPCHECK("verneig");
+    GHOSTCHECK("Du verneigst Dich ein bisschen heftig - Dein Kopf taucht "
+        +"kurz in den Boden.\n",
+      gname()+" verneigt sich. Ein bisschen heftig - "+QPP(MALE,WER)
+        +" Kopf\ntaucht kurz in den Boden ein.\n", 0);
+    if ((!str) || (str == "dich")) {
+      out_sel="Du verneigst Dich vor den Anwesenden.";
+      out_oth="@@name@@ verneigt sich anmutig.";
+    }
+    else  {
+      ParseRest(str);
+      if (for_all)  {
+        out_sel="Du verneigst Dich@@adverb@@ vor @@alle@@.";
+        out_vic="@@name@@ verneigt sich@@adverb@@ vor @@alle@@.";
+        return MixedOut(WEM);
+      }
+      if (!who && !adverb)
+        Return("Verneige dich irgendwie oder vor jemandem.\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Wie willst Du das denn schaffen?",
+        "Vor Sachen wird hier nicht verneigt, klar?\n"))
+          return 1;
+      out_sel="Du verneigst Dich@@adverb@@"+(who ? " vor" : "")+"@@ wem@@.";
+      if (who ) out_vic="@@name@@ verneigt sich@@adverb@@ vor Dir.";
+      out_oth="@@name@@ verneigt sich@@adverb@@"+(who ? " vor" : "")
+        +"@@ wem@@.";
+    }
+    return FeelIt();
+
+    /**************** Verneinen ***************/
+    case "nein":
+    case "noe":
+    HELPCHECK("nein");
+    GHOSTCHECK("Du schuettelst Deinen Kopf so heftig, dass er kurz "
+        +"davonschwebt.\n",
+      gname()+" schuettelt heftig den Kopf.\nSo heftig, dass dieser "
+        +"kurz davonschwebt und wieder eingefangen werden muss.\n", 0);
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Schuettle wie den Kopf?\n");
+    out_sel="Du schuettelst@@adverb@@ den Kopf.";
+    out_oth="@@name@@ schuettelt@@adverb@@ den Kopf.";
+    return FeelIt();
+
+    /**************** Wackeln ***************/
+    case "wackel":
+    case "wackl":
+    HELPCHECK("wackel");
+    if (ghost())
+      Return("Da gibt es nichts mehr, womit Du wackeln kannst.\n");
+    if (str)
+      if (strstr(str, "mit ")==0)
+        sscanf(str, "mit %s", sfoo);
+      else if (strstr(str,"mit ")>0)  {
+        sscanf(str, "%s mit %s", sfoo, sfoo);
+        flag=1;
+      }
+    if (sfoo=="") sfoo=0;
+    ParseRest(str, (sfoo ? (flag ? " mit " : "mit ")+sfoo : 0));
+    if (str && !adverb && !sfoo)
+      Return("Wackle wie oder womit?\n");
+    out_sel="Du wackelst@@adverb@@ mit "+(sfoo ? sfoo+"." : "dem Hintern.");
+    out_oth="@@name@@ wackelt@@adverb@@ mit "
+      +(sfoo ? sfoo+"." : QPP(MALE,WEM)+" Hintern.");
+    return FeelIt();
+
+    /**************** Waelzen ***************/
+    case "waelz":
+    HELPCHECK("waelz");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Waelze Dich wie auf dem Boden?\n");
+    out_sel="Du waelzt Dich"+(adverb ? "@@adverb@@" : " vor Lachen")
+      +" auf dem Boden.";
+    out_oth="@@gname@@ waelzt sich"+(adverb ? "@@adverb@@" : " vor Lachen")
+      +(ghost() ? " im" : " auf dem")+" Boden.";
+    return FeelIt();
+
+    /**************** Warten ***************/
+    case "wart":
+    HELPCHECK("wart");
+    ParseRest(str);
+    if (!str)  {
+      out_sel="Du tippst mit dem Fuss auf den Boden.";
+      out_oth="@@gname@@ tippt mit dem Fuss auf den Boden.";
+    } else if (!who && adverb)  {
+      out_sel="Du wartest@@adverb@@.";
+      out_oth="@@gname@@ wartet@@adverb@@.";
+    } else  {
+      out_sel="Du wartest@@adverb@@ auf "+(who ? "@@wen@@." : str+".");
+      if (who) out_vic="@@gname@@ wartet@@adverb@@ auf Dich.";
+      out_oth="@@gname@@ wartet@@adverb@@ auf "+(who ? "@@wen@@." : str+".");
+    }
+    return FeelIt();
+
+#ifdef WECKE
+    /**************** Wecken ***************/
+    case "weck":
+    HELPCHECK("weck");
+    if (ParseRemote(str))
+      return 1;
+//    ParseRest(str);
+    if (!who)
+      Return("Wen willst Du wecken?\n");
+    if (sscanf(str, "%s %s", sfoo, sfoo)==2)
+      flag=1;
+    out_sel="Dein Wecker klingelt bei @@wem@@@@adverb@@"
+      +(adverb ? "." : (flag ? ": "+sfoo : "."));
+    out_vic=" "+name(WESSEN)+" Wecker klingelt bei Dir@@adverb@@"
+      +(adverb ? "." : (flag ? ": "+sfoo : "."));
+    out_oth="@@gname@@ wirft "+QPP(MALE, WEN)
+      +" Wecker@@adverb@@ nach @@wem@@.";
+          if (!who->QueryProp(P_VISUALBELL))
+         out_vic[0]=7; // chr(7)==BEL
+      else out_vic=out_vic[1..];
+    return FeelIt();
+#endif
+
+    /**************** Weinen ***************/
+    case "wein":
+    HELPCHECK("wein");
+    GHOSTCHECK("Es reicht leider nur fuer ein paar winzige Nebelwoelkchen, "
+        +"nicht fuer Traenen.\n",
+      gname()+" verzieht das Gesicht und ein paar winzige Nebel-\n"
+        +"woelkchen entfernen sich von seinen \"Augen\".\n", 0);
+    if (!str)  {
+      out_sel="Waaaaah! Du weinst bitterlich.";
+      out_oth="@@name@@ bricht in Traenen aus und weint bitterlich.";
+    }
+    else  {
+      ParseRest(str);
+      if (!who && !adverb)
+        Return("Weine Dich irgendwie bei irgendwem aus, aber nicht so.\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Bei sich selber kann man sich so schlecht ausweinen.",
+        "Bei wem willst Du Dich ausweinen???"))
+          return 1;
+      if (who)  {
+        out_sel="Du weinst Dich@@adverb@@ bei@@ wem@@ aus.";
+        out_vic="@@name@@ weint sich@@adverb@@ bei Dir aus.";
+        out_oth="@@name@@ weint sich@@adverb@@ bei@@ wem@@ aus.";
+      }
+      else  {
+        out_sel="Du brichst in Traenen aus und weinst@@adverb@@.";
+        out_oth="@@name@@ bricht in Traenen aus und weint@@adverb@@.";
+      }
+    }
+    return FeelIt();
+
+    /**************** Winken ***************/
+    case "wink":
+    HELPCHECK("wink");
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du winkst @@alle@@@@adverb@@ zu.";
+      out_vic="@@name@@ winkt @@alle@@@@adverb@@ zu.";
+      return MixedOut(WEM);
+    }
+    if (!who && !adverb && str)
+      Return("Vielleicht solltest Du auch sagen, wem oder wie Du "
+        +"(zu)winken willst.\n");
+    if (who && CheckLife(NOT_SELF, NOT_DEAD,
+      "Wink Dir nicht selber zu.",
+      "Du musst schon einem Lebewesen zuwinken."))
+        return 1;
+    out_sel="Du winkst@@ wem@@@@adverb@@"+(who ? " zu" : "")+".";
+    if (who) out_vic="@@gname@@ winkt Dir@@adverb@@ zu.";
+    out_oth="@@gname@@ winkt@@ wem@@@@adverb@@"+(who ? " zu" : "")+".";
+    return FeelIt();
+
+    /**************** Wuergen ***************/
+    case "wuerg":
+    HELPCHECK("wuerg");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Wuerge wen oder wie?\n");
+    if (!who)  {
+      out_sel="Du faengst@@adverb@@ an zu wuergen.";
+      out_oth="@@gname@@ faengt@@adverb@@ an zu wuergen.";
+    } else if (CheckLife(NOT_SELF, NOT_DEAD,
+          "Du wuergst ein bischen an Dir rum. Dir wird schnell langweilig.",
+	  "Wuerg lieber ein Lebewesen.")) {
+      return 1;
+    } else {
+      out_sel="Du springst @@wen@@ an und faengst an, "+who->QueryPronoun(WEN)
+        +"@@adverb@@ zu wuergen.";
+      out_vic="@@gname@@ springt Dich auf einmal an und wuergt Dich@@adverb@@.";
+      out_oth="@@gname@@ springt auf einmal @@wen@@ an und wuergt "
+        +who->QueryPronoun(WEN)+"@@adverb@@.";
+    }
+    return FeelIt();
+
+    /**************** Wundern ***************/
+    case "wunder":
+    HELPCHECK("wunder");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Wie oder ueber wen willst Du Dich wundern?\n");
+    out_sel="Du wunderst Dich@@adverb@@"+(who ? " ueber @@wen@@." : ".");
+    if (who) out_vic="@@gname@@ wundert sich@@adverb@@ ueber Dich.";
+    out_oth="@@gname@@ wundert sich@@adverb@@"+(who ? " ueber @@wen@@." : ".");
+    return FeelIt();
+
+    /**************** Wuscheln ***************/
+    case "wuschel":
+    case "wuschl":
+    HELPCHECK("wuschel");
+    ParseRest(str);
+    if (!who)
+      Return("Wen willst Du denn wuscheln?\n");
+    if (CheckLife(0,NOT_DEAD,
+      "", "Hmm, sehr tot. Ne, lieber nicht."))
+        return 1;
+    if (who->QueryProp(P_FROG))  {
+      write("Du verwuschelst...  aeh... hm. Ein Frosch hat wohl nix "
+        +"zum Wuscheln.\n");
+      return 1;
+    };
+    GHOSTCHECK("Du willst "+who->name(WEN)+" wuscheln - aber Deine "
+        +"Haende gehen\nglatt durch den Kopf durch.\n",
+      gname()+" will "+who->name(WEN)+" den Kopf wuscheln, aber "
+        +"die Geister-\nhaende gehen glatt durch den Kopf durch.\n",
+      gname()+" will Dich wuscheln, aber diese Geisterhaende "
+        +"gehen\nglatt durch Deinen Kopf durch - Du hast ein seltsames "
+        +"Gefuehl dabei.\n");
+    if (member(({"highlander","boing","mieze","freund"}), who->query_real_name())>-1)
+      switch (who->query_real_name())  {
+        case "highlander": str1="Federn"; break;
+        case "freund"    :
+        case "mieze"     :
+        case "boing"     : str1="Fell"; break;
+      }
+    else if (who->is_class_member(({CL_DRAGON, CL_FISH, CL_REPTILE})))
+      str1="Schuppen";
+    else if (who->is_class_member(({CL_BIRD, "elster","greif"})))
+      str1="Federn";
+    else if (who->is_class_member(({CL_MAMMAL_LAND,CL_FELINE,"tiger",
+                                    "steinbeisser","knuddeleisbaer"})))
+      str1="Fell";
+    else str1="Haare";
+    out_sel="Du verwuschelst@@adverb@@ @@wessen@@ "+str1+".";
+    out_vic="@@name@@ verwuschelt@@adverb@@ Dein"
+      +(str1=="Fell" ? " " : "e ")+str1+".";
+    out_oth="@@name@@ verwuschelt@@adverb@@ @@wessen@@ "+str1+".";
+    return FeelIt();
+
+    /**************** Zitieren ***************/
+    case "zitier":
+    HELPCHECK("zitier");
+    ParseRest(str);
+    if (!str)
+      Return("Zitiere was oder wen womit?\n");
+    sfoo=implode(explode(str, " ")[1..], " ");
+    if (sfoo=="") sfoo=0;
+    if (who)  {
+      out_sel="Du zitierst @@wen@@"+(sfoo ? ": \""+sfoo+"\"" : "")+".";
+      out_vic="@@gname@@ zitiert Dich"+(sfoo ? ": \""+sfoo+"\"" : "")+".";
+      out_oth="@@gname@@ zitiert @@wen@@"+(sfoo ? ": \""+sfoo+"\"" : "")+".";
+    }
+    else  {
+      sfoo=explode(str, "/")[0];
+      out_sel="Du zitierst@@adverb@@"+(sfoo ? ": \""+sfoo+"\"" : "")+".";
+      out_oth="@@gname@@ zitiert@@adverb@@"+(sfoo ? ": \""+sfoo+"\"" : "")+".";
+    }
+    return FeelIt();
+
+    /**************** Zittern ***************/
+    case "zitter":
+    HELPCHECK("zitter");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Zittere wie?\n");
+    out_sel="Du zitterst"+(adverb ? "@@adverb@@." : " vor Angst.");
+    out_oth="@@gname@@ zittert"+(adverb ? "@@adverb@@." : " vor Angst.");
+    return FeelIt();
+
+    /**************** Schulterzucken ***************/
+    case "zuck" :
+    HELPCHECK("zuck");
+          if (str)
+      if (sscanf(str,"%s mit den schultern",sfoo))
+        str=sfoo;
+    else if (sscanf(str,"%s den schultern",sfoo))
+      str=sfoo;
+    else
+      if (sscanf(str,"%s schultern",sfoo))
+        str=sfoo;
+    if (str=="") str=0;
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Zucke wie mit den Schultern?\n");
+    out_sel="Du zuckst@@adverb@@ mit den Schultern.";
+    out_oth="@@gname@@ zuckt"+(adverb ? "@@adverb@@" : " ratlos")
+            +" mit den Schultern.";
+    return FeelIt();
+
+    /**************** Zwinkern ***************/
+    case "zwinker":
+    HELPCHECK("zwinker");
+    if (ghost())
+      Return("Vergiss es - das ist als Geist viel zu unauffaellig, als dass\n"
+        +"es andere Leute sehen wuerden.\n");
+    ParseRest(str);
+    if (str && !who && !adverb)
+      Return("Zwinkere wie? Zwinkere wem zu?\n");
+    if (who && CheckLife(NOT_SELF,NOT_DEAD,
+      "Du kannst Dir nicht selber zuzwinkern.",
+      "Wieso reagiert das Ding da nicht auf Dein Zwinkern? Ist es etwa tot?"))
+        return 1;
+    out_sel="Du zwinkerst@@ wem@@@@adverb@@"+(who ? " zu." : ".");
+    if (who) out_vic="@@name@@ zwinkert Dir@@adverb@@ zu.";
+    out_oth="@@name@@ zwinkert@@ wem@@@@adverb@@"+(who ? " zu." : ".");
+    return FeelIt();
+
+    /**************** Zunge rausstrecken ***************/
+    case "streck":
+    HELPCHECK("streck");
+    GHOSTCHECK("Sorry, Du hast keine Zunge zum Rausstrecken.\n","",0);
+    if (!str)
+      Return("Strecke was wie wem wo wann wieso?\n");
+    str=lower_case(str);
+    if (sscanf(str, "%s zunge raus", str1)!=1 &&
+    sscanf(str, "%s die zunge raus", str1)!=1)
+      Return("Strecke was wie wem wo wann wieso?\n");
+    ParseRest(str1);
+    if (for_all)  {
+      out_sel="Du streckst @@alle@@@@adverb@@ die Zunge raus.";
+      out_vic="@@name@@ streckt @@alle@@@@adverb@@ die Zunge raus.";
+      return MixedOut(WEM);
+    }
+    out_sel="Du streckst@@ wem@@@@adverb@@ die Zunge raus.";
+    if (who) out_vic="@@name@@ streckt Dir@@adverb@@ die Zunge raus.";
+    out_oth="@@name@@ streckt@@ wem@@@@adverb@@ die Zunge raus.";
+    return FeelIt();
+
+    // Spezialsachen - Geisterverben und Magierverben
+
+    /**************** Rasseln ***************/
+    case "rassel":
+    case "rassl":
+    if (!ghost())
+      Return("Das ist nicht Dein Fachgebiet - Du bist doch kein Geist!\n");
+    HELPCHECK("rassel");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Rassel wie?\n");
+    out_sel="Du rasselst"+(adverb ? "@@adverb@@" : " fuerchterlich")
+      +" mit einer rostigen Rasselkette,\n"
+      +"die Du auf einmal fuer einen Moment in der Hand haeltst.";
+    out_oth="@@gname@@ holt auf einmal eine rostige Rasselkette aus\n"
+      +"dem Nichts und faengt an,"+(adverb ? "@@adverb@@" : " fuerchterlich")
+      +" damit zu rasseln.\n"
+      +"Danach ist die Kette auf einmal wieder verschwunden.";
+    return FeelIt();
+
+    /**************** Heulen ***************/
+    case "heul":
+    if (!ghost())
+      Return("Lass das mal den Fachleuten (also den Geistern).\n");
+    HELPCHECK("heul");
+    ParseRest(str);
+    if (str && !adverb)
+      Return("Heule wie?\n");
+    out_sel="Du heulst"+(adverb ? "@@adverb@@." : " schauerlich.");
+    out_oth="@@gname@@ heult"+(adverb ? "@@adverb@@." : " schauerlich.");
+    return FeelIt();
+
+    /**************** Treten (tretet) ***************/
+    case "kick":
+    if (!IS_WIZARD(this_player()))
+      return 0;
+    HELPCHECK("kick");
+    if (!str)  {
+      GHOSTCHECK("Dein Fuss faehrt durch die beruehmte langvergessene "
+          +"unsichtbare Schildkroete\nhindurch.\n",
+        gname()+" will die beruehmte langvergessene unsichtbare\n"
+          +"Schildkroete treten, aber "+QPP(MALE,WER)
+          +" Fuss faehrt durch sie hindurch.\n", 0);
+      out_sel="Du tretest die beruehmte langvergessene unsichtbare "
+        +"Schildkroete.";
+      out_oth="@@name@@ tretet die beruehmte langvergessene unsichtbare "
+        +"Schildkroete.";
+    }
+    else  {
+      ParseRest(str);
+      if (for_all)  {
+        out_sel="Du tretest @@alle@@@@adverb@@.";
+        out_vic="@@name@@ tretet @@alle@@@@adverb@@.";
+        return MixedOut(WEN);
+      }
+      if (!who && !adverb)
+        Return("Wenn Du schon was nach kick tippst, dann sag mir wen "
+          +"oder wie ichdas soll.\n");
+      if (who && CheckLife(NOT_SELF,NOT_DEAD,
+        "Du schaffst es nicht, Dir selber in den Hintern zu treten.",
+        "Tote Sachen tritt man nicht auch noch!"))
+          return 1;
+      if (who)  {
+        out_sel="Du tretest@@ wen@@@@adverb@@.";
+        if (who) out_vic="@@gname@@ tretet Dich@@adverb@@.";
+        out_oth="@@gname@@ tretet@@ wen@@@@adverb@@.";
+      }
+      else  {
+        out_sel="Du tretest die beruehmte langvergessene unsichtbare "
+          +"Schildkroete@@adverb@@.";
+        out_oth="@@gname@@ tretet die beruehmte langvergessene unsichtbare "
+          +"Schildkroete\n@@adverb@@.";
+      }
+    }
+    return FeelIt();
+
+    /************* Nassspritzen ***************/
+    case "splash":
+    if (!IS_WIZARD(this_player()) &&
+    !(IS_SEER(this_player()) && present("SEHER\nspritzpistole",this_player())))
+      return 0;
+    HELPCHECK("splash");
+    ParseRest(str);
+    if (for_all)  {
+      out_sel="Du ziehst Deine Wasserpistole und spritzt @@alle@@@@adverb@@ "
+        +"patschnass.";
+      out_vic="@@gname@@ zieht "+QPP(FEMALE,WEN)+" Wasserpistole und spritzt\n"
+        +"@@alle@@@@adverb@@ patschnass.";
+      return MixedOut(WEN);
+    }
+    if (!who)
+      Return("Wen willst Du denn nassmachen?\n");
+    if (who == this_player())  {
+      out_sel="Sag mal, kommst Du Dir nicht ein bisschen doof vor?\n"
+        +"Du ziehst Deine Wasserpistole und spritzt Dich@@adverb@@ selber patschnass.";
+      out_oth="@@gname@@ zieht "+QPP(FEMALE,WEN)+" Wasserpistole und spritzt "
+        +"sich@@adverb@@ aus unerfindlichen Gruenden selbst patschnass.";
+    }
+    else  {
+      out_sel="Du ziehst Deine Wasserpistole und spritzt @@wen@@@@adverb@@ "
+        +"patschnass.";
+      out_vic="@@gname@@ zieht "+QPP(FEMALE,WEN)+" Wasserpistole und spritzt "
+        +"Dich@@adverb@@ patschnass.";
+      out_oth="@@gname@@ zieht "+QPP(FEMALE,WEN)+" Wasserpistole und spritzt "
+        +"@@wen@@@@adverb@@ patschnass.";
+    }
+    return FeelIt();
+
+    /**************** Anflammen ***************/
+    case "flam":
+    if (!IS_WIZARD(this_player()) &&
+    !(IS_SEER(this_player()) && present("SEHER\nflammenwerfer",this_player())))
+      return 0;
+    HELPCHECK("flame");
+    if (ghost())
+      Return("Du hast leider grade Deinen Flammenwerfer nicht dabei.\n");
+    ParseRest(str);
+    ifoo=!random(7);
+    if (for_all)  {
+      out_sel="Du holst aus Deinen tiefsten Taschen (oder was weiss denn "
+        +"ich woher) Deinen\nMorgenGrauen handgearbeiteten Mini-Flammenwerfer "
+        +"(tm), richtest ihn aus und...\n"
+        +(ifoo ? "schaust leicht frustriert auf das Streichholz, in das "
+          +"er sich verwandelt hat."
+        : "feuerst@@adverb@@ einen riesigen Feuerball auf @@alle@@ ab.\n"
+          +"Es riecht auf einmal so verbrannt hier...");
+      out_vic="@@name@@ holt auf einmal irgendwoher einen MorgenGrauen "
+        +"handgearbeiteten\nMini-Flammenwerfer (tm), richtet ihn aus und...\n"
+        +(ifoo ? "schaut ziemlich frustriert auf das Streichholz, in das "
+          +"sich das Ding verwandelt hat."
+        : "feuert@@adverb@@ einen riesigen Feuerball auf @@alle@@ ab.\n"
+          +"Dir wird so warm um's Herz...");
+      return MixedOut(WEN);
+    }
+    if (!who)
+      Return("Wen willst Du denn ankokeln?\n");
+    out_sel="Du holst aus Deinen tiefsten Taschen (oder was weiss denn "
+      +"ich woher) Deinen\nMorgenGrauen handgearbeiteten Mini-Flammenwerfer "
+      +"(tm), richtest ihn aus und...\n"
+      +(ifoo ? "schaust leicht frustriert auf das Streichholz, in das er "
+        +"sich verwandelt hat."
+      : "feuerst@@adverb@@ einen riesigen Feuerball auf @@wen@@ ab.\n"
+        +"Es riecht auf einmal so verbrannt hier...");
+    out_vic="@@name@@ holt auf einmal irgendwoher einen MorgenGrauen "
+      +"handgearbeiteten\nMini-Flammenwerfer (tm), richtet ihn auf Dich "
+      +"aus und...\n"
+      +(ifoo ? "schaut ziemlich frustriert auf das Streichholz, in das "
+        +"sich das Ding\nverwandelt hat."
+      : "feuert@@adverb@@ einen riesigen Feuerball auf Dich ab.\n"
+        +"Dir wird so warm ums Herz...");
+    out_oth="@@name@@ holt auf einmal irgendwoher einen MorgenGrauen "
+      +"handgearbeiteten\nMini-Flammenwerfer (tm), richtet ihn "
+      +"auf@@ wen@@ aus und...\n"
+      +(ifoo ? "schaut ziemlich frustriert auf das Streichholz, in das "
+        +"sich das Ding\nverwandelt hat."
+      : "feuert@@adverb@@ einen riesigen Feuerball auf@@ wen@@ ab.\nEs "
+        +"riecht auf einmal irgendwie verbrannt hier ...");
+    return FeelIt();
+
+    // Special 2: remote verbs
+
+    /**************** Remote knuddeln ***************/
+    case "rknuddel":
+    case "rknuddl":
+    HELPCHECK("rknuddel");
+    if (ParseRemote(str))
+      return 1;
+    if (!who)
+      Return("Knuddle wen?\n");
+    if (CheckLife(NOT_SELF,0,
+      "Das bringt doch nix, lass es halt.",
+      0))
+        return 1;
+    if (present(who, environment()))
+      Return("Wenn jemand neben Dir steht, nimm knuddel.\n");
+    out_sel="Du knuddelst @@wen@@@@adverb@@ aus der Ferne.";
+    out_vic="@@gname@@ knuddelt Dich@@adverb@@ aus der Ferne.";
+    return FeelIt();
+
+    /**************** Remote winken ***************/
+    case "rwink":
+    HELPCHECK("rwink");
+    if (ParseRemote(str))
+      return 1;
+    if (!who)
+      Return("Winke wem zu?\n");
+    if (CheckLife(NOT_SELF,0,
+      "Sehr witzig. Pah.", 0))
+        return 1;
+    if (present(who, environment()))
+      Return("Wenn jemand neben Dir steht, nimm wink.\n");
+    out_sel="Du winkst @@wem@@@@adverb@@ aus der Ferne zu.";
+    out_vic="@@gname@@ winkt Dir@@adverb@@ aus der Ferne zu.";
+    return FeelIt();
+
+    /**************** Verbenliste ***************/
+    case "verb":
+    case "verben":
+    HELPCHECK("verb");
+    More(SOULHELP->Help());
+    return 1;
+
+    /**************** Adverbienverwaltung ***************/
+    case "adverb":
+    case "adverben":
+    case "adverbien": {   /* Das ist die richtige Form, aber wer weiss das? */
+    string f1,f2;
+    HELPCHECK("adverb");
+    if (!str || str=="#" || str=="$")
+      return zeige_adverbs((str=="#" ? 1 : (str=="$" ? 2 : 0)));
+    if (sscanf(str, "%s %s", f1,f2)==2)  {
+      f1 = lower_case(f1); // kleingeschrieben speichern, spart Umwandlung
+      if (f1=="")
+        Return("Hm, da muss wohl ein Leerzeichen zu viel gewesen sein. Bitte "
+          +"nochmal,\naber ohne zuviele Leerzeichen.\n");
+      if (f1=="?")  {
+        f2 = lower_case(f2);
+        string match;
+        if ((match=QueryStdAdverbs()[f2] || plr_adverbs[f2]))
+          write("Die Abkuerzung "+f2+" gehoert zu dem Adverb:\n"+match+LF);
+        else
+          write("Diese Abkuerzung ist bisher nicht definiert.\n");
+        return 1;
+      }
+      if (QueryStdAdverbs()[f1])
+        Return("Die Standardabkuerzungen koennen nicht neu definiert "
+          +"werden.\n");
+      if (sizeof(plr_adverbs)>=100) 
+      {
+        write("Mehr als 100 eigene Adverbien kannst Du nicht definieren.\n");
+        return 1;
+      }
+      if (plr_adverbs[f1])  {
+        plr_adverbs[f1]=f2;
+        write("OK, Adverb mit der Abkuerzung \""+f1+"\" auf \""+f2
+          +"\" gesetzt.\n");
+      }
+      else  {
+        if (sizeof(f1) > 6)
+          Return("Die Abkuerzung ist zu lang, bitte nicht mehr als "
+            +"6 Zeichen.\n");
+        plr_adverbs[f1]=f2;
+        write("OK, neues Adverb \""+f2+"\" mit der Abkuerzung \""+f1+"\".\n");
+      }
+    }
+    else  {
+      str = lower_case(str);
+      if (QueryStdAdverbs()[str])
+        Return("Die Standardadverben koennen nicht geloescht werden.\n");
+      else if (!plr_adverbs[str])
+        Return("Du hast kein Adverb mit dieser Abkuerzung.\n"
+          +"Syntax: adverb, um die Adverbien anzuzeigen,\n"
+          +"        adverb #, um nur Deine Adverbien anzuzeigen,\n"
+          +"        adverb $, um nur die Standardadverbien anzuzeigen,\n"
+          +"        adverb ? <Abkuerzung>, um nachzusehen, ob <Abkuerzung> "
+            +"definiert ist,\n"
+          +"        adverb <Abkuerzung> <Adverb>, um der <Abkuerzung> das "
+            +"<Adverb>\n"
+          +"               zuzuordnen,\n"
+          +"        adverb <Abkuerzung>, um das Adverb mit der <Abkuerzung> "
+            +"zu loeschen,\n");
+      else  {
+        write("OK, Adverb \""+plr_adverbs[str]+"\" geloescht.\n");
+        plr_adverbs=m_copy_delete(plr_adverbs, str);
+      }
+    }
+    return 1; 
+    }
+  }
+  return(0);  //fallthrough
+}
+
diff --git a/std/player/soulhelp.c b/std/player/soulhelp.c
new file mode 100644
index 0000000..27a48f9
--- /dev/null
+++ b/std/player/soulhelp.c
@@ -0,0 +1,317 @@
+// MorgenGrauen MUDlib
+//
+// player/soulhelp.c -- Hilfe zu den Soulkommandos
+//
+// $Id: soulhelp.c 7423 2010-02-07 22:56:38Z Zesstra $
+
+#pragma strong_types,save_types
+
+#include <wizlevels.h>
+#ifdef WECKE
+#undef WECKE
+#endif
+#define WECKE
+
+static string *wizcmds, *plrcmds, *ghostcmds;
+static mapping help;
+
+private string HelpVerb(string v);
+private string* SortIt(string *arr);
+
+
+/**
+  Initialisierung
+*/
+void create()  {
+	plrcmds=({
+		"zuck", "schmieg", "antworte", "applaudiere",
+		"betaste", "cls", "drehe (daeumchen)", "danke",
+		"druecke", "erroete", "flippe", "frage",
+		"furze", "gaehne", "gluckse", "grinse",
+		"guck", "haetschel", "hickse", "huepfe",
+		"huste", "keuche", "kichere", "klatsche",
+		"knabbere", "knickse", "knirsche", "knurre",
+		"knutsche", "kotze", "kuesse", "lache",
+		"laechle", "liebe", "nicke", "niese",
+		"gib", "pfeife", "ruelpse", "runzle",
+		"schmolle", "schmunzle", "schnippe", "schnarche",
+		"schnurre", "schreie", "schuettle", "seufze",
+		"singe", "sniefe/schniefe", "spucke", "starre",
+		"staune", "stiere", "stoehne", "stosse",
+		"streichle", "tanze", "tritt", "troeste",
+		"umarme", "verneige", "wackle", "waelze",
+		"weine", "winke", "zwinkere", "verben",
+		"aechze", "erbleiche", "fluche", "verfluche",
+		"kitzle", "nein", "deute", // "zeige",
+		"denke [text]", "knuddle", "taetschel", "wuschel",
+		"strecke ... [die] zunge raus", "kratz",
+		"grummel", "jubel / juble ... [zu]", "wuerg",
+		"gratuliere / beglueckwuensche", "raeusper",
+		"argl", "rotfl", "grunz", "kuschel", "atme ... auf",
+		"freue", "sniff", "grueble", "bohre ... [in der nase]",
+		"putze [nase]", "bibbere", "quietsche/quieke", "schluchze",
+		"schnaufe", "schnaube", "philosophiere", "sabbere",
+		"stimme [...] zu", "krieche", "mustere", "schaeme",
+		"schnalze ... [zunge]", "stampfe ... [auf]", "zitiere", "lobe",
+		"quake", "reibe ... [die] Augen|Haende", "stutze", "schweige",
+		"klopfe", "wundere", "stupse", "brummel", "entschuldige",
+		"mopper", "zeige", "traeume", "begruesse","jammer",
+	});
+#ifdef WECKE
+	plrcmds+=({"wecke"});
+#endif
+	wizcmds=({
+		"kick", "splash", "flame",
+	});
+	ghostcmds=({
+		"rassel/rassle", "heule", "erschrecke",
+	});
+	// Aufbau des help-mappings:
+	// key    : Verb, wie es in soul.c in CHECK_HELP geschrieben ist
+	// entry 1: &n = [<Name>]   &a = [<Adverb>]    &t=[<Text>}
+	//          ! danach heisst noetige Angabe, also ohne []
+	// entry 2: &g = Verhaelt sich bei Geistern anders
+	//          &a = Man kann "alle" als Ziel angeben
+	//          &d = Defaultadverb; muss am Ende angegeben werden
+	help=([
+		"zuck" : "&a [[mit [den]] schultern]"; "Gibt man nicht mindestens "
+			+"\"schultern\" an, so zuckt man zusammen. Das Defaultadverb gilt "
+			+"nur fuer das Schulterzucken.&dratlos",
+		"schmieg" : "&n &a"; "",
+		"antwort" : "&n &t!"; "",
+		"applaudier" : "&n &a"; "&g&a",
+		"begruess" : "&n! &a"; "",
+		"betast" : "&n! &a"; "",
+		"bibber" : "&a"; "",
+		"bohr" : "&a [[in [der]] nase]"; "",
+		"brummel" : "&a | &t"; "",
+		"cls" : ""; "Loescht den Bildschirm auf vt100-kompatiblen Terminals.",
+		"dreh" : "&a [daeumchen | daumen]"; "",
+		"dank" : "&n! &a"; "",
+		"drueck" : "&n! &a"; "&g&a&dzaertlich",
+		"entschuldig" : "&n &a"; "",
+		"erroet" : "&a"; "&g",
+		"erschreck" : "&n! &a"; "&g&dfuerchterlich",
+		"flipp" : "&a"; "&dtotal",
+		"frag" : "&n![|]&t!"; "",
+		"freu" : "&n &a"; "",
+		"furz" : "&a"; "&gHinterlaesst eine duftige Erinnerung im Raum.",
+		"gaehn" : "&a"; "&g",
+		"glucks" : "&a"; "&dwie ein Huhn",
+		"gratulier" : "&n! &a"; "",
+		"grins" : "&n &a"; "&g&a",
+		"gruebel" : "&a"; "",
+		"grummel" : "&a"; "",
+		"guck" : "&a"; "",
+		"haetschel" : "&n! &a"; "&g&a",
+		"hicks" : "&a"; "&g",
+		"huepf" : "&n &a"; "&g",
+		"hust" : "&n &a"; "&g",
+		"jubel" : "&n &a"; "",
+		"jammer" : "&a"; "",
+		"keuch" : "&a"; "&g&dvor Anstrengung",
+		"kicher" : "&n &a"; "",
+		"klatsch" : "&a"; "&g",
+		"klopf" : "&n! &a"; "",
+		"knabber" : "&n! &a"; "&g",
+		"knicks" : "&n &a"; "&g&a",
+		"knirsch" : "&a"; "&g",
+		"knurr" : "&n &a"; "&a",
+		"knutsch" : "&n! &a"; "&g",
+		"kotz" : "&n &a"; "&g",
+		"kriech" : "&n &a"; "",
+		"kuess" : "&n! &a"; "&gKann ausserdem bei bestimmten Gelegenheiten "
+			+"gewisse Nebeneffekte haben.",
+		"lach" : "&n &a"; "&g&a",
+		"laechel" : "&n &a"; "&g&a",
+		"lieb" : "&n! &a"; "",
+		"lob" : "{&n! &a} | &t!"; "Kann einen Nebeneffekt haben.",
+		"mopper" : "&a"; "",
+		"muster" : "&n! &a"; "",
+		"nick" : "&n &a"; "&a&dzustimmend",
+		"nies" : "&a"; "&g",
+		"gib" : "&n! &a [[eine] ohrfeige]"; "&g&a",
+		"pfeif" : "&n &a [nach]"; "&g&danerkennend",
+		"philosophier" : "&a | &t"; "",
+		"putz" : "&a [[die] nase]"; "",
+		"quak" : "&a"; "Geht als Frosch wesentlich besser.",
+		"quiek" : "&a"; "&dvergnuegt",
+		"raeusper" : "&a"; "",
+		"reib" : "&a [[die] Augen] | [[die] Haende]"; "Ohne weitere Angabe reibt "
+			+"man sich die Augen;&dmuede bzw. vergnuegt",
+		"ruelps" : "&a"; "&g",
+		"runzel" : "&a"; "&g",
+		"sabber" : "&n &a"; "",
+		"schaem" : "&a"; "",
+		"schluchz" : "&a"; "&dherzzerreissend",
+		"schmoll" : "&a"; "&g",
+		"schmunzel" : "&a"; "",
+		"schnalz" : "&a [[mit [der]] zunge]"; "",
+		"schnaub" : "&a"; "&dentruestet",
+		"schnauf" : "&a"; "&dvor Anstrengung",
+		"schnipp" : "&a"; "&g",
+		"schnarch" : "&a"; "&g&dlaut",
+		"schnief" : "&a"; "&g",
+		"schnurr" : "&a"; "&dzufrieden",
+		"schrei" : "&n &a"; "&g",
+		"schuettel" : "&n &a"; "&g&aHat einen Unterschied, je nachdem, ob ein Name"
+			+" angegeben wurde oder nicht.",
+		"schweig" : "&a"; "",
+		"seufz" : "&a"; "&g",
+		"sing" : "&a | &t"; "",
+		"sniff" : "&n &a"; "&dtraurig",
+		"spuck" : "&n &a"; "&g",
+		"stampf" : "&a"; "",
+		"starr" : "&n &a"; "Standardadverb gilt hier nur bei Angabe eines Namens."
+			+"&dvertraeumt",
+		"staun" : "&n &a"; "",
+		"stier" : "&n &a"; "&g",
+		"stimm" : "&n &a"; "",
+		"stoehn" : "&a"; "&g",
+		"stoss" : "&n! &a"; "&g",
+		"streichel" : "&n! &a"; "&g&a",
+		"stups" : "&n! &a"; "",
+		"stutz" : "&a"; "",
+		"tanz" : "&n &a"; "&g",
+		"traeum" : "{&n &a} | &t"; "Es koennen Probleme auftreten, wenn ein Adverb"
+			+" in freiem Text erkannt wird. Text wird genau so an \"traeum(s)t\" "
+			+"angehaengt, wie er angegeben wurde.",
+		"tritt" : "&n &a"; "&g&a",
+		"troest" : "&n! &a"; "",
+		"umarm" : "&n! &a"; "&g&a",
+		"verneig" : "&n &a"; "&g&a",
+		"wackel" : "&a [mit &t!]"; "&gMan kann mit allem moeglichem wackeln.",
+		"waelz" : "&a"; "&dvor Lachen",
+		"wart" : "&n &a"; "Man kann auf alles moegliche warten (warte alles "
+			+"moegliche)",
+		"weck" : "&n {&a | &t}"; "Piepst <Name> an und sendet ihm ggf. den Text.",
+		"wein" : "&n &a"; "&g",
+		"wink" : "&n &a"; "&a",
+		"wuerg" : "&n &a"; "",
+		"wunder" : "&n &a"; "",
+		"zeig" : "<Objekt-ID>"; "&aZeigt das Objekt (Langbeschreibung).",
+		"zitier" : "&n &a"; "Hier uebernimmt das Adverb ggf. die Stelle des "
+			+"zitierten, wenn dieser nicht anwesend ist (zitiere Ja /Jof)",
+		"zitter" : "&a"; "",
+		"zwinker" : "&n &a"; "&g",
+		"aechz" : "&a"; "",
+		"argl" : "&a"; "",
+		"atm" : "&a"; "&derleichtert",
+		"bewunder" : "&n! &a"; "",
+		"erbleich" : "&a"; "&g",
+		"fluch" : "&a"; "&g",
+		"grunz" : "&a"; "",
+		"kuschel" : "&n! &a"; "&g",
+		"rotfl" : "&a"; "",
+		"verfluch" : "{&n &a} | &t!"; "",
+		"kitzel" : "&n! &a"; "&g",
+		"nein" : "&a"; "&g",
+		"deut" : "&n! &a"; "&a",
+		"denk" : "&a | &t"; "&g",
+		"knuddel" : "&n! &a"; "&g&a",
+		"kratz" : "&a"; "",
+		"streck" : "&n &a [[die] zunge raus"; "&g",
+		"taetschel" : "&n! &a"; "&g",
+		"wuschel" : "&n! &a"; "",
+		"rassel" : "&a"; "&g&dfuerchterlich",
+		"heul" : "&a"; "&g&dschauerlich",
+		"kick" : "&n &a"; "&g&a",
+		"splash" : "&n! &a"; "&a",
+		"flame" : "&n! &a"; "&g&a",
+		"rknuddel" : "&n! &a"; "Nur fuer Ferngebrauch gedacht.",
+		"rwink" : "&n! &a"; "Nur fuer Ferngebrauch gedacht.",
+		"verb" : ""; "Listet alle derzeit moeglichen Verben auf.",
+		"adverb" : "[# | $ | {? <Abkuerzung>} | <Abkuerzung> [<Adverb>]]";
+			"Genauere Hilfe mit \"adverb ?\"",
+	]);
+}
+
+/**
+  Gibt die Hilfe zu einem Verb oder die Verbenuebersicht zurueck
+  \param verb   "string"  Ein Verb dessen Hilfe angezeigt werden soll.
+                0 oder "" Ueberblicksseite
+  \return 	String der an den Spieler ausgegeben werden kann.
+*/
+string Help(string verb)  {
+	string out;
+	if (verb && verb!="")
+		return HelpVerb(verb);
+	out="";
+	out+=break_string("Standardverben:\n"+implode(SortIt(plrcmds), ", ")
+		+".", 78, 0, 1);
+	if (IS_WIZARD(this_player()))
+		out+="\n"+break_string("Magierverben:\n"+implode(SortIt(wizcmds), ", ")
+			+".", 78, 0, 1);
+	if (this_player()->ghost())
+		out+="\n"+break_string("Geisterverben:\n"+implode(SortIt(ghostcmds), ", ")
+			+".", 78, 0, 1);
+	out+=break_string("\nAdverbien koennen entweder in der Abkuerzung (bei "
+		+"bereits definierten Adverbien) oder mit \"/<Text>\" angegeben werden. "
+		+"Bei der zweiten Methode wird <Text> so, wie er angegeben wurde, an "
+		+"Stelle des Adverbs eingesetzt.", 78, 0, 1);
+	out+="\n"+break_string("Eine kurze Hilfe zu einem Verb bekommst du mit einem der "
+                +"Befehle \"<Verb> -?\", \"<Verb> /?\", \"<Verb> -h\", \"<Verb> /h\" oder "
+                +"\"<Verb> hilfe\". (Da war jemand wirklich fleissig).", 78);
+	return out+"\n"+break_string("Einige Befehle reagieren auch auf leicht "
+		+"unterschiedliche Schreibweise. Diese Befehlsliste kann sich mit Deinem "
+		+"Zustand aendern.", 78);
+}
+
+/**
+  Sortiert ein array of string alphabetisch. 
+  \param arr Das zu sortierende Array
+  \return    Sortierte Kopie des Arrays
+*/
+private string*
+SortIt(string *arr)  {
+  return sort_array(arr, 
+           function int (string x, string y){ 
+             return lower_case(x) > lower_case(y) ;
+           } 
+         );
+}
+
+#define NO_MORE_E ({"cls","flipp","gruebel","grummel","guck","haetschel",\
+  "jubel","laechel","gib","runzel","schmunzel","schuettel","sniff",\
+  "streichel","tritt", "wackel","argl","rotfl","kuschel","kitzel","knuddel",\
+  "taetschel","wuschel","rassel","kick","splash","flame","verb","adverb",\
+  "nein","brummel","mopper",})
+
+/**
+  Gibt die Hilfe zu einem Verb 
+  \param v     Ein Verb dessen Hilfe angezeigt werden soll.
+  \return 	Hilfe zum Verb in Form eines Strings
+*/
+private string
+HelpVerb(string v)  {
+	string h,t;
+	if (member(m_indices(help),v)<0)
+		return "Dazu ist keine Hilfe vorhanden.\n";
+	h="Syntax:\n* "+v;
+	if (member(NO_MORE_E, v)<0)
+		h+="e";
+	h+=" ";
+	t=help[v,0];
+	t=implode(explode(t,"&t!"), "<Text>");
+	t=implode(explode(t,"&n!"), "<Name>");
+	t=implode(explode(t,"&t"), "[<Text>]");
+	t=implode(explode(t,"&n"), "[<Name>]");
+	t=implode(explode(t,"&a"), "[<Adverb>]");
+	h+=t;
+	t=help[v,1];
+	if (t!="")  {
+		h+="\nBemerkungen:";
+		if ((explode(t,"&"))[0]!="")
+			t="\n"+break_string((explode(t,"&"))[0], 76, "* ", 1)[0..<2]
+				+"&"+implode((explode(t,"&"))[1..<1],"&");
+		t=implode(explode(t,"&g"), "\n* Verhaelt sich bei Geistern anders "
+			+"als sonst.");
+		t=implode(explode(t,"&a"), "\n* Bei diesem Verb geht alle(n) als "
+			+"Zielangabe.");
+		if (strstr(t,"&d")>=0)  {
+			t=implode(explode(t,"&d"), "\n* Standardadverb ist ");
+			t+=".";
+		}
+	}
+	return break_string(h+t, 78, 2, 1);
+}
diff --git a/std/player/travel.c b/std/player/travel.c
new file mode 100644
index 0000000..3e07c0a
--- /dev/null
+++ b/std/player/travel.c
@@ -0,0 +1,506 @@
+
+/* 'reise' handling
+ *
+ * Ueberarbeitete und 
+ * erweiterte Version: Tilly@MorgenGrauen, 10.01.02
+ * Basierend auf     : base.c@SilberLand,
+ *                     Revision 3.55, Woody@SilberLand, 11.05.99
+ */   
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <player.h>
+#include <living/moving.h>
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <config.h>
+#include <wizlevels.h>
+#include <moving.h>
+#include <living.h>
+#include <attributes.h>
+#include <defines.h>
+#include <new_skills.h>
+#include <combat.h>
+#include <transport.h>
+
+void create(){}
+
+private string _traveller(string *s)
+{
+  switch (sizeof(s))
+  {
+    case 1 : return s[0];
+    case 2 : return s[0]+" oder "+s[1];
+  }
+  return efun::implode(s[0..<2],", ")+" oder "+s[<1];
+}
+
+static int reise(string str)
+{
+  mixed  t, ship, dstr;
+  string mit, nach, s1, s2;
+  int    i;
+
+  _notify_fail("Syntax: reise mit <transportmittel> nach <zielort>\n\n"
+               "Weitere Syntaxen moeglich, bitte 'hilfe reise' lesen.\n");
+
+  t = QueryProp(P_TRAVEL_INFO);
+  
+  if (!pointerp(t) || (sizeof(t) < 4) || !objectp(t[0]) || !objectp(t[1]))
+  {
+    SetProp(P_TRAVEL_INFO, t = 0);
+  }
+/* * * * 
+ * REISE
+ * * * */
+  if (!str)
+  {
+    if (!t)
+    {
+      write("Du hast keine Reiseroute festgelegt.\n");
+    }
+    else if (t[0] == environment())
+    {
+      if (environment()->id("Transporter"))
+      {
+        write(sprintf("Du reist mit %s%s.\n",environment()->name(WEM,1), 
+                                             t[2]?" "+t[3]:""));
+      }
+      else
+      {
+        write(sprintf("Du wirst mit %s%s reisen.\n",t[1]->name(WEM,1), 
+                                                   t[2]?" "+t[3]:""));
+      }
+    }
+    else
+    {
+       write(sprintf("Deine letzte Route (mit %s%s) ist hier nicht wirksam.\n",
+                     t[1]->name(WEM,1),
+                     t[2]?" "+t[3]:""));
+    }
+    return 1;
+  }
+
+  str = lower_case( _unparsed_args() );
+/* * * * * * * 
+ * REISE ROUTE
+ * * * * * * */
+  if (str == "route")
+  {
+    string *harbours;
+    
+    if (environment()->id("Transporter"))
+    {
+      if (environment()->QueryProp(P_NO_TRAVELING))
+      {
+        write(break_string(
+         capitalize(environment()->name(WER,1))+" hat gar keine "
+         "Reiseroute. Wo Dich das wohl hinfuehrt?",78));
+        return 1;
+      }
+
+      harbours = environment()->QueryHarbours(1);
+      
+      if (!sizeof(harbours) || !stringp(harbours[0]))
+      {
+        write("Die Reiseroute "+environment()->name(WESSEN,1)+" ist "
+             +"leider nicht bekannt.\n");
+      }
+      else
+      {
+        write(break_string(capitalize(efun::implode(harbours," - "))+".",78,
+         "Reiseroute "+environment()->name(WESSEN,1)+": ",BS_INDENT_ONCE));
+      }
+      return 1;
+    }
+    if (environment()->QueryProp(P_NO_TRAVELING))
+    {
+      write(break_string("Hier kannst Du leider keine Reiseroute waehlen. "
+                         "Was nicht heisst, dass hier kein Transportmittel "
+                         "verkehrt.",78));
+      return 1;
+    }
+    if (!pointerp(ship = TRAVELD->HasTransporter(environment())))
+    {
+      _notify_fail("Hier verkehrt kein Transportmittel.\n");
+      return 0;
+    }
+
+    write("Hier verkehren folgende Transportmittel \n"
+          "--------------------------------------- \n");
+         
+    for (i = 0; i < sizeof(ship); i++)
+    {    
+      if (!ship[i]->Query(P_SHORT)) continue;
+      
+      harbours = ship[i]->QueryHarbours(1);
+
+      if (sizeof(harbours) && stringp(harbours[0]))
+      {
+        write(break_string(efun::implode(harbours," - ")+".",78,
+                           ship[i]->Query(P_SHORT)+": ",BS_INDENT_ONCE));
+      }
+      else
+      {
+        write(ship[i]->Query(P_SHORT)+": Route unbekannt.\n");
+      }
+    }
+    return 1;
+  }
+/* * * * * * * 
+ * REISE AUS
+ * REISE NICHT
+ * * * * * * */
+  if (member((["aus","nicht"]),str))
+  {
+    if (!t)
+    {
+      write("Du hattest keine Reiseroute eingestellt.\n");
+    }
+    else
+    {
+      write("Du loeschst Deine Reiseroute.\n");
+    }
+    SetProp(P_TRAVEL_INFO, 0);
+    return 1;
+  }
+
+  str = regreplace(str,"\\<(zu|zum|zur|ins|ans)\\>","nach",0);
+/* * * * * * * * *
+ * REISE MIT NACH
+ * REISE NACH MIT
+ * * * * * * * * */
+  if ((sscanf(str, "mit %s nach %s", mit, nach) == 2) ||
+      (sscanf(str, "nach %s mit %s", nach, mit) == 2))
+  {
+    _notify_fail("Hier kannst Du leider keine Reiseroute waehlen.\n");
+
+    if (environment()->QueryProp(P_NO_TRAVELING))
+    {
+      return 0;
+    }
+    if (!sizeof(nach))
+    {
+      _notify_fail("Syntax: reise mit <transportmittel> nach <zielort>\n"
+                   "        reise nach <zielort> mit <transportmittel>\n");
+      return 0;
+    }
+    if (environment()->id("Transporter"))
+    {
+      if (environment()->id(mit))
+      {
+        command("reise nach "+nach);
+        return 1;
+      }
+      else
+      {
+        _notify_fail("Beende erstmal Deine aktuelle Reise mit "+
+                      environment()->name(WEM,1)+".\n");
+        return 0;
+      }
+    }
+    if (!pointerp(ship = TRAVELD->HasTransporter(environment(), mit)))
+    {
+      _notify_fail("So ein Transportmittel verkehrt hier nicht.\n");
+      return 0;
+    }
+    for (i = sizeof(ship) -1 ; i >= 0; i--)
+      if (!ship[i]->HasRoute(nach))
+      {
+        ship[i] = 0;
+      }
+
+    ship -= ({0});
+
+    if (pointerp(t) && objectp(t[1]) && (member(ship,t[1]) != -1))
+    {
+      ship = ({ t[1] });
+    }
+    if (sizeof(ship) > 1)
+    {
+      if (object_name(environment()) == ship[0]->HasRoute(nach)[0])
+      {
+        _notify_fail("Aber da bist Du doch bereits.\n");
+        return 0;
+      }
+     write("Dorthin kannst Du mit "+CountUp(map_objects(ship,"name",WEM))
+           +"reisen.\n");
+ 
+
+      dstr = filter( filter_objects(ship,"short"), lambda( ({'x}),
+             ({ #'==, ({#'environment, 'x}), environment() }) ) );
+
+      if (sizeof(dstr))
+      {
+        ship = dstr[0];
+      }
+      else
+      {
+        ship = ship[0];
+      }
+
+      dstr = ship->HasRoute(nach);
+
+      write(sprintf("Du entscheidest Dich fuer %s und reist %s.\n",
+            ship->name(WEN,1),dstr[1]));
+    }
+    else if (sizeof(ship) < 1)
+    {
+      _notify_fail("Nach '"+capitalize(nach)+"' kann Dich das angegebene "
+                  +"Transportmittel leider nicht bringen.\n");
+      return 0;
+    }
+    else
+    {
+      ship = ship[0];
+      dstr = ship->HasRoute(nach);
+
+      if (object_name(environment()) == dstr[0])
+      {
+        _notify_fail("Aber da bist Du doch bereits.\n");
+        return 0;
+      }
+      if (t && stringp(t[2]))
+      {
+        if (t[2] == dstr[0])
+        {
+          _notify_fail("Aber das tust Du doch bereits.\n");
+          return 0;
+        }
+      }
+      write(sprintf("Ok, Du reist nun mit %s %s.\n",
+                     ship->name(WEM,1),dstr[1]));
+    }
+    if (environment(ship)==environment() && ship->short())
+    {
+      ship->Enter(this_object());
+    }
+    SetProp(P_TRAVEL_INFO, ({ environment(), ship, dstr[0], dstr[1] }) );
+    return 1;
+  }
+/* * * * * * *
+ * REISE NACH
+ * * * * * * */
+  if (sscanf(str,"nach %s",nach))
+  {
+    _notify_fail("Hier kannst Du leider keine Reiseroute waehlen.\n");
+
+    if (environment()->QueryProp(P_NO_TRAVELING))
+    {
+      return 0;
+    }
+    if (environment()->id("Transporter"))
+    {
+      if (!dstr = environment()->HasRoute(nach))
+      {
+        _notify_fail("Dorthin kann Dich "+environment()->name(WER,1)+
+                     " leider nicht bringen.\n");
+        return 0;
+      }
+      if (t && stringp(t[2]))
+      {
+        if (t[2] == dstr[0])
+        {
+          _notify_fail("Aber das tust Du doch bereits.\n");
+          return 0;
+        }
+      }
+      write(sprintf("Ok, Du reist jetzt mit %s %s.\n",
+                    environment()->name(WEM,1),dstr[1]));
+      
+      if (IS_WIZARD(this_object()))
+      {
+        write("Als Magier nimmst Du natuerlich die Abkuerzung.\n");
+        move(dstr[0],M_NOCHECK);
+        return 1;
+      }
+      SetProp(P_TRAVEL_INFO,({ environment(), 
+                               environment(), 
+                               dstr[0], 
+                               dstr[1] }) );
+
+      if (object_name(environment(ship = environment())) == dstr[0] && 
+          ship->short())
+      {
+        environment()->Leave(this_object());
+
+        if (environment() != ship)
+        {
+          SetProp(P_TRAVEL_INFO, 0);
+        }
+      }
+      return 1;
+    }
+    if (!pointerp(ship = TRAVELD->HasTransporter(environment())))
+    {
+      _notify_fail("Von hier aus kannst Du nicht reisen.\n");
+      return 0;
+    }
+    for (i = sizeof(ship) - 1; i >= 0; i--)
+      if (!ship[i]->HasRoute(nach))
+      {
+        ship[i] = 0;
+      }
+
+    ship -= ({ 0 });
+ 
+    if (pointerp(t) && objectp(t[1]) && (member(ship,t[1]) != -1))
+    {
+      ship = ({ t[1] });
+    }
+    if (sizeof(ship) > 1)
+    {
+      if (object_name(environment()) == ship[0]->HasRoute(nach)[0])
+      {
+        _notify_fail("Aber da bist Du doch bereits.\n");
+        return 0;
+      }
+
+      write(break_string("Dahin kannst Du mit "
+           +_traveller(map_objects(ship, "name", WEM))+" gelangen.",78));
+
+      dstr = filter(filter_objects(ship,"short"),lambda( ({'x}),
+            ({ #'==, ({#'environment, 'x}), environment() }) ) );
+
+      if (sizeof(dstr))
+      {
+        ship = dstr[0];
+      }
+      else
+      {
+        ship = ship[0];
+      }
+
+      dstr = ship->HasRoute(nach);
+
+      write(sprintf("Du waehlst %s und reist %s.\n",ship->name(WEN,1),
+                                                    dstr[1]));
+    }
+    else if (sizeof(ship) < 1)
+    {
+      _notify_fail("Nach '"+capitalize(nach)+"' kann Dich leider keines der "
+                  +"hier verkehrenden Transportmittel bringen.\n");
+      return 0;
+    }
+    else
+    {
+      ship = ship[0];
+      dstr = ship->HasRoute(nach);
+
+      if (object_name(environment()) == dstr[0])
+      {
+        _notify_fail("Aber da bist Du ja bereits.\n");
+        return 0;
+      }
+      else if (t && stringp(t[2]))
+      {
+        if (t[2] == dstr[0])
+        {
+          _notify_fail("Aber das tust Du doch bereits.\n");
+          return 0;
+        }
+      }
+      write(sprintf("Ok, Du reist nun mit %s %s.\n",ship->name(WEM),
+                                                    dstr[1]));
+    }
+    if (IS_WIZARD(this_object()))
+    {
+      write("Als Magier nimmst Du natuerlich die Abkuerzung.\n");
+      move(dstr[0],M_NOCHECK);
+      return 1;
+    }
+    if (environment(ship)==environment() && ship->short())
+    {
+      ship->Enter(this_object());
+    }
+    SetProp(P_TRAVEL_INFO, ({ environment(), ship, dstr[0], dstr[1] }) );
+    return 1;
+  }
+/* * * * * *
+ * REISE MIT
+ * * * * * */
+  if (sscanf(str, "mit %s", mit))
+  {
+    _notify_fail("Hier kannst Du leider keine Reiseroute waehlen.\n");
+
+    if (environment()->QueryProp(P_NO_TRAVELING))
+    {
+      return 0;
+    }
+    if (environment()->id("Transporter"))
+    {
+      if (environment()->id(mit))
+      {
+        _notify_fail("Aber das tust Du doch bereits.\n");
+        return 0;
+      }
+      else
+      {
+        _notify_fail("Beende erstmal Deine aktuelle Reise mit "+
+                      environment()->name(WEM,1)+".\n");
+        return 0;
+      }
+    }
+    if (t && objectp(t[1]) && t[1]->id(mit) && t[0] == environment())
+    {
+      _notify_fail("Aber das tust Du doch bereits.\n");
+      return 0;
+    }
+    if (!pointerp(ship = TRAVELD->HasTransporter(environment(),mit)))
+    {
+      _notify_fail("So ein Transportmittel verkehrt hier nicht.\n");
+      return 0;
+    }
+    if (sizeof(ship) > 1)
+    {
+      write("'"+capitalize(mit)+"' koennte "
+               +_traveller(map_objects(ship,"name",WER))+" sein.\n");
+
+      dstr = filter(filter_objects(ship,"short"),lambda( ({'x}),
+            ({ #'==, ({#'environment, 'x}), environment() }) ) );
+
+      if (sizeof(dstr))
+      {
+        ship = dstr[0];
+      }
+      else
+      {
+        ship = ship[0];
+      }
+      write(sprintf("Du waehlst %s.\n", ship->name(WEN,1)));
+    }
+    else if (sizeof(ship) < 1)
+    {
+      notify_fail("So ein Transportmittel verkehrt hier nicht.\n");
+      return 0;
+    }
+    else
+    {
+      ship = ship[0];
+      write(sprintf("Du reist nun mit %s.\n",ship->name(WEM,1)));
+    }
+    if (environment(ship)==environment() && ship->short())
+    {
+      ship->Enter(this_object());
+    }
+    if (pointerp(t) && stringp(t[2]) && stringp(t[3]) &&
+        member(ship->QueryHarbours(),t[2]) != -1)
+    {
+      write("Du behaeltst Dein bisheriges Reiseziel ("+t[3]+") bei.\n");
+      SetProp(P_TRAVEL_INFO, ({ environment(), ship, t[2], t[3] }) );
+    }
+    else
+    {
+      SetProp(P_TRAVEL_INFO, ({ environment(), ship, 0, 0 }) );
+    }
+    return 1;
+  }
+  return 0;
+}
+
diff --git a/std/player/util.c b/std/player/util.c
new file mode 100644
index 0000000..a6c8fa9
--- /dev/null
+++ b/std/player/util.c
@@ -0,0 +1,111 @@
+// MorgenGrauen MUDlib
+//
+// player/util. -- Utilities
+//
+// $Id: util.c 6371 2007-07-17 22:46:50Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include "/sys/player/util.h"
+#include "/sys/thing/properties.h"
+
+public void ShowPropList(string *props) 
+{
+  int i,j;
+
+  j=sizeof(props);
+
+  for ( i=0; i<j ; i++) 
+  {
+    write("*"+props[i]+": ");
+    PrettyDump(QueryProp(props[i]));
+    write("\n");
+  }
+}
+
+static void PrettyDump(mixed x) 
+{
+  if (pointerp(x)) 
+  {
+    DumpArray(x);
+  }
+  else if (mappingp(x))
+  {
+    DumpMapping(x);
+  }
+  else if (objectp(x)) 
+  {
+    write ("OBJ("+object_name(x)+")");
+  }
+  else if (stringp(x))
+  {
+    write("\""+x+"\"");
+  }
+  else
+  {
+    write (x);
+  }
+}
+
+static void DumpArray(mixed *x) 
+{
+  int i,j;
+
+  write ("({ ");
+  if ( (j=sizeof(x))>0 )
+  {
+    for ( i=0 ; i<(j-1) ; i++) 
+    {
+      PrettyDump(x[i]);
+      write(", ");
+    }
+    PrettyDump(x[i]);
+    write(" ");
+  }
+  write ("})");
+}
+
+static void DumpMapping(mapping x)
+{
+  int   i, c, s;
+  mixed *ind;
+
+  write("([ ");
+
+  if ( (c=sizeof(ind=m_indices(x)))<1 )
+  {
+    write(" ])");
+    return;
+  }
+
+  s=get_type_info(x,1);
+
+  DumpKeyValPair(x, ind[0], s);
+  for ( i=1 ; i<c ; i++ )
+  {
+    write(", ");
+    DumpKeyValPair(x, ind[i], s);
+  }
+  write(" ])");
+}
+
+// Lacht nicht ueber den Namen!!! -Boing
+// Nein, ueber den Namen lache ich nicht ... -Paracelsus
+static void DumpKeyValPair(mapping x, mixed key, int size)
+{ int j, vc;
+
+  PrettyDump(key);
+  write(" : ");
+  PrettyDump(x[key,0]);
+
+  for ( j=1; j<size; j++)
+  {
+    write("; ");
+    PrettyDump(x[key, j]);
+  }
+}
diff --git a/std/player/viewcmd.c b/std/player/viewcmd.c
new file mode 100644
index 0000000..5d7e618
--- /dev/null
+++ b/std/player/viewcmd.c
@@ -0,0 +1,754 @@
+// MorgenGrauen MUDlib
+//
+// player/viewcmd.c -- player view command handling
+//
+// $Id: viewcmd.c 9548 2016-04-17 19:28:22Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include "/sys/thing/properties.h"
+#include "/sys/living/put_and_get.h"
+#include "/sys/living/description.h"
+
+#include <container.h>
+#include <player.h>
+#include <properties.h>
+#include <rooms.h>
+#include <wizlevels.h>
+#include <defines.h>
+#include <moving.h>
+#include <new_skills.h>
+#include <ansi.h>
+
+#include <sys_debug.h>
+
+varargs mixed More(string str, int fflag, string returnto);
+
+void create()
+{
+  Set(P_BRIEF, SAVE, F_MODE);
+  Set(P_BLIND, SAVE, F_MODE);
+}
+
+int _toggle_brief()
+{
+  int brief;
+
+  if (query_verb()=="kurz")
+    brief=1;
+  else if (query_verb()=="ultrakurz")
+    brief=2;
+  else brief=0;
+  SetProp(P_BRIEF, brief);
+  write("Du bist nun im \""+
+        (brief?(brief==1?"Kurz":"Ultrakurz"):"Lang")+"\"modus.\n");
+  return 1;
+}
+
+private int sortinv(mixed a, mixed b) { return a[0] > b[0]; }
+
+private string collectinv(mixed obj)
+{
+  if(obj[0]=="") return 0;
+  return (obj[2] ? " " : "")
+       + obj[0]
+       + (obj[1] > 1 ? " ("+obj[1]+")" : "");
+}
+
+#define I_AUTOLOAD      1
+#define I_KEEP          4
+#define I_FORMATTED     16
+#define I_ARMOUR        64
+#define I_SORT          256
+#define I_WEAPON        1024
+#define I_FORCE_SORT    4096
+#define I_NO_TABLE      16384
+
+private string getflags(string arg, int flags)
+{
+  int no, i;
+  if(sizeof(arg) < 2) return 0;
+  no = (arg[0] == '-');
+
+  for(i = 1; i < sizeof(arg); i++)
+  {
+    switch(arg[i])
+    {
+    case 'a': flags |= I_AUTOLOAD << no; break;
+    case 'b': flags |= I_KEEP << no; break;
+    case 'f': flags |= I_FORMATTED << no; break;
+    case 'r': flags |= I_ARMOUR << no; break;
+    case 's': flags |= I_SORT << no; break;
+    case 'w': flags |= I_WEAPON << no; break;
+    case 'v': flags |= (I_ARMOUR | I_WEAPON) << !no; break;
+    case '1': flags |= I_NO_TABLE; break;
+        // Die Option macht nur Aerger und kommentiert ist sie eh nicht.
+        // Wer das dringend braucht, soll Wargons Schiebepuzzle benutzen.
+        //
+        // Tiamak, 15.10.2000
+        //    case 'S': flags |= I_FORCE_SORT << no; break;
+    default : return arg[i..i]; // wird ausgegeben an Spieler als unbekannt.
+    }
+  }
+  return 0;
+}
+
+static int _check_keep(object ob)
+{
+  return (ob->QueryProp(P_KEEP_ON_SELL))==geteuid(ME);
+}
+
+int _inventory(string str)
+{
+  mixed *args, output;
+  int ansi, i, flags, minv;
+  mixed inventory, weapons, armours, misc;
+  string format;
+
+  if(CannotSee()) return 1;
+
+  if((str = _unparsed_args()) && str!="")
+  {
+    string error;
+    error = "Benutzung: i[nventar] [-/+1abfrsvw]\n";
+    args = regexp(regexplode(str, "[-+][1abfrswv][1abfrswv]*"),
+                  "[-+][1abfrswv][1abfrswv]*");
+    if(!sizeof(args)) return (_notify_fail(error), 0);
+    if(sizeof(args = map(args, #'getflags/*'*/, &flags) - ({ 0 })))
+    {
+      printf("%s: Unbekanntes Argument.\n"+error, implode(args, ", "));
+      return 1;
+    }
+  }
+  // Fuer Spieler gehen nur sichtbare Objekte in den Algorithmus
+  if (IS_LEARNING(ME))  
+    inventory = all_inventory(ME);
+  else
+    inventory = filter_objects(all_inventory(ME), "short");
+  
+  ansi = member(({"vt100", "ansi"}), QueryProp(P_TTY)) != -1;
+  minv = 1 | (flags & (I_FORMATTED | (I_FORMATTED << 1)) ? 2 : 0);
+  format = (flags & I_NO_TABLE) ? "=" : "#";
+
+//  if(flags & (I_FORCE_SORT | I_FORCE_SORT << 1))
+//  {
+//    closure sf;
+//    sf = flags & I_FORCE_SORT ? #'> : #'<;
+//    s = sort_array(s, lambda(({'a, 'b}),
+//                             ({#'funcall, sf,
+//                                   ({#'||,({#'call_other,'a,"short"}),""}),
+//                                   ({#'||,({#'call_other,'b,"short"}),""})})));
+//    map_objects(s, "move", this_object());
+//    s = all_inventory(ME);
+//  }
+
+  if (flags & I_AUTOLOAD)
+    inventory = filter_objects(inventory,"QueryProp",P_AUTOLOADOBJ);
+  else if (flags & (I_AUTOLOAD << 1))
+    inventory -= filter_objects(inventory,"QueryProp",P_AUTOLOADOBJ);
+
+  if(flags & I_KEEP)
+    inventory = filter(inventory,#'_check_keep);
+  else if(flags & (I_KEEP << 1))
+    inventory -= filter(inventory,#'_check_keep);
+
+  armours = filter_objects(inventory, "QueryProp", P_ARMOUR_TYPE);
+  // Kleidung dazu addieren, vorher die erkannten Ruestungen abziehen, die
+  // muessen nicht nochmal durchiteriert werden.
+  armours += filter_objects(inventory-armours, "IsClothing");
+  // Ruestungen werden hier nicht abgezogen, weil es Kram gibt, welche sowohl
+  // Ruestung als auch Waffe ist.
+  weapons = filter_objects(inventory, "QueryProp", P_WEAPON_TYPE);
+  misc = inventory - weapons - armours; // rest ;-)
+
+  if(flags & I_WEAPON)
+  {
+    inventory = weapons; misc = ({});
+    if(!(flags & (I_ARMOUR))) armours = ({});
+  }
+  if(flags & I_ARMOUR)
+  {
+     inventory = armours; misc = ({});
+     if(!(flags & I_WEAPON)) weapons = ({});
+  }
+  if(flags & (I_WEAPON << 1)) { weapons = ({}); inventory = armours + misc; }
+  if(flags & (I_ARMOUR << 1)) { armours = ({}); inventory = weapons + misc; }
+
+  output = "";
+  if(flags & (I_FORMATTED | (I_FORMATTED << 1)))
+  {
+    inventory = make_invlist(this_player(), inventory, minv);
+    if(flags & (I_SORT | (I_SORT << 1)))
+    inventory = sort_array(inventory, #'sortinv/*'*/);
+    output += sprintf("%"+format+"-78s\n",
+                      implode(map(inventory,#'collectinv/*'*/),"\n"));
+  }
+  else
+  {
+    if(weapons && sizeof(weapons))
+    {
+      weapons = make_invlist(this_player(), weapons, minv);
+      if(flags & (I_SORT | (I_SORT << 1)))
+        weapons = sort_array(weapons, #'sortinv/*'*/);
+      output += (ansi?ANSI_BOLD:"") + "Waffen:" + (ansi?ANSI_NORMAL:"")+"\n"
+              + sprintf("%"+format+"-78s\n",
+                        implode(map(weapons, #'collectinv/*'*/), "\n"));
+    }
+    if(armours && sizeof(armours))
+    {
+      armours = make_invlist(this_player(), armours, minv);
+      if(flags & (I_SORT | (I_SORT << 1)))
+        armours = sort_array(armours, #'sortinv/*'*/);
+      output += (ansi?ANSI_BOLD:"") 
+              + "Kleidung & Ruestungen:" + (ansi?ANSI_NORMAL:"")+"\n"
+              + sprintf("%"+format+"-78s\n",
+                        implode(map(armours, #'collectinv/*'*/), "\n"));
+    }
+    if(misc && sizeof(misc))
+    {
+      misc = make_invlist(this_player(), misc, minv);
+      if(flags & (I_SORT | (I_SORT << 1)))
+        misc = sort_array(misc, #'sortinv/*'*/);
+      output += (ansi?ANSI_BOLD:"") + "Verschiedenes:" + (ansi?ANSI_NORMAL:"")+"\n"
+              + sprintf("%"+format+"-78s\n",
+                        implode(map(misc, #'collectinv/*'*/), "\n"));
+    }
+  }
+
+  // Spielerwunsch: 'inventar' sollte doch das Bezugsobjekt auf den Spieler
+  // aendern.
+  SetProp(P_REFERENCE_OBJECT, this_object());
+
+  if (output=="") 
+    output += (ansi?ANSI_BOLD:"")+"Die Liste ist leer."+(ansi?ANSI_NORMAL:"");
+  More(output);
+  return 1;
+}
+
+private nosave int exa_cnt;
+private nosave int exa_time;
+private nosave string *exa;
+
+varargs int _examine(string str, int mode)
+{
+  object base, *objs, env;
+  string what, detail, parent, out, error;
+  int i, size, done;
+
+  if(CannotSee()) return 1;
+
+  _notify_fail("Was willst Du denn untersuchen?\n");
+  if (!str) return 0;
+
+  if (member(({"boden","decke","wand","waende"}),old_explode(str," ")[0]) == -1) {
+    exa_cnt -= (time() - exa_time)/2;
+    exa_time = time();
+    exa_cnt++;
+    if (!exa)
+      exa = ({ str });
+    else
+      exa += ({ str });
+    if (exa_cnt > 10) {
+      log_file("ARCH/LOOK", 
+          sprintf("%s: %s in %s\n%@O\n",dtime(time()),getuid(this_object()), 
+            environment() ? object_name(environment()) : "???",exa), 150000);
+      exa_cnt = 0;
+      exa = ({});
+    }
+    else if (exa_cnt < 0) {
+      exa_cnt = 0;
+      exa = ({});
+    }
+  }
+  // do we look at an object in our environment ?
+  if (sscanf(str,"%s in raum", what) || sscanf(str,"%s im raum", what))
+      base = environment();
+  // is the object inside of me (inventory)
+  else if (sscanf(str,"%s in mir", what) || sscanf(str,"%s in dir", what))
+      base = this_object();
+  else {
+      what = str;
+      // get the last object we looked at
+      base = QueryProp(P_REFERENCE_OBJECT);
+      
+      // if a reference object exists, test for its existance in the room
+      // or in our inventory
+      if (objectp(base))
+      {
+       if (base == environment() || base==this_object())
+        {
+          // Umgebung oder Spieler selber sind als Bezugsobjekt immer in
+          // Ordnung, nichts machen.
+        }
+        // Das Referenzobjekt darf nicht unsichtbar sein.
+        else if (!base->short())
+          base = 0;
+        else if(member(deep_inventory(environment()), base) != -1)
+        {
+          foreach(env : all_environment(base)) {
+            // Ist eine Umgebung Living oder intransparenter Container oder
+            // unsichtbar?
+            if (living(env) || !env->QueryProp(P_TRANSPARENT)
+                || !env->short())
+            {
+              // in dem Fall ist ende, aber wenn das gefundene Env nicht
+              // dieses Living selber oder sein Env ist, wird das
+              // Referenzobjekt zusaetzlich genullt.
+              if (env != this_object() && env != environment())
+                base = 0;
+              break;
+            }
+          }
+        }
+        else
+          base = 0; // nicht im Raum oder Inventory
+      }
+  }
+
+  // scan input if we want a specific object to look at
+  if(sscanf(what, "%s an %s", detail, parent) == 2 ||
+     sscanf(what, "%s am %s", detail, parent) == 2 ||
+     sscanf(what, "%s in %s", detail, parent) == 2 ||
+     sscanf(what, "%s im %s", detail, parent) == 2)
+  {
+    // if an ref object exists get its inventory. (Oben wurde sichergestellt,
+    // dass das Referenzobjekt einsehbar ist)
+    if(base)
+      objs = base->locate_objects(parent, 1) || ({});
+    else {
+      // else get our inv and env
+      objs = environment()->locate_objects(parent, 1)
+           + locate_objects(parent, 1);
+    }
+    objs = filter_objects(objs, "short"); // nur sichtbare...
+    if(sizeof(objs) > 1)
+      return (notify_fail("Es gibt mehr als eine(n) "+capitalize(parent)+".\n"), 0);
+    else
+    {
+      if (sizeof(objs))
+        base = objs[0];
+      else
+        return (notify_fail("Hier ist kein(e) "+capitalize(parent)+".\n"), 0);
+    }
+    objs = 0;
+  }
+  else detail = what;
+
+  int base_was_env = 1;
+  do {
+    // if a base exists get its inventory, else get our inv and env
+    if (base)
+    {
+      if  (base == this_object() || base == environment() ||
+          (base->QueryProp(P_TRANSPARENT) && !living(base)))
+      {
+        // ich kann in base reingucken...
+        objs = base->locate_objects(detail, 1) || ({});
+      }
+      else
+      {
+        // Referenzobjekt da, aber nicht reinguckbar. base aber nicht nullen,
+        // denn es ist ja noch gueltig fuer Detailsuchen an base...
+        objs = ({});
+      }
+    }
+    else
+    {
+      objs = environment()->locate_objects(detail, 1)
+           + locate_objects(detail, 1);
+      base = environment();
+    }
+    objs = filter_objects(objs, "short"); // nur sichtbare...
+
+    if(!sizeof(objs))
+    {
+      // wenn keine Objekte gefunden wurden, wird nach Details gesucht...
+      if((out = base->GetDetail(detail, QueryProp(P_REAL_RACE),SENSE_VIEW)) ||
+         (out = base->GetDoorDesc(detail)))
+      {
+        SetProp(P_REFERENCE_OBJECT, base);
+        return (write(out), 1);
+      }
+      else
+      {
+        // wenn auch keine Details gefunden, dann schauen, ob Ende ist
+        // (base==env) oder wir evtl. noch im env suchen koennen.
+        if (base == environment())
+        {
+          if (base_was_env) {
+            // in diesem Fall war das Env das Bezugsobjekt - daher wegwerfen.
+            SetProp(P_REFERENCE_OBJECT, 0);
+            _notify_fail("Sowas siehst Du da nicht!\n");
+          }
+          else {
+            _notify_fail("Sowas siehst Du auch da nicht!\n");
+            // in diesem Fall war nicht das Env das Bezugsobjekt - es soll
+            // behalten und nicht geloescht werden.
+          }
+          return 0;
+        }
+        else {
+          base_was_env=0;
+          write(break_string("Du findest an "+base->name(WEM)
+                +" kein \"" + capitalize(detail) + "\"."
+                 " Dein Blick wendet sich der Umgebung zu.",78));
+          base = 0;
+        }
+      }
+    }
+    else  // Objekte gefunden!
+      done = 1;
+  } while(!done);
+
+  // Es muss min. ein (sichtbares) Objekt geben, sonst waere man hier nicht
+  // hingekommen.
+  object ob = objs[0];
+  SetProp(P_REFERENCE_OBJECT, ob);
+  tell_object(ME, ob->long(mode));
+  return 1;
+}
+
+varargs int _sense_exa(string str)
+{
+  object base, *objs, env;
+  string what, detail, parent, out, error;
+  int sense;
+
+  if(member(({"riech","rieche","schnupper","schnuppere"}),query_verb())!=-1)
+  {
+    _notify_fail("Du kannst nichts Besonderes riechen.\n");
+    sense = SENSE_SMELL;
+  }
+  else if(member(({"lausche","lausch","hoer","hoere"}),query_verb())!=-1)
+  {
+    if(QueryProp(P_DEAF))
+      return notify_fail("Du bist taub!\n"), 0;
+
+    _notify_fail("Du kannst nichts Besonderes hoeren.\n");
+    sense = SENSE_SOUND;
+  }
+  else if(member(({"taste","beruehre","beruehr"}),query_verb())!=-1)
+  {
+    sense = SENSE_TOUCH;
+    // ein "ab" ganz am Ende von str wird abgeschnitten, es soll sowohl "taste
+    // x ab" als auch "taste x" funktionieren.
+    if (str) {
+      _notify_fail("Sowas kannst Du hier nicht ertasten!\n");
+      string *tmp = explode(str," ");
+      if (sizeof(tmp) > 1 && tmp[<1] == "ab")
+        str = implode(tmp[0..<2], " ");
+    }
+    else
+      _notify_fail("Was willst Du denn abtasten?\n");
+  }
+  else if (member(({"lies","lese","les"}), query_verb()) > -1)
+  {
+    _notify_fail("Was willst Du lesen?\n");
+    if ( !str ) // Kein SENSE_DEFAULT zulassen.
+      return 0;
+    if (this_object()->CannotSee()) {
+      notify_fail("Du kannst nichts sehen!\n");
+      return 0;
+    }
+    sense = SENSE_READ;
+  }
+
+  if (!str) {
+    if(!detail =
+        environment()->GetDetail(SENSE_DEFAULT,QueryProp(P_REAL_RACE),sense))
+      return 0;
+    write(detail);
+    return 1;
+  }
+  else if(sscanf(str,"an %s",what)==1)
+    str=what;
+
+  // do we look at an object in our environment ?
+  if (sscanf(str,"%s in raum", what) || sscanf(str,"%s im raum", what))
+      base = environment();
+  // is the object inside of me (inventory)
+  else if (sscanf(str,"%s in mir", what) || sscanf(str,"%s in dir", what))
+      base = this_object();
+  else {
+      what = str;
+      // get the last object we looked at
+      base = QueryProp(P_REFERENCE_OBJECT);
+      
+      // if a reference object exists, test for its existance in the room
+      // or in our inventory
+      if (objectp(base))
+      {
+        if (base == environment() || base==this_object())
+        {
+          // Umgebung oder Spieler selber sind als Bezugsobjekt immer in
+          // Ordnung, nichts machen.
+        }
+        // Das Referenzobjekt darf nicht unsichtbar sein.
+        else if (!base->short())
+          base = 0;
+        else if(member(deep_inventory(environment()), base) != -1)
+        {
+          foreach(env : all_environment(base)) {
+            // Ist eine Umgebung Living oder intransparenter Container oder
+            // unsichtbar?
+            if (living(env) || !env->QueryProp(P_TRANSPARENT)
+                || !env->short())
+            {
+              // in dem Fall ist ende, aber wenn das gefundene Env nicht
+              // dieses Living selber oder sein Env ist, wird das
+              // Referenzobjekt zusaetzlich genullt.
+              if (env != this_object() && env != environment())
+                base = 0;
+              break;
+            }
+          }
+        }
+        else
+          base = 0; // nicht im Raum oder Inventory
+      }
+  }
+
+  // scan input if we want a specific object to look at
+  if(sscanf(what, "%s an %s", detail, parent) == 2 ||
+     sscanf(what, "%s am %s", detail, parent) == 2 ||
+     sscanf(what, "%s in %s", detail, parent) == 2 ||
+     sscanf(what, "%s im %s", detail, parent) == 2)
+  {
+    // if an ref object exists get its inventory. (Oben wurde sichergestellt,
+    // dass das Referenzobjekt einsehbar ist)
+
+    if(base)
+      objs = base->locate_objects(parent, 1) || ({});
+    else
+    {
+      // else get our inv and env
+      objs = environment()->locate_objects(parent, 1)
+           + locate_objects(parent, 1);
+    }
+    objs = filter_objects(objs, "short"); // nur sichtbare...
+    if(sizeof(objs) > 1)
+      return (notify_fail("Es gibt mehr als eine(n) "+capitalize(parent)+".\n"), 0);
+    else
+    {
+      if(sizeof(objs))
+          base = objs[0];
+      else
+          return (notify_fail("Hier ist kein(e) "+capitalize(parent)+".\n"), 0);
+    }
+    objs = 0;
+  }
+  else detail = what;
+
+  // wie auch immer haben wir jetzt ein Bezugsobjekt.
+  int maxtries=3;
+  do {
+    int base_was_env=1;
+    // als ersten werden in Frage kommende Objekte gesucht. Wenn base
+    // existiert (idR nur im ersten Durchlauf), wird dort gesucht, sonst in
+    // Env und Inv.
+    if (base)
+    {
+      if  (base == this_object() || base == environment() ||
+          (base->QueryProp(P_TRANSPARENT) && !living(base)))
+      {
+        // ich kann in base reingucken...
+        objs = base->locate_objects(detail, 1) || ({});
+      }
+      else
+      {
+        // Referenzobjekt da, aber nicht reinguckbar. base aber nicht nullen,
+        // denn es ist ja noch gueltig fuer Detailsuchen an base...
+        objs = ({});
+      }
+    }
+    else
+    {
+      objs = environment()->locate_objects(detail, 1)
+           + locate_objects(detail, 1);
+      base = environment();
+    }
+    objs = filter_objects(objs, "short"); // nur sichtbare...
+
+    if (sizeof(objs))
+    {
+      // Objekte gefunden, mal schauen, ob die taugen (d.h. fuer den jew. Sinn
+      // Infos haben. Wenn nicht, muessen wir weitersuchen.
+      // Aber erstmal die Objekte durchlaufen.
+      foreach(object ob: objs)
+      {
+        if (sense == SENSE_READ)
+        {
+          // Extrawurst: P_READ_MSG auch noch abfragen.
+          out = ob->QueryProp(P_READ_MSG);
+          if (!stringp(out))
+            out = ob->GetDetail(SENSE_DEFAULT,QueryProp(P_REAL_RACE),SENSE_READ);
+        }
+        else 
+          out=ob->GetDetail(SENSE_DEFAULT,QueryProp(P_REAL_RACE),sense);
+        if (stringp(out))
+        {
+          SetProp(P_REFERENCE_OBJECT, ob);
+          tell_object(ME, out);
+          return 1;
+        }
+      }
+    }
+
+    // Keine Objekte gefunden, die in Frage kommen. Nach Details suchen.
+    if(out = base->GetDetail(detail, QueryProp(P_REAL_RACE),sense))
+    {
+      SetProp(P_REFERENCE_OBJECT, base);
+      return (write(out), 1);
+    }
+    else
+    {
+      // Auch keine Details gefunden... Wenn wir uns noch das Env angucken
+      // koennen (weil base != env), dann machen wir das, ansonsten ist
+      // jetzt hier leider Ende...
+      if(base == environment())
+      {
+        if (base_was_env)
+          SetProp(P_REFERENCE_OBJECT, 0);
+        return 0;
+      }
+      else
+      {
+        // nochmal im naechsten Schleifendurchlauf ohne base probieren.
+        base = 0;
+        base_was_env = 0; 
+      }
+    }
+  } while(--maxtries);
+
+  // nach dieser Schleife sollte man nie ankommen...
+  raise_error(sprintf("_sense_exa(): zuviele Versuche, etwas zu finden."));
+
+  return 0;
+}
+
+varargs int look_into(string str,int mode)
+{
+  object *found_obs;
+
+  if( CannotSee() ) return 1;
+  _notify_fail("Wo willst Du denn reinschauen ?\n");
+  found_obs=find_obs(str,PUT_GET_NONE);
+  if (!found_obs)
+  {
+    if (environment() &&
+        (environment()->GetDetail(str,QueryProp(P_REAL_RACE))||
+         environment()->GetDoorDesc(str)))
+      _notify_fail("Da kannst Du so nicht reinsehen.\n");
+    return 0;
+  }
+  return _examine(str, mode);
+}
+
+/* Gebe die Umgebung des aktiven Spielers zurueck, lasse dabei  */
+/* rekursiv geschachtelte Raeume zu.                            */
+/* Wenn allow_short 0 ist, so wird immer die long-descr benutzt */
+varargs string env_descr(int allow_short,int flags, int force_short )
+{
+  object env;
+  int brief;
+
+  env = environment(ME);
+
+  if(!env)
+    return "Du schwebst im Nichts ... Du siehst nichts, rein gar nichts ...\n";
+
+  if (!force_short && (!allow_short || !QueryProp(P_BRIEF)))
+      return env->int_long(ME,ME,flags);
+
+  if (!flags && ((brief=QueryProp(P_BRIEF))>=2))
+      return "";
+
+  return env->int_short(ME,ME);
+}
+
+int _look(string str)
+{
+  string s;
+  int flag;
+
+  if(CannotSee()) return 1;
+
+  if(!str)
+  {
+    SetProp(P_REFERENCE_OBJECT, 0);
+    write( env_descr() );
+    return 1;
+  }
+  if(str=="-f" || str=="genau")
+  {
+    SetProp(P_REFERENCE_OBJECT, 0);
+    write( env_descr(0,2) );
+    return 1;
+  }
+  if(str=="-k" || str=="kurz")
+  {
+    SetProp(P_REFERENCE_OBJECT, 0);
+    write( env_descr(1,2,1) );
+    return 1;
+  }
+  if(str[0..2]=="-f "){
+    flag=2;
+    str=str[3..];
+  }
+  else if(str[0..5]=="genau "){
+    flag=2;
+    str=str[6..];
+  }
+  else flag = 0;
+  if (sscanf(str,"%s an",s)) str=s;
+  if (sscanf(str,"%s in mir",s)||sscanf(str,"%s in dir",s)) return _examine(str,flag);
+  if (sscanf(str,"in %s",s)) return look_into(s,flag);
+  return _examine(str,flag);
+}
+
+int _equipment(string arg)
+{
+  if (CannotSee()) return 1;
+  call_other("/std/player/invmaster/invmaster", "ShowInv", ME, arg);
+  return 1;
+}
+
+static mixed _query_localcmds()
+{
+  return
+    ({({"ausruestung", "_equipment",0,0}),
+      ({"i","_inventory",0,0}),
+      ({"inv","_inventory",0,0}),
+      ({"inventur","_inventory",0,0}),
+      ({"schau","_look",0,0}),
+      ({"schaue","_look",0,0}),
+      ({"unt","_examine",0,0}),
+      ({"untersuch","_examine",0,0}),
+      ({"betracht","_examine",0,0}),
+      ({"untersuche","_examine",0,0}),
+      ({"betrachte","_examine",0,0}),
+      ({"betr","_examine",0,0}),
+      ({"lausche","_sense_exa",0,0}),
+      ({"lausch","_sense_exa",0,0}),
+      ({"hoer","_sense_exa",0,0}),
+      ({"hoere","_sense_exa",0,0}),
+      ({"lies","_sense_exa",0,0}),
+      ({"lese","_sense_exa",0,0}),
+      ({"les","_sense_exa",0,0}),
+      ({"schnupper","_sense_exa",0,0}),
+      ({"schnuppere","_sense_exa",0,0}),
+      ({"riech","_sense_exa",0,0}),
+      ({"rieche","_sense_exa",0,0}),
+      ({"taste","_sense_exa",0,0}),
+      ({"beruehre","_sense_exa",0,0}),
+      ({"beruehr","_sense_exa",0,0}),
+      ({"kurz","_toggle_brief",0,0}),
+      ({"lang","_toggle_brief",0,0}),
+      ({"ultrakurz","_toggle_brief",0,0}) 
+    });
+}
diff --git a/std/post.c b/std/post.c
new file mode 100644
index 0000000..d0dbbb1
--- /dev/null
+++ b/std/post.c
@@ -0,0 +1,42 @@
+/* Neue std/post
+   (C) 1993 by Loco
+*/
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/room";
+
+#include "/mail/post.h"
+#include <properties.h>
+#include <rooms.h>
+
+void create() {
+  int i;
+  (::create());
+  SetProp(P_INT_SHORT,"Postamt");
+  SetProp(P_INT_LONG,"\
+Dies ist ein Testpostamt.\n\
+Von hier aus kannst Du Briefe an Deine Mitspieler schicken und Briefe von\n\
+ihnen lesen. Wenn Du das willst, tippe 'post' oder 'mail',\n\
+bzw. 'mail <spieler>'.\n");
+  SetProp( P_LIGHT, 1 );
+  AddCmd("post","do_mail");
+  AddCmd("mail","do_mail");
+  SetProp(P_INDOORS,1);
+    for (i=0;i<NRCABINS;++i) AddItem(MAILCABIN,REFRESH_REMOVE);
+    AddItem(COUNTER,REFRESH_REMOVE);
+}
+
+int do_mail(string str) {
+  object mailer;
+  if (this_interactive()!=this_player()) return 0;
+  mailer=clone_object(MAILER);
+  mailer->SetOfficeName(short());
+  mailer->do_mail(str);
+  return 1;
+}
+
+
diff --git a/std/preload_file b/std/preload_file
new file mode 100644
index 0000000..5717fca
--- /dev/null
+++ b/std/preload_file
@@ -0,0 +1,36 @@
+/secure/errord
+/secure/materialdb
+/p/daemon/eventd
+/p/daemon/channeld
+/room/void
+/std/shells/magier
+/p/daemon/routingd
+/p/daemon/sastatd
+/std/shells/elf
+/std/shells/human
+/std/shells/dwarf
+/std/shells/hobbit
+/std/shells/feline
+/secure/login
+/secure/mailer
+/secure/combat
+/obj/uhr
+/obj/mliste
+/secure/scoremaster
+/secure/explorationmaster
+/secure/questmaster
+/secure/lepmaster
+/secure/potionmaster
+/items/money
+/obj/tools/xtool
+/obj/tools/lupe
+/secure/udp_mail
+/secure/inetd
+/secure/awmaster
+/p/daemon/traveld
+/p/daemon/mand
+/p/daemon/svn2news
+/p/daemon/moneylog
+/p/daemon/uptime_master
+/players/tiamak/workroom
+/secure/zweities
diff --git a/std/pub.c b/std/pub.c
new file mode 100644
index 0000000..53b019a
--- /dev/null
+++ b/std/pub.c
@@ -0,0 +1,75 @@
+// MorgenGrauen MUDlib
+//
+// pub.c -- Alles, was eine Kneipe braucht.
+//
+// $Id: pub.c 6571 2007-10-21 14:41:10Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/room";
+inherit "/std/room/pub";
+
+#include <properties.h>
+
+protected void create()
+{
+  room::create();
+  pub::create();
+  SetProp(P_LIGHT,1);
+  SetProp(P_INDOORS,1);
+  SetProp(P_INT_LONG,
+   "Der Magier, der diesen Pub erschuf, war leider zu faul, eine "+
+   "Beschreibung\ndafuer herzustellen.\n");
+  SetProp(P_INT_SHORT,"generic pub");
+  AddItem("/obj/topliste",REFRESH_REMOVE);
+}
+
+protected void create_super() {
+      set_next_reset(-1);
+}
+
+void reset()
+{
+   room::reset();
+   pub::reset();
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/std/ranged_weapon.c b/std/ranged_weapon.c
new file mode 100644
index 0000000..5578483
--- /dev/null
+++ b/std/ranged_weapon.c
@@ -0,0 +1,275 @@
+// MorgenGrauen MUDlib
+//
+// ranged_weapon.c -- Standardobjekt fuer Fernkampfwaffen
+//
+// $Id: ranged_weapon.c 8820 2014-05-14 19:31:46Z Zesstra $
+
+// Gebraucht wird Munition mit einer MUN_* id. Dieses Objekt sollte ausserdem
+// P_DAM_TYPE setzen sowie P_SHOOTING_WC und ggf. P_HIT_FUNC. In der Regel
+// sollten diese Objekte units sein, da groessere Anzahlen davon gebraucht
+// werden.
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/living/skill_utils";
+inherit "/std/weapon";
+
+#include <ranged_weapon.h>
+
+#include <properties.h>
+#include <defines.h>
+#include <combat.h>
+#include <new_skills.h>
+#include <unit.h>
+
+static int last_shoot;
+
+protected void create()
+{
+    ::create();
+
+    SetProp(P_WEAPON_TYPE, WT_RANGED_WEAPON);
+    SetProp(P_STRETCH_TIME, 1);
+    SetProp(P_AMMUNITION, MUN_ARROW);
+    SetProp(P_SHOOTING_WC, 70);
+    SetProp(P_RANGE, 50);
+    SetProp(P_INFO, "Syntax: schiesse WAS auf WEN?\n");
+    // fuer den Fall, das man den Bogen als Knueppel nutzt...
+    SetProp(P_WC, 30);
+    SetProp(P_QUALITY, 100);
+
+    AddCmd( ({"schiess", "schiesse"}), "cmd_shoot");
+}
+      
+static int shoot_dam(mapping shoot)
+// ausgegliedert, damit man diese Funktion ggf. ueberschreiben kann.
+{   mixed   res;
+    object  hitfunc;
+    closure usk;
+
+    // diesselbe Formel wie bei Nahkampfwaffen, nur das hier DEX anstatt STR
+    // benutzt wird, und die WC der Munition noch addiert wird
+    shoot[SI_SKILLDAMAGE] = random(1+( 2*(shoot[P_WC]+shoot[P_SHOOTING_WC])
+                                     + 10*PL->QueryAttribute(A_DEX) )/3);
+    // HitFunc der Munition addieren
+    if ( objectp(hitfunc=(shoot[P_AMMUNITION]->QueryProp(P_HIT_FUNC))) )
+      shoot[SI_SKILLDAMAGE] += (hitfunc->HitFunc(shoot[SI_ENEMY]));
+
+    usk=symbol_function("UseSkill",PL);
+
+    // Schuss-Skill der Gilde fuer diesen Munitionstyp anwenden
+    if ( mappingp(res=funcall(usk,SHOOT(shoot[P_WEAPON_TYPE]),
+                              deep_copy(shoot))) )
+      SkillResTransfer(res, &shoot);
+
+    // Allgemeinen Schuss-Skill der Gilde anwenden
+    if ( mappingp(res=funcall(usk,SK_SHOOT,deep_copy(shoot))) )
+      SkillResTransfer(res, &shoot);
+
+    return shoot[SI_SKILLDAMAGE];
+}
+
+static string FindRangedTarget(string str, mapping shoot)
+{   string wen;
+    mixed  area;
+    int    psr;
+
+    if ( sscanf(str, "%s auf %s", str, wen)!=2 )
+    {
+        shoot[SI_ENEMY]=PL->SelectFarEnemy(PL->PresentEnemies(),1,4);
+        str = shoot[P_WEAPON_TYPE];
+    }
+    else if ( objectp(shoot[SI_ENEMY]=present(wen, environment(PL))) )
+    {
+        return str;
+    }
+    else if ( ( (area=(environment(PL)->QueryProp(P_TARGET_AREA)))
+               || (area=environment(environment(PL))) )
+             && ( objectp(area)
+                 || ( stringp(area)
+                     && objectp(area=find_object(area)) ) )
+             && (psr=environment(PL)->QueryProp(P_SHOOTING_AREA)) )
+    {
+        shoot[SI_ENEMY]=present(wen, area);
+        if ( shoot[SI_ENEMY] && (psr>QueryProp(P_RANGE)) )
+        {
+            write(break_string((shoot[SI_ENEMY]->Name(WER))+
+                " ist leider ausserhalb der Reichweite "+name(WESSEN, 1)+".",
+               78));
+            return 0;
+         }        
+    }
+
+    if ( !shoot[SI_ENEMY] )
+    {
+        write("Es ist kein Feind vorhanden auf den Du schiessen koenntest.\n");
+        return 0;
+    }
+
+    return str;
+}
+
+static int cmd_shoot(string str)
+{   int    dam;
+    string no_at;
+    object quiver;
+    mapping shoot;
+
+    if ( PL->QueryProp(P_GHOST) )
+    {
+        write("Deine immateriellen Finger gleiten durch die Waffe hindurch.\n");
+        return 1;
+    }
+   
+    if ( !QueryProp(P_NOGET) )
+    {
+        // Katapulte oder aehnliches die von Magiern aufgestellt werden
+        // muessen natuerlich nicht gezueckt werden :).
+        if ( !QueryProp(P_WIELDED) )
+        {
+            notify_fail(break_string("Du solltest "+name(WEN, 1)+
+                                     " dazu schon zuecken.", 78));
+            return 0;
+        }
+
+        if ( (PL->InFight()) && ((PL->PresentPosition())<2) )
+        {
+            write(break_string(
+                "Du solltest Dich erst etwas weiter zurueckziehen, um mit "+
+                name(WEM, 1)+" schiessen zu koennen!", 78));
+            return 1;
+        }
+    }
+
+    if ( ( (QueryProp(P_EQUIP_TIME)+QueryProp(P_STRETCH_TIME)*2) > time() )
+        || ( (last_shoot+QueryProp(P_STRETCH_TIME)) > absolute_hb_count() ) )
+    {
+        write(break_string("Du kannst mit "+name(WEM, 1)+
+                           " noch nicht wieder schiessen.", 78));
+        return 1;
+    }
+
+    if ( (PL->QueryProp(P_DISABLE_ATTACK))>0 )
+    {
+        write(break_string("Solange Du noch gelaehmt bist, kannst Du "+
+                           name(WEN, 1)+" nicht benutzen!", 78));
+        return 1;
+    }
+
+    if ( PL->QueryProp(P_ATTACK_BUSY) )
+    {
+        write("Nicht so hektisch! So schnell bist Du nicht.\n");
+        return 1;
+    }
+
+    // P_ATTACK_BUSY natuerlich auch setzen...
+    PL->SetProp(P_ATTACK_BUSY,1);
+
+    // Info-Mapping erzeugen
+    shoot = ([ P_WEAPON       : ME,
+               P_WEAPON_TYPE  : QueryProp(P_AMMUNITION),
+               P_STRETCH_TIME : QueryProp(P_STRETCH_TIME),
+               P_WC           : QueryProp(P_SHOOTING_WC),
+               SI_SPELL       : 0
+            ]);
+
+    if ( !stringp(str) || str=="" )
+      str=shoot[P_WEAPON_TYPE];
+    else if (str[0..3]=="mit ")
+      str=str[4..];
+    else if (str[0..3]=="auf ")
+      str=shoot[P_WEAPON_TYPE]+" "+str;
+
+    if ( !(str=FindRangedTarget(str,shoot)) )
+      return 1;
+
+    if ( shoot[SI_ENEMY]->QueryProp(P_GHOST) )
+    {
+        write(break_string("Aber "+(shoot[SI_ENEMY]->name(WER, 1))+
+                           " ist doch ein Geist!", 78));
+        return 1;
+    }
+    else if ( no_at=(shoot[SI_ENEMY]->QueryProp(P_NO_ATTACK)) )
+    {
+        if ( stringp(no_at) )
+          write(no_at);
+        else
+          write(break_string("Du kannst "+(shoot[SI_ENEMY]->name(WEN, 1))+
+                             " nicht angreifen.", 78));
+        return 1;
+    }
+    else if ( shoot[SI_ENEMY]==PL )
+    {
+        write("Du kannst doch nicht auf Dich selbst schiessen!\n");
+        return 1;
+    }
+
+    if ( !(shoot[P_AMMUNITION]=present(str, PL))
+        && ( !objectp(quiver=PL->QueryArmourByType(AT_QUIVER))
+            || !(shoot[P_AMMUNITION]=present(str, quiver)) ) )
+    {
+        if ( str==shoot[P_WEAPON_TYPE] )
+            write(break_string("Du hast keine passende Munition bei Dir, um "+
+                               "mit "+name(WEM, 1)+" schiessen zu koennen.",
+                               78));
+        else
+          write(break_string("Du hast kein '"+str+"' bei Dir.", 78));
+        return 1;
+    }
+
+    if ( !shoot[P_AMMUNITION]->id(shoot[P_WEAPON_TYPE]) )
+    {
+        write(break_string((shoot[P_AMMUNITION]->Name(WER))+" kannst Du mit "+
+                           name(WEM, 1)+" nicht verschiessen.", 78));
+        return 1;
+    }
+
+    shoot[P_AMMUNITION]->SetProp(U_REQ, 1);
+
+    shoot[SI_SKILLDAMAGE_MSG]  = shoot[SI_SKILLDAMAGE_MSG2]
+                               = shoot[P_AMMUNITION]->name(WEN);
+    shoot[SI_SKILLDAMAGE_TYPE] = ( shoot[P_AMMUNITION]->QueryProp(P_DAM_TYPE)
+                                  || ({DT_PIERCE}) );
+
+    shoot[P_SHOOTING_WC]  = shoot[P_AMMUNITION]->QueryProp(P_SHOOTING_WC);
+
+    dam=shoot_dam(shoot);
+
+    // Textausgabe
+    if (shoot[SI_ENEMY])
+    {
+      tell_object(PL,break_string(
+          "Du schiesst "+shoot[SI_SKILLDAMAGE_MSG]+" auf "+
+          shoot[SI_ENEMY]->name(WEN, 1)+".", 78) );
+      tell_room(environment(PL), break_string(
+          PL->Name(WER)+" schiesst "+shoot[SI_SKILLDAMAGE_MSG2]+" auf "+
+          shoot[SI_ENEMY]->name(WEN, 1)+".", 78),
+          ({ shoot[SI_ENEMY], PL }) );
+      if ( environment(PL)!=environment(shoot[SI_ENEMY]) )
+        tell_room(environment(shoot[SI_ENEMY]),break_string(
+            PL->Name(WER)+" schiesst "+shoot[SI_SKILLDAMAGE_MSG2]+" auf "+
+            shoot[SI_ENEMY]->name(WEN, 1)+".", 78),
+            ({ shoot[SI_ENEMY] }) );
+      tell_object(shoot[SI_ENEMY], break_string(
+          PL->Name(WER)+" schiesst "+shoot[SI_SKILLDAMAGE_MSG2]+
+          " auf Dich ab.",78) );
+
+      shoot[SI_ENEMY]->Defend(dam,shoot[SI_SKILLDAMAGE_TYPE],shoot[SI_SPELL],PL);
+    }
+    // Munition verbrauchen
+    if ( shoot[P_AMMUNITION]->IsUnit() )
+      shoot[P_AMMUNITION]->AddAmount(-1);
+    else
+      shoot[P_AMMUNITION]->remove();
+
+    // es wird an dieser stelle absolute_hb_count anstelle von time() benutzt
+    // um dem Spieler wirklich die vollen 2 sek Zeit zu geben um zu reagieren
+    // ohne das er dadurch einen Schaden hat (wie er es z.b. bei time() haette)
+    last_shoot=absolute_hb_count();
+
+    return 1;
+}
+
diff --git a/std/restriction_checker.c b/std/restriction_checker.c
new file mode 100644
index 0000000..f2d4248
--- /dev/null
+++ b/std/restriction_checker.c
@@ -0,0 +1,323 @@
+// MorgenGrauen MUDlib
+//
+// restriction_checker.c -- Beschraenkungen in Gilden, Spells und Skills
+//
+// $Id: restriction_checker.c 8754 2014-04-26 13:12:58Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+//#pragma range_check
+#pragma no_shadow
+
+inherit "/std/util/executer";
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <properties.h>
+#include <new_skills.h>
+#include <wizlevels.h>
+#include <questmaster.h>
+
+#define DEBUG(x) if (find_player("zesstra")) tell_object(find_player("zesstra"),x)
+
+// erweitert um das <pl> argument und die extra-Argument, die bereits in <fun>
+// angegeben wurden. (*grummel*)
+protected mixed execute_anything(mixed fun, object pl, varargs mixed args)
+{
+  //DEBUG(sprintf("ex_any(): Fun: %O, Ob: %O, args: %O\n",fun,pl,args));
+  // wenn in fun schon extraargumente stehen, dann pl einfuegen und args
+  // dranhaengen.
+  if (pointerp(fun) && sizeof(fun) > 2)
+  {
+    // args kann ({}) sein, wenn keine Extraargumente uebergeben wurden, macht
+    // aber nix.
+    args = ({pl}) + fun[2..] + args;
+    fun = fun[0..1];
+    return ::execute_anything(fun, args...);
+  }
+  // s.o.
+  return ::execute_anything(fun, pl, args...);
+}
+
+mapping race_modifier(object pl, mapping map_ldfied) {
+  mapping rmap,help;
+
+  if (mappingp(map_ldfied) && member(map_ldfied,SM_RACE) && objectp(pl) &&
+      mappingp(rmap=map_ldfied[SM_RACE]))
+//    if (mappingp(help=rmap[pl->_query_real_race()]))
+    if (mappingp(help=rmap[pl->QueryProp(P_REAL_RACE)]))
+        return map_ldfied+help;
+    else if(member(rmap,"*") && mappingp(help=rmap["*"]))
+        return map_ldfied+help;
+  return map_ldfied;
+}
+
+string
+check_restrictions(object pl, mapping restr) {
+  /* Folgende Einschraenkungen koennen geprueft werden:
+   * - Mindestwerte bei allen Attributen und Properties Level, QP, XP
+   * - bestimmte Rassen koennen ausgeschlossen werden bzw. erlaubt werden
+   *   (SR_EXCLUDE_RACE ist ein Array von ausgeschlossenen Rassen,
+   *    SR_INCLUDE_RACE eines mit eingeschlossenen)
+   * - SR_RACE kann ein Mapping sein, dass Rassen ein eigenes
+   *   Restriction-mapping zuordnet. "*" steht fuer alle anderen Rassen.
+   */
+  closure cl,cl2;
+  string race, guild;
+
+  if (!mappingp(restr) || !sizeof(restr)) return 0; // Keine Einschraenkungen
+  if (!objectp(pl)) return "";
+  if (pl->QueryDisguise())
+    return "Zieh erst mal den Tarnhelm aus.\n";
+
+  restr=race_modifier(&pl,&restr);
+
+  cl=symbol_function("QueryProp",pl);
+  cl2=symbol_function("QueryAttribute",pl);
+
+  foreach(string to_check, mixed condition: restr)
+  {
+    switch(to_check)
+    {
+      case P_LEVEL:
+        if (funcall(cl,P_LEVEL) < condition)
+          return "Deine Stufe reicht dafuer nicht aus.\n";
+        break;
+      case P_GUILD_LEVEL:
+        if (funcall(cl,P_GUILD_LEVEL) < condition)
+          return "Deine Gildenstufe reicht dafuer nicht aus.\n";
+        break;
+      case SR_SEER: // das macht nun wahrlich nur bei interactives sinn!!!
+        if (condition && query_once_interactive(pl) && !IS_SEER(pl))
+          return "Du musst dazu erst Seher werden.\n";
+        break;
+      case P_XP:
+        if (funcall(cl,P_XP) < condition)
+          return "Du hast nicht genuegend Erfahrung dafuer.\n";
+        break;
+      case P_QP:
+        if (funcall(cl,P_QP) < condition)
+          return "Du hast nicht genuegend Aufgaben geloest.\n";
+        break;
+      case P_ALCOHOL:
+        if (funcall(cl,P_ALCOHOL) >= condition)
+          return "Du bist zu besoffen.\n";
+        break;
+      case P_DRINK:
+        if (funcall(cl,P_DRINK) >= condition)
+          return "Du hast zuviel getrunken.\n";
+        break;
+      case P_FOOD:
+        if (funcall(cl,P_FOOD) >= condition)
+          return "Du hast zuviel gegessen.\n";
+        break;
+      case P_DEAF:
+        if (funcall(cl,P_DEAF))
+          return "Du kannst nichts hoeren.\n";
+        break;
+      case P_BLIND:
+        if (funcall(cl,P_BLIND))
+          return "Du kannst nichts sehen.\n";
+        break;
+      case P_FROG:
+        if (funcall(cl,P_FROG))
+          return "Als Frosch kannst Du das leider nicht.\n";
+        break;
+      case A_INT:
+        if (funcall(cl2,A_INT) < condition)
+          return "Deine Intelligenz reicht dafuer nicht aus.\n";
+        break;
+      case A_DEX:
+        if (funcall(cl2,A_DEX) < condition)
+          return "Deine Geschicklichkeit reicht dafuer nicht aus.\n";
+        break;
+      case A_CON:
+        if (funcall(cl2,A_CON) < condition)
+          return "Deine Ausdauer reicht dafuer nicht aus.\n";
+        break;
+      case A_STR:
+        if (funcall(cl2,A_STR) < condition)
+          return "Deine Staerke reicht dafuer nicht aus.\n";
+        break;
+      case SR_BAD:
+        if (funcall(cl,P_ALIGN) < condition)
+          return "Du bist zu boese.\n";
+        break;
+      case SR_GOOD:
+        if (funcall(cl,P_ALIGN) > condition)
+          return "Du bist nicht boese genug.\n";
+        break;
+      case SR_MIN_SIZE:
+        if (funcall(cl,P_SIZE) < condition)
+          return "Du bist dafuer zu klein.\n";
+        break;
+      case SR_MAX_SIZE:
+        if (funcall(cl,P_SIZE) > condition)
+          return "Du bist dafuer zu gross.\n";
+        break;
+      case SR_FREE_HANDS:
+        if (condition > (funcall(cl,P_MAX_HANDS)-funcall(cl,P_USED_HANDS)))
+          return "Du hast nicht genug freie Haende dafuer.\n";
+        break;
+      case SR_EXCLUDE_RACE:
+        if (IS_LEARNER(pl))
+          race=funcall(cl,P_RACE);
+        else
+          race=funcall(cl,P_REAL_RACE); //race=pl->_query_real_race();
+        if ( pointerp(condition) && member(condition,race)>=0 )
+          return ("Als "+race+" kannst Du das nicht.\n");
+        break;
+     case SR_INCLUDE_RACE:
+        if (IS_LEARNER(pl))
+          race=funcall(cl,P_RACE);
+        else
+          race=funcall(cl,P_REAL_RACE); //race=pl->_query_real_race();
+        if (pointerp(condition) && member(condition,race)<0)
+          return ("Als "+race+" kannst Du das nicht.\n");
+        break;
+     case SR_EXCLUDE_GUILD:
+        guild=funcall(cl,P_GUILD);
+        if (pointerp(condition) && member(condition,guild)>=0)
+          return ("Mit Deiner Gildenzugehoerigkeit kannst Du das nicht.\n");
+        break;
+      case SR_INCLUDE_GUILD:
+        guild=funcall(cl,P_GUILD);
+        if (pointerp(condition) && member(condition,guild)<0)
+          return ("Mit Deiner Gildenzugehoerigkeit kannst Du das nicht.\n");
+        break;
+     case SR_FUN:
+        string res=execute_anything(condition, pl);
+        if (stringp(res))
+          return res;
+        break;
+     case SR_PROP:
+        foreach ( string propname, mixed value, string msg: condition ) {
+          if ( funcall(cl, propname) != value )
+            return ( stringp(msg) && sizeof(msg) ? msg :
+              "Koennte es sein, dass Du vergessen hast, etwas zu "
+              "erledigen?\n");
+        }
+        break;
+     case SR_QUEST:
+        if (pointerp(condition))
+        {
+          // geloeste Quests aus der Liste der Bedingungen entfernen
+          condition -= filter(condition, "QueryQuest", pl);
+          if ( sizeof(condition) )
+            return ("Du musst zuerst das Abenteuer '"+condition[0]+
+              "' loesen.\n");
+        }
+        break;
+     case SR_MINIQUEST:
+        if (pointerp(condition))
+        {
+          closure unsolved_MQs = function int (mixed mq_num)
+            { return !(QM)->HasMiniQuest(pl, mq_num); };
+          if ( sizeof(filter(condition, unsolved_MQs)) )
+            return "Du hast eine benoetigte Miniquest noch nicht geloest.\n";
+            // Der Name der MQ wird absichtlich nicht explizit ausgegeben.
+        }
+        break;
+    } // switch
+  } // foreach
+  return 0;
+}
+
+varargs mixed
+GetData(string dname, mapping map_ldfied, object pl) {
+  mixed dat,res;
+
+  if (!dname || !mappingp(map_ldfied)) return 0;
+  if (closurep(dat=map_ldfied[dname]) && (res=funcall(dat,pl,map_ldfied)))
+        return res;
+  return dat;
+
+}
+
+varargs int
+GetValue(string vname, mapping map_ldfied, object pl) {
+  mixed dat,res;
+
+  // printf("GetValue(%O): %O\n",vname,map_ldfied);
+  if (!vname || !map_ldfied) return 0;
+  if ((dat=map_ldfied[vname]) && (res=execute_anything(dat,pl,map_ldfied)))
+        return res;
+  // printf("Value: %O\n",dat);
+  return intp(dat) ? dat : 0;
+}
+
+varargs int
+GetFactor(string fname, mapping map_ldfied, object pl) {
+  mixed res;
+
+  // printf("GetFactor(%O):\n",fname);
+  if (!fname  || !(res=GetValue(FACTOR(fname),map_ldfied,pl)))
+        return 100;
+  if (res<10) res=10;
+  else if (res>1000) res=1000;
+  // printf("Factor: %O\n",res);
+  return res;
+}
+
+varargs int
+GetOffset(string oname, mapping map_ldfied, object pl) {
+  mixed res;
+
+  // printf("GetOffset(%O):\n",oname);
+  if (!oname  || !(res=GetValue(OFFSET(oname),map_ldfied,pl)))
+        return 0;
+  if (res<-10000) res=-10000;
+  else if (res>10000) res=10000;
+  // printf("Offset: %O\n",res);
+  return res;
+}
+
+varargs int
+GetFValue(string vname, mapping map_ldfied, object pl) {
+  return (GetValue(vname,map_ldfied,pl)*GetFactor(vname,map_ldfied,pl))/100;
+}
+
+varargs int
+GetValueO(string vname, mapping map_ldfied, object pl) {
+  return GetValue(vname,map_ldfied,pl)+GetOffset(vname,map_ldfied,pl);
+}
+
+varargs int
+GetFValueO(string vname, mapping map_ldfied, object pl) {
+  return ((GetValue(vname,map_ldfied,pl)*GetFactor(vname,map_ldfied,pl))/100
+                  + GetOffset(vname,map_ldfied,pl));
+}
+
+varargs int
+GetRandFValueO(string vname, mapping map_ldfied, object pl) {
+  return (random(GetValue(vname,map_ldfied,pl)*GetFactor(vname,map_ldfied,pl))/100
+          + GetOffset(vname,map_ldfied,pl));
+}
+
+mapping AddSkillMappings(mapping oldmap, mapping newmap) {
+  mapping res,t1,t2;
+  mixed *inx,ind;
+  int i;
+
+  if (!mappingp(oldmap)) return newmap;
+  if (!mappingp(newmap)) return oldmap;
+  inx=({SI_SKILLRESTR_LEARN,SI_SKILLRESTR_USE,SM_RACE});
+  res=oldmap+newmap;
+  for (i=sizeof(inx);i--;) {
+        ind=inx[i];
+        t1=oldmap[ind];
+        t2=newmap[ind];
+        if (t1) {
+          if (t2)
+                res[ind]=t1+t2;
+          else
+                res[ind]=t1;
+        } else {
+          if (t2)
+                res[ind]=t2;
+        }
+  }
+  return res;
+}
+
diff --git a/std/room.c b/std/room.c
new file mode 100644
index 0000000..29ab503
--- /dev/null
+++ b/std/room.c
@@ -0,0 +1,239 @@
+// MorgenGrauen MUDlib
+//
+// room.c -- room base object
+//
+// $Id: room.c 9475 2016-02-19 21:16:17Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/thing/properties";
+inherit "/std/thing/language";
+inherit "/std/hook_provider";
+inherit "/std/room/light";
+inherit "/std/container/inventory";
+inherit "/std/room/moving";
+inherit "/std/room/restrictions";
+inherit "/std/room/description";
+inherit "/std/room/exits";
+inherit "/std/room/commands";
+inherit "/std/room/items";
+inherit "/std/room/doors";
+
+#include <thing/properties.h>
+#include <config.h>
+#include <properties.h>
+#include <rooms.h>
+#include <language.h>
+#include <wizlevels.h>
+#include <moving.h>
+#include <defines.h>
+#include <doorroom.h>
+#include <functionlist.h>
+
+void reset()
+{
+  items::reset();
+  exits::reset();
+  doors::reset();
+}
+
+static int
+_query_noget()
+{
+  return 1;
+}
+
+//#define DB(x) if (find_player("jof")) tell_object(find_player("jof"),x); else write(x)
+//#undef DB
+//#define DB(x)
+void maybe_replace_program()
+{
+  string *list, first;
+  object first_ob;
+
+// Debugkram auskommentiert, Zesstra, 04.11.06
+//  DB("MAYBE_REPLACE_PROGRAM\n");
+//  DB(sprintf("FILENAME: %O\n",object_name(this_object())));
+//  DB(sprintf("FIRST_OB: %O\n",inherit_list(this_object())));
+//  first_ob=find_object(first=(list=inherit_list(this_object()))[1]);
+//  DB(sprintf("%O ?= %O\n",sizeof(list),1+sizeof(inherit_list(first_ob))));
+//  DB(sprintf("%O ?= sizeof(%O)\n",1,list=functionlist(this_object(),RETURN_FUNCTION_NAME|NAME_INHERITED)));
+  if (object_name(this_object())=="/std/room" ||
+      !(first_ob=find_object(first=(list=inherit_list(this_object()))[1])) ||
+      (sizeof(list)!=1+sizeof(inherit_list(first_ob))) ||
+      (1!=sizeof(list=functionlist(this_object(),
+           RETURN_FUNCTION_NAME|NAME_INHERITED))) ||
+      list[0]!="create")
+    return;
+//  DB("REPLACING\n");
+  replace_program(first);
+}
+
+protected void create()
+{
+  maybe_replace_program();
+  /* Set effective userid to userid */
+  /* so that we may clone other things */
+  seteuid(getuid(this_object()));
+  properties::create();
+  restrictions::create();
+  commands::create();
+  light::create();
+  description::create();
+  exits::create();
+  items::create();
+  doors::create();
+
+  SetProp(P_NAME,0);
+  SetProp(P_NAME_ADJ,({}));
+  Set(P_SHORT,0);
+  Set(P_LONG,0);
+  Set(P_TRANSPARENT,0);
+  Set(P_ADJECTIVES,({}));
+  Set(P_IDS,({}));
+  Set(P_WEIGHT,PROTECTED,F_MODE);
+  Set(P_TOTAL_WEIGHT,PROTECTED,F_MODE);
+  Set(" clean counter ",2);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+private int
+check_clean_count()
+{
+  int cc;
+
+  cc=Query(" clean counter ");
+  if (--cc<=0)
+    return 1;
+  Set(" clean counter ",cc);
+  return 0;
+}
+
+int
+clean_up(int arg)
+{
+  mixed itema;
+
+  if(arg>1) return 1; // better not ;)
+
+  if (Query(" never clean ")) return 0;
+
+  // if there are any item we have produced ourselfes check them
+  if(pointerp(itema = QueryProp(P_ITEMS)))
+  {
+    mixed names;
+    int i;
+    i = sizeof(names = all_inventory(this_object()));
+    while(i--)
+    {
+      if (query_once_interactive(names[i]))
+      {
+	Set(" clean counter ",2);
+	return 1;
+      }
+      if(objectp(names[i])) names[i] = explode(object_name(names[i]),"#")[0];
+    }
+
+    for(i = sizeof(itema)-1; i >= 0; i--)
+    {
+      // Semantik:
+      // 1. Wenn RITEM_OBJECT ein array ist, kann evtl ge'clean'ed werden.
+      // 2. Wenn es ein REFRESH_NONE Item ist und es entweder nicht mehr 
+      //    existiert oder nicht in diesem Raum ist, kein clean_up (es wuerde
+      //    beim neuladen des Raumes ja wieder erzeugt;
+      //    falls es aber hier ist, wird es mitvernichtet, dann ists ok)
+      // 3. Wenn es ein REFRESH_DESTRUCT ist und noch existiert, aber nicht
+      //    hier ist, KEIN clean_up.
+      if (!pointerp(itema[i][RITEM_OBJECT]) 
+	  && ((itema[i][RITEM_REFRESH] == REFRESH_NONE 
+	      && (!itema[i][RITEM_OBJECT] 
+		|| environment(itema[i][RITEM_OBJECT])!=this_object())) 
+	    || (itema[i][RITEM_REFRESH] == REFRESH_DESTRUCT 
+	      && itema[i][RITEM_OBJECT] 
+	      && environment(itema[i][RITEM_OBJECT]) != this_object())))
+	  return 1;
+      names -= (pointerp(itema[i][RITEM_FILE]) ?
+	  itema[i][RITEM_FILE] : ({ itema[i][RITEM_FILE] }));
+    }
+    // if there are objects left in the room do not clean up but try again later
+    if(sizeof(names) && !check_clean_count()) return 1;
+  }
+  else
+    // is there any object lying around?
+    if(first_inventory(this_object()) && !check_clean_count()) return 2;
+
+  // do clean_up
+  //log_file("clean_up_log",sprintf(
+  //	"%s:%s: %O\n",ctime(time())[11..18],__HOST_NAME__,this_object()));
+  
+  remove();
+  // wenn der Raum sich im remove() nicht zerstoert, hat er dafuer vermutlich
+  // nen Grund. Evtl. klappts ja naechstes Mal.
+
+  return(1);
+}
+
+/* Instead of printing the exits with the long description, we implement */
+/* the command "exits" to show them. */
+int
+show_exits() {
+  mixed ex;
+  if( this_player()->CannotSee() ) return 1;
+  if ((ex=QueryProp(P_HIDE_EXITS)) && intp(ex)) return 1;
+  if (ex = GetExits(this_player())) write(ex);
+  return 1;
+}
+
+int
+toggle_exits(string str)
+{
+  int ex;
+
+  /* Nur das aktuelle Environment des Spielers ist zustaendig fuer die
+     Auflistung der Ausgaenge. Anderenfalls wird das Kommando von Raeumen
+     im Raum (z.B. Transportern) abgefangen und es werden dessen Ausgaenge
+     aufgelistet.
+     Sprich: keine Auflistung von Aussen. */
+  if (environment(this_player()) != this_object()) return 0;
+  if (!str) return show_exits();
+  if (str!="auto") return 0;
+  ex = this_player()->QueryProp(P_SHOW_EXITS);
+  this_player()->SetProp(P_SHOW_EXITS, !ex);
+  if (ex) write("Ausgaenge werden nicht mehr automatisch angezeigt.\n");
+  else write("Ausgaenge werden automatisch angezeigt.\n");
+  return 1;
+}
+
+void
+init()
+{
+  Set(" clean counter ",2);
+
+  exits::init();
+  commands::init();
+  description::init();
+  doors::init();
+
+  add_action("toggle_exits", "exits");
+  add_action("toggle_exits", "ausgang");
+  add_action("toggle_exits", "ausgaenge");
+  add_action("toggle_exits", "aus");
+}
+
+int _query_para(){
+  int re;
+  if(re=Query(P_PARA))return re;
+  if(sizeof(regexp(({object_name(this_object())}),".*\\^0$")))
+      return -1;
+  return to_int(
+    regreplace(object_name(this_object()),".*\\^\([1-9][0-9]*\)$","\\1",1));
+}
+
+//dies ist ein Raum, war gewuenscht von mehreren Leuten.
+status IsRoom() {return(1);}
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));
+}
diff --git a/std/secure_thing.c b/std/secure_thing.c
new file mode 100644
index 0000000..7219529
--- /dev/null
+++ b/std/secure_thing.c
@@ -0,0 +1,34 @@
+/*
+ * thing.c fuer Objekte, die sicherheitsrelevant sind.
+ * Sollte von Dingen aus /secure oder Magiertools auf jeden
+ * Fall statt /std/thing inherited werden.
+ *
+ * Der Unterschied zu /std/thing besteht darin, potentiell unsichere
+ * Auswertungen wie process_string() (@@fun@@) zu verhindern - ansonsten
+ * koennte jemand Magiern Code unterschieben.
+ *
+ */
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma no_shadow
+#pragma range_check
+#pragma pedantic
+
+inherit "/std/thing";
+#include <properties.h>
+
+// int secure_level() // ist nun in simul_efun
+
+varargs string long(int mode)
+{
+  return funcall(QueryProp(P_LONG));
+}
+
+string short()
+{
+  string sh;
+  if( sh=QueryProp(P_SHORT) )
+    return funcall(sh)+".\n";
+}
+
diff --git a/std/shells/baum.c b/std/shells/baum.c
new file mode 100644
index 0000000..522f3fe
--- /dev/null
+++ b/std/shells/baum.c
@@ -0,0 +1,113 @@
+// Autor: Rumata@gmx.de
+// ... sozusagen meine private rasse ...
+//
+// MorgenGrauen MUDlib
+//
+// shells/magier.c -- magier shell
+//
+// $Id: baum.c 8675 2014-02-18 20:39:54Z Zesstra $
+
+#pragma strong_types,save_types
+
+inherit "/std/shells/magier";
+
+//#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <language.h>
+#include <moving.h>
+#include <attributes.h>
+#include <combat.h>
+#include <defines.h>
+#include <ansi.h>
+#include <udp.h>
+#include <new_skills.h>
+
+static int _wurzel;
+
+void create()
+{
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);    
+      return;
+  }
+
+  ::create();
+	_wurzel = -1;
+}
+
+static int wurzel_an( string arg ) {
+	string num;
+	int count;
+
+	if( this_object() != this_interactive() ) return 0;
+	notify_fail( "WAS willst Du schlagen?\n" );
+	if( !arg || sscanf( arg, "wurzel%s", num ) == 0 ) return 0;
+
+	if( sscanf( num, " %d", count ) == 0 ) {
+		notify_fail( "schage wurzel <zahl>\n" );
+		return 0;
+	}
+
+	_wurzel = count;
+	say( Name(WER) + " schlaegt hier Wurzeln.\n" );
+	write( "Du schlaegst nun Wurzeln.\n" );
+	return 1;
+}
+
+static int wurzel_aus( string arg ) {
+	
+	if( this_object() != this_interactive() ) return 0;
+	notify_fail( "WAS willst du loesen?\n" );
+	if( !arg ) return 0;
+	if( member( ({"wurzel","wurzeln"}),	arg ) == -1 ) return 0;
+	
+	_wurzel = -1;
+	say( Name(WER) + " loest " + QueryPronoun(FEMALE,WEN,PLURAL)
+			 + " Wurzeln aus dem Boden.\n" );
+	write( "Du loest Deine Wurzeln aus dem Boden.\n" );
+	return 1;
+}
+
+int wurzel_info( string arg ) {
+	if( this_object() != this_interactive() ) return 0;
+	switch( _wurzel ) {
+	case -1:
+		write( "Deine Wurzeln sind lose.\n" );
+		break;
+	case 0:
+		write( "Deine Wurzeln sitzen fest.\n" );
+		break;
+	case 1:
+		write( "Noch eine Bewegung und Du sitzt fest.\n" );
+		break;
+	default:
+		printf( "Noch %d Bewegungen, und Du sitzt fest.\n", _wurzel );
+	}
+	return 1;
+}
+
+static mixed _query_localcmds() {
+	return ::_query_localcmds() + ({
+		({ "schlag"    , "wurzel_an"  ,1,ARCH_LVL }),
+		({ "loes"      , "wurzel_aus" ,1,ARCH_LVL }),
+		({ "wurzelinfo", "wurzel_info",0,ARCH_LVL })
+	});
+}
+
+varargs int move( mixed dest, int method, string dir,
+									string textout, string textin )
+{
+	if( _wurzel == 0 ) {
+		return ME_TOO_HEAVY;
+	}
+	if( _wurzel > 0 ) { _wurzel--; }
+	return ::move( dest, method, dir, textout, textin );
+}
+
+static int new_quit() {
+	_wurzel = -1;
+	return ::new_quit();
+}
diff --git a/std/shells/darkelf.c b/std/shells/darkelf.c
new file mode 100644
index 0000000..45e37fb
--- /dev/null
+++ b/std/shells/darkelf.c
@@ -0,0 +1,270 @@
+// MorgenGrauen MUDlib
+//
+// shells/darkelf.c -- Darkelf Shell
+//
+// $Id: darkelf.c 8675 2014-02-18 20:39:54Z Zesstra $
+
+#pragma strong_types,save_types
+
+inherit "/std/player/base";
+
+#include <properties.h>
+#include <attributes.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+#include <language.h>
+#include <defines.h>
+#include <combat.h>
+#include <defuel.h>
+#include <errord.h>
+
+
+protected void create()
+{
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+    set_next_reset(-1);
+    return;
+  }
+  base::create();
+  SetDefaultHome("/gilden/dunkelelfen");
+  SetDefaultPrayRoom("/d/unterwelt/cadra/room/town/templemain");
+  SetProp(P_AVERAGE_SIZE,175);
+  SetProp(P_AVERAGE_WEIGHT,70000);
+  SetProp(P_ALIGN, -500);
+  SetProp(P_SKILL_ATTRIBUTE_OFFSETS,([SA_ENEMY_SAVE:110]));
+  SetProp(P_ATTRIBUTES_OFFSETS,([A_INT:4,A_DEX:2]));
+  SetProp(P_MATERIAL_KNOWLEDGE,([MATGROUP_UNHOLY: 100,
+                                 MATGROUP_MAGIC:   70,
+                                 MATGROUP_DEAD:    50,
+                                 MATGROUP_METAL:   30]) );
+  SetProp(P_RESISTANCE_STRENGTHS,
+            ([ DT_HOLY :    0.25,
+               DT_UNHOLY : -0.15,
+               DT_TERROR : -0.05 ]));
+  SetProp(P_MAX_FOOD,80);
+  SetProp(P_MAX_DRINK,150);
+  SetProp(P_MAX_ALCOHOL,70);
+
+  SetProp(P_SP_DELAY, HEAL_DELAY-2); // dafuer regeneriert man im freien
+  SetProp(P_HP_DELAY, HEAL_DELAY-1); // wirklich _nichts_
+  SetProp(P_FOOD_DELAY,FOOD_DELAY+1);
+  SetProp(P_DRINK_DELAY,DRINK_DELAY-2);
+  SetProp(P_ALCOHOL_DELAY,ALCOHOL_DELAY+1);
+
+  SetProp(P_MAGIC_RESISTANCE_OFFSET,
+        ([ MT_ILLUSION    : 200,
+           MT_ANGRIFF     : 500,
+	   MT_BEHERRSCHUNG: 500,
+	   MT_PSYCHO      : 800 ]));
+
+  /* Groesse wird nur einmal gesetzt */
+  if(!QueryProp(P_SIZE)) {
+    SetProp(P_SIZE,150+random(50));
+    Set(P_SIZE,SAVE,F_MODE_AS);
+  }
+
+  /* Dito Gewicht */
+  if(!QueryProp(P_WEIGHT) || (QueryProp(P_WEIGHT) == 75000)){
+    SetProp(P_WEIGHT,60000+random(20001));
+    if(QueryProp(P_GENDER)==FEMALE)
+      SetProp(P_WEIGHT,QueryProp(P_WEIGHT)-5000);
+    Set(P_WEIGHT,SAVE,F_MODE_AS);
+  }
+  SetProp(P_DEFUEL_LIMIT_FOOD,40);
+  SetProp(P_DEFUEL_LIMIT_DRINK,30);
+  SetProp(P_DEFUEL_TIME_FOOD,400);
+  SetProp(P_DEFUEL_TIME_DRINK,250);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,0.45);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,0.4);
+}
+
+static void FinalSetup()
+{
+   if (!QuerySkill(SK_NIGHTVISION))
+      ModifySkill(SK_NIGHTVISION, 5000, 0, "ANY");
+}
+
+string _query_race()
+// nicht static, da sie manchmal auch so aufgerufen wird...
+{
+  return "Dunkelelf";
+}
+
+string _query_real_race()
+{
+  return "Dunkelelf";
+}
+
+static string _query_racedescr()
+{
+	  return
+"\
+Das Volk der Dunkelelfen lebt in einer grossen Hoehlenstadt gut versteckt\n\
+hinter einem Wasserfall. Ueber kaum ein anderes Volk gibt es soviele\n\
+Vorurteile wie ueber die Dunkelelfen, und so werden sie von allen misstrauisch\n\
+beaeugt oder sogar bekaempft. In diesem Kampf, insbesondere gegen die Elfen,\n\
+sind sie voellig auf sich allein gestellt, und so hat sich eine mehr oder\n\
+minder autarke Gesellschaft entwickelt. Die Dunkelelfen haben eine eigene\n\
+Kultur und eine eigene Goettin, der sie huldigen. Wie auch die Elfen\n\
+verfuegen sie ueber ausserordenlich grosse magische Faehigkeiten, auch wenn\n\
+sie sich mehr auf die schwarze Seite der Magie spezialisiert haben.\n";
+}
+
+// int QueryAllowSelect() { return 0; }
+// Aktiviert am 28.07.03, Ark.
+int QueryAllowSelect() { return 1; }
+
+string *_query_racestring()
+{
+  if (QueryProp(P_GENDER)==2)
+    return ({"Dunkelelfe","Dunkelelfe","Dunkelelfe","Dunkelelfe"});
+  return ({"Dunkelelf","Dunkelelfen","Dunkelelf","Dunkelelf"});
+}
+
+static string _query_default_guild()
+{
+  return "dunkelelfen";
+}
+
+static int sun_in_room(object room)
+{
+  if (!room) return 0;
+  closure qp=symbol_function("QueryProp", room);
+  int lt=funcall(qp, P_LIGHT_TYPE);
+  // (lt & LT_SUN) ist hier zunaechst _testweise_ drin. Die Rasse wurde
+  // anders genehmigt. Sollte das im MG ueberhand nehmen und jeder Keller
+  // nun sonnendurchflutet sein, dann wird das wieder ausgebaut!
+  // 27.06.04 Padreic
+  return ( (funcall(qp, P_INT_LIGHT)>0) &&
+          ((lt & LT_SUN) || ((lt==LT_MISC) && !funcall(qp, P_INDOORS))));
+}
+
+protected void heart_beat()
+{
+  ::heart_beat();
+  if (sun_in_room(environment()) &&
+      QueryProp("Dunkelelfen:Outdoor")<random(100) && !QueryProp(P_GHOST)) {
+     int hp;
+     hp=QueryProp(P_HP)-1;
+     SetProp(P_HP, hp);
+     if (hp<0) {
+       tell_object(ME,
+         "Das war wohl zuviel fuer Dich. Das naechste mal solltest Du Dich "
+        +"wohl besser\ngegen die Sonne schuetzen.\n");
+       SetProp(P_KILL_NAME,"Zuviel Sonne");
+       do_damage(999,ME);
+       SetProp(P_KILL_NAME,0);
+     }
+     else tell_object(ME, /* die Info musste irgendwie in eine Zeile */
+           "Die Sonne scheint gnadenlos auf Dein Haupt und schwaecht Dich.\n");
+  }
+}
+
+static int _query_no_regeneration()
+{
+  if (sun_in_room(environment()))
+     return NO_REG;
+  return Query(P_NO_REGENERATION);
+}
+
+int StdSkill_Nightvision(object me, string sname, mixed sinfo)
+{
+  int last, light;
+
+  if (!sinfo || !environment()) return 0;
+  if (intp(sinfo)) sinfo=([SI_SKILLABILITY:sinfo]);
+  if (!mappingp(sinfo)) return 0;
+  light=(QueryProp(P_PLAYER_LIGHT)<=0 ? -1 : 1);
+  if (last=sinfo[SI_USR]) { // letztes Lichtlevel != 0
+     if (light==last) {
+       if (sinfo[SI_LASTLIGHT]<=time())
+          return sinfo[SI_SKILLABILITY]+1;
+       return -1;
+     }
+     else {
+       last=( MAX_ABILITY - sinfo[SI_SKILLABILITY] );
+       last=(last/1000) + (last%1000 > random(1001) ? 1 : 0);
+       if (light<0) last/=2; // an Dunkelkeit schneller gewoehnen...
+       if(!this_interactive() || this_interactive()==this_object())
+       {
+	       ModifySkill(sname, ([SI_USR: light, SI_LASTLIGHT: time()+last]),
+        	           0, sinfo[SI_GUILD]);
+       }
+       if (last<=0)
+          return sinfo[SI_SKILLABILITY]+1;
+       return -1;
+     }
+  }
+  else { // Startwert...
+    if(!this_interactive() || this_interactive()==this_object())
+    {
+    	ModifySkill(sname, ([SI_USR: light, SI_LASTLIGHT: 0]), 0, sinfo[SI_GUILD]);
+    }
+    return sinfo[SI_SKILLABILITY]+1;
+  }
+  return 0;
+}
+
+varargs int CannotSee(int silent)
+{
+  string is_blind;
+
+  if ( is_blind = QueryProp( P_BLIND ) ) {
+     if (!silent) {
+       if (stringp(is_blind)) write(is_blind);
+       else write("Du bist blind!\n");
+     }
+     return 1;
+  }
+  if (environment() && (!IS_LEARNER(ME) || !Query(P_WANTS_TO_LEARN))) {
+     if (QueryProp(P_GHOST)) {
+        if (StdSkill_Nightvision(ME, SK_NIGHTVISION, QuerySkill(SK_NIGHTVISION))>0)
+            return 0;
+     }
+     else if (UseSkill(SK_NIGHTVISION)>0) return 0;
+     if (!silent) {
+        if (QueryProp(P_PLAYER_LIGHT)<=0)
+          write("Du kannst nichts sehen, da sich Deine Augen noch nicht an die Dunkelheit\n"
+               +"gewoehnt haben!\n");
+        else write("Du bist von dem hellen Licht total geblendet und Du musst Dich erst langsam\n"
+                  +"daran gewoehnen.\n");
+     }
+     return 1;
+  }
+  return 0;
+}
+
+
+/*
+ * 2003-11-05, Zook:
+ *
+ *   Temporaere Funktion, die den Delfen es ermoeglicht, einen
+ *   moeglicherweise falschen Raum (Indoor/Outdoor) zu melden.
+ *
+ */
+
+static int _indoorbug(string key)
+{
+  if (!stringp(key))
+    key= "";
+
+  ERRORD->LogReportedError(
+      ([ F_PROG: "unbekannt",
+         F_LINE: 0,
+         F_MSG: "Sonnenfehler: " + key,
+         F_OBJ: environment(this_object())
+      ])
+      );
+
+  write("Du hast einen fehlerhaften Innen-/Aussen-/Sonnenlichtraum gemeldet.\n");
+
+  return 1;
+}
+
+static mixed _query_localcmds()
+{
+  return ({ ({"sonnenfehler", "_indoorbug", 0, 0 }) })
+            + base::_query_localcmds();
+}
+
diff --git a/std/shells/dwarf.c b/std/shells/dwarf.c
new file mode 100644
index 0000000..7a696dc
--- /dev/null
+++ b/std/shells/dwarf.c
@@ -0,0 +1,156 @@
+// MorgenGrauen MUDlib
+//
+// shells/dwarf.c -- Dwarven Shell
+//
+// $Id: dwarf.c,v 3.14 2004/12/13 12:54:31 Zook Exp $
+
+#pragma strong_types,save_types
+
+inherit "std/player/base";
+
+#include <properties.h>
+#include <attributes.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+#include <language.h>
+#include <combat.h>
+#include <defuel.h>
+
+
+void create(){
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);    
+      return;
+  }
+
+  mixed res;
+
+  base::create();
+  SetDefaultHome("/d/gebirge/room/zkapelle");
+  SetDefaultPrayRoom("/d/gebirge/room/zkapelle");
+  SetProp(P_ATTRIBUTES_OFFSETS,([A_STR:2,A_DEX:1,A_CON:3]));
+  SetProp(P_SKILL_ATTRIBUTE_OFFSETS,([SA_DURATION:110]));
+  SetProp(P_AVERAGE_SIZE,120);
+  SetProp(P_AVERAGE_WEIGHT,75000);
+  SetProp(P_MATERIAL_KNOWLEDGE,([MATGROUP_STONE:30,
+                                 MATGROUP_METAL:30, 
+                                 MATGROUP_PRECIOUS_METAL: 40, 
+                                 MAT_GOLD:100]));
+  SetProp(P_RESISTANCE_STRENGTHS,
+	  ([ DT_FIRE : -0.2,
+	   DT_WATER : 0.4 ]));
+  SetProp(P_MAX_ALCOHOL,200);
+  SetProp(P_MAX_FOOD,160);
+
+  SetProp(P_SP_DELAY,HEAL_DELAY+1);
+  SetProp(P_POISON_DELAY,POISON_DELAY+1);
+  SetProp(P_FOOD_DELAY,FOOD_DELAY-1);
+  SetProp(P_ALCOHOL_DELAY,ALCOHOL_DELAY-1);
+
+  SetProp(P_MAGIC_RESISTANCE_OFFSET,
+          ([ MT_ANGRIFF : 200,
+	   MT_ILLUSION : -500,
+           MT_BEHERRSCHUNG : 1000,
+	   MT_VERWANDLUNG : 500 ]));
+
+  if(!IS_SEER(this_object())){
+    SetProp(P_MSGIN,"stapft herein");
+    SetProp(P_MSGOUT,"stapft");
+  }
+
+  if(!(res=QueryProp(P_HANDS)) || !pointerp(res) || (sizeof(res)<3))
+    res=({" mit blossen Haenden",35,({DT_BLUDGEON}) });
+  res[1]=35;
+  SetProp(P_HANDS,res);
+  SetProp(P_BODY,10);
+
+  /* Groesse wird nur einmal gesetzt */
+  if(!QueryProp(P_SIZE)){
+    SetProp(P_SIZE,110+random(21));
+    Set(P_SIZE,SAVE,F_MODE_AS);
+  }
+
+  /* Dito Gewicht */
+  if(!QueryProp(P_WEIGHT) || (QueryProp(P_WEIGHT) == 75000)){
+    SetProp(P_WEIGHT,65000+random(20001));
+    if(QueryProp(P_GENDER)==FEMALE)
+      SetProp(P_WEIGHT,QueryProp(P_WEIGHT)-5000);
+    Set(P_WEIGHT,SAVE,F_MODE_AS);
+  }
+
+  SetProp(P_DEFUEL_LIMIT_FOOD,70);
+  SetProp(P_DEFUEL_LIMIT_DRINK,50);
+  SetProp(P_DEFUEL_TIME_FOOD,535);
+  SetProp(P_DEFUEL_TIME_DRINK,500);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,0.8);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,0.6);
+
+}
+
+string _query_race()
+{
+  return "Zwerg";
+}
+
+string _query_real_race()
+{
+  return "Zwerg";
+}
+
+string _query_racedescr()
+{
+  return
+  "Zwerge sind kleine aber kraeftige Gebirgsbewohner, nicht sehr gespraechig,\n"
+    +"leicht erzuernt, aber eine schlagkraeftige Unterstuetzung fuer ihre Freunde."
+    +"\n"
+    +"Ihr Mut und ihre Standfestigkeit ist weit und breit beruehmt, auch ihr\n"
+    +"Geschick im Umgang mit Zwergenwaffen verleiht ihnen zusaetzliche Kraft.\n"
+    +"Leider sind Zwerge nicht allzu schlau, sie verlassen sich lieber auf\n"
+    +"ihre Kraft als auf ihr Gehirn.\n";
+}
+
+int QueryAllowSelect() { return 1; }
+
+string *_query_racestring()
+{
+  if (QueryProp(P_GENDER)==2)
+    return ({"Zwergin","Zwergin","Zwergin","Zwergin"});
+  return ({"Zwerg","Zwerges","Zwerg","Zwerg"});
+}
+int _query_hp_delay(){
+  int re;
+  re = Query(P_HP_DELAY);
+  if (environment() && environment()->QueryProp(P_INDOORS))
+    re--;
+  else
+    re++;
+  return re;
+}
+int _query_sp_delay(){
+  int re;
+  re = Query(P_SP_DELAY);
+  if (environment() && environment()->QueryProp(P_INDOORS))
+    re--;
+  else
+    re++;
+  return re;
+}
+
+string _query_default_guild(){
+  return "abenteurer";
+}
+
+mixed RaceDefault(string arg)
+{
+  if (!arg)
+    return 0;
+  switch(arg)
+  {
+    case P_HANDS :
+      return ({" mit blossen Haenden",35,({DT_BLUDGEON}) });
+    case P_BODY :
+      return 10;
+  }
+  return 0;
+}
diff --git a/std/shells/elf.c b/std/shells/elf.c
new file mode 100644
index 0000000..dec4118
--- /dev/null
+++ b/std/shells/elf.c
@@ -0,0 +1,154 @@
+// MorgenGrauen MUDlib
+//
+// shells/elf.c -- Elven Shell
+//
+// $Id: elf.c 8928 2014-09-08 16:18:41Z Zesstra $
+
+#pragma strong_types,save_types
+
+inherit "std/player/base";
+
+#include <properties.h>
+#include <attributes.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+#include <language.h>
+#include <combat.h>
+#include <defuel.h>
+
+
+
+void create()
+{
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);    
+      return;
+  }
+
+  base::create();
+  SetDefaultHome("/d/wald/room/es_mitte");
+  SetDefaultPrayRoom("/d/wald/room/es_mitte");
+  SetProp(P_AVERAGE_SIZE,195);
+  SetProp(P_AVERAGE_WEIGHT,70000);
+  SetProp(P_SKILL_ATTRIBUTE_OFFSETS,([SA_ENEMY_SAVE:110]));
+  SetProp(P_ATTRIBUTES_OFFSETS,([A_INT:3,A_DEX:2,A_CON:1]));
+  SetProp(P_MATERIAL_KNOWLEDGE,([MATGROUP_WOOD:30, 
+                                 MATGROUP_HERBAL:30, 
+                                 MATGROUP_LIVING:20]));
+
+  SetProp(P_MAX_FOOD,80);
+  SetProp(P_MAX_DRINK,150);
+  SetProp(P_MAX_ALCOHOL,70);
+
+  SetProp(P_SP_DELAY,HEAL_DELAY-1);
+  SetProp(P_FOOD_DELAY,FOOD_DELAY+1);
+  SetProp(P_DRINK_DELAY,DRINK_DELAY-2);
+  SetProp(P_ALCOHOL_DELAY,ALCOHOL_DELAY+1);
+
+  SetProp(P_MAGIC_RESISTANCE_OFFSET,
+          ([ MT_ILLUSION : 800,
+           MT_ANGRIFF : 200,
+	   MT_VERWANDLUNG : 400,
+	   MT_PSYCHO : 500 ]));
+
+  // Elfen kriegen die Ebene der Wipfellaeufer per default. (Zu diesem
+  // Zeitpunkt stehen in P_CHANNELS nur die default channel drin, wird dann
+  // ueber das Einlesen des Savefiles ggf. ueberschrieben.)
+  SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({"wipfellaeufer"}));
+
+  if(!IS_SEER(this_object())){
+    SetProp(P_MSGIN,"wandelt herein");
+    SetProp(P_MSGOUT,"wandelt");
+  }
+
+  /* Groesse wird nur einmal gesetzt */
+  if(!QueryProp(P_SIZE)){
+    SetProp(P_SIZE,185+random(21));
+    Set(P_SIZE,SAVE,F_MODE_AS);
+  }
+
+  /* Dito Gewicht */
+  if(!QueryProp(P_WEIGHT) || (QueryProp(P_WEIGHT) == 75000)){
+    SetProp(P_WEIGHT,60000+random(20001));
+    if(QueryProp(P_GENDER)==FEMALE)
+      SetProp(P_WEIGHT,QueryProp(P_WEIGHT)-5000);
+    Set(P_WEIGHT,SAVE,F_MODE_AS);
+  }
+  SetProp(P_DEFUEL_LIMIT_FOOD,40);
+  SetProp(P_DEFUEL_LIMIT_DRINK,20);
+  SetProp(P_DEFUEL_TIME_FOOD,400);
+  SetProp(P_DEFUEL_TIME_DRINK,200);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,0.4);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,0.35);
+
+}
+
+string _query_race()
+{
+  return "Elf";
+}
+
+string _query_real_race()
+{
+  return "Elf";
+}
+
+string _query_racedescr()
+{
+  return 
+"\
+Als Elfen bezeichnet man in der Regel jene hageren Hinterwaeldler, deren\n\
+demonstratives Naturgehabe in der Regel nur durch ihre Liebe zu kitschigen\n\
+Gedichten und ausschweifendem Geschlechtsleben in den Schatten gestellt wird.\n\
+Einen Elf kann man im allgemeinen nicht nur an aeusseren Missbildungen\n\
+(spitze Ohren, spindelduerre Gestalt, blonde Haare) erkennen, sondern auch\n\
+an seiner aufdringlichen Art, ueber jeden und alles hemmungslos ins Gruene\n\
+loszuphilosophieren.\n";
+
+}
+
+int QueryAllowSelect() { return 1; }
+
+string *_query_racestring()
+{
+  if (QueryProp(P_GENDER)==2)
+    return ({"Elfe","Elfe","Elfe","Elfe"});
+  return ({"Elf","Elfen","Elf","Elf"});
+}
+int _query_hp_delay(){
+  int re;
+  re = Query(P_HP_DELAY);
+  if (environment() && environment()->QueryProp(P_INDOORS))
+    re++;
+  else
+    re--;
+  return re;
+}
+int _query_sp_delay(){
+  int re;
+  re = Query(P_SP_DELAY);
+  if (environment() && environment()->QueryProp(P_INDOORS))
+    re++;
+  else
+    re--;
+  return re;
+}
+
+string _query_default_guild(){
+  return "wipfellaeufer";
+}
+
+static void FinalSetup()
+{
+  if(QueryProp(P_GUILD) != "chaos")
+    SetProp(P_RESISTANCE_STRENGTHS,
+            ([ DT_MAGIC : -0.2,
+               DT_HOLY : 0.1,
+               DT_UNHOLY : 0.3 ]));
+  else
+    SetProp(P_RESISTANCE_STRENGTHS,
+            ([ DT_MAGIC : -0.2,
+               DT_UNHOLY : 0.1,
+               DT_HOLY : 0.3 ]));
+}
diff --git a/std/shells/feline.c b/std/shells/feline.c
new file mode 100644
index 0000000..f590fab
--- /dev/null
+++ b/std/shells/feline.c
@@ -0,0 +1,259 @@
+// MorgenGrauen MUDlib
+//
+// shells/feline.c -- Feline Shell
+//
+// $Id: feline.c 8487 2013-05-21 19:15:52Z Zesstra $
+
+#pragma strong_types,save_types
+
+inherit "/std/player/base";
+
+#include <attributes.h>
+#include <combat.h>
+#include <health.h>
+#include <new_skills.h>
+#include <properties.h>
+#include <language.h>
+#include <wizlevels.h>
+#include <defuel.h>
+
+
+void create()
+{   
+    if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);    
+      return;
+    }
+
+    int   i,g;
+    mixed h;
+
+    base::create();
+
+// Startraum/Kapelle setzen
+    SetDefaultHome("/d/dschungel/paracelsus/room/fkapelle");
+    SetDefaultPrayRoom("/d/dschungel/paracelsus/room/fkapelle");
+
+// Besondere rassenspezifische Properties (bei denen es nichts macht, wenn
+// sie nach jedem Einloggen neu gesetzt werden):
+
+    SetProp(P_ATTRIBUTES_OFFSETS, // Summe 4 statt 6 wg. SA_SPEED
+    ([
+        A_STR :  1,
+        A_INT :  2,
+        A_DEX :  2,
+        A_CON : -1 
+    ]) );
+    SetProp(P_AVERAGE_SIZE,200);
+    SetProp(P_AVERAGE_WEIGHT,85000);
+    SetProp(P_BODY,15);
+    SetProp(P_SKILL_ATTRIBUTE_OFFSETS,([SA_SPEED:120]));
+    SetProp(P_MATERIAL_KNOWLEDGE,
+    ([
+        MATGROUP_WOOD    :  60,
+        MATGROUP_JEWEL   : 100,
+        MATGROUP_EATABLE :  30
+    ]) );
+
+    SetProp(P_MAGIC_RESISTANCE_OFFSET,
+    ([
+        MT_ANGRIFF     :  200,
+        MT_ILLUSION    : -500,
+        MT_VERWANDLUNG :  500,
+        MT_PSYCHO      : 1000 
+    ]) );
+
+    SetProp(P_RESISTANCE_STRENGTHS,
+    ([
+        DT_WATER : -0.1,
+        DT_ACID  :  0.1,
+        DT_COLD  :  0.1 
+    ]) );
+
+// Lebenspunkte werden langsamer als normal regeneriert
+    SetProp(P_HP_DELAY,HEAL_DELAY+1);
+
+// Magiepunkte werden schneller als normal regeneriert
+    SetProp(P_SP_DELAY,HEAL_DELAY-1);
+
+// Gift wirkt etwas langsamer als normal
+    SetProp(P_POISON_DELAY,POISON_DELAY+1);
+
+// Getraenke werden etwas schneller als normal abgebaut
+    SetProp(P_DRINK_DELAY,DRINK_DELAY-1);
+
+// Essen wird etwas schneller als normal abgebaut ...
+    SetProp(P_FOOD_DELAY,FOOD_DELAY-1);
+    SetProp(P_MAX_FOOD,140);
+
+// Es gibt einige Sachen, die sollen nur beim ersten Einloggen gesetzt werden.
+// Andere muessen nachtraeglich - aber nur einmal - gemacht werden, weil sich
+// etwas geaendert hat.
+    switch( QueryProp(P_SHELL_VERSION) )
+    {
+        case 0 :
+
+            g=QueryProp(P_GENDER);
+
+            if ( !(i=QueryProp(P_SIZE)) || (i<(g==FEMALE?165:170)) 
+                || (i>(g==FEMALE?225:230)) )
+            {
+                SetProp(P_SIZE, (g==FEMALE?195:200)
+                    + random(16) - random(16) + random(16) - random(16) );
+            }
+
+            if( !(i=QueryProp(P_WEIGHT)) || (i<(g==FEMALE?70000:77000)) 
+               || (i>(g==FEMALE?88000:95000)) || (i==75000) )
+            {
+                SetProp(P_WEIGHT, (g==FEMALE?70000:77000)
+                    + random(4501) + random(4501) + random(4501) + random(4501) );
+            }
+
+
+            SetProp(P_MATERIAL,([
+                MAT_MISC_LIVING : 90,
+                MAT_PELT        :  8,
+                MAT_HORN        :  2
+            ]) );
+
+            if ( !IS_SEER(this_object()) )
+            {
+                SetProp(P_MSGIN,"schleicht herein");
+                SetProp(P_MSGOUT,"schleicht");
+                SetProp(P_MMSGIN,"erscheint mit einem grellen Blitz");
+                SetProp(P_MMSGOUT,"verschwindet mit einem grellen Blitz");
+                SetProp(P_HANDS,({" mit scharfen Krallen",40, ({DT_RIP}) }));
+            }
+            else
+            {
+                if ( !pointerp(h=QueryProp(P_HANDS)) || (sizeof(h)<1) )
+                    h = ({" mit scharfen Krallen",40, ({DT_RIP}) });
+                else
+                    h = ({h[0],40, ({DT_RIP}) });
+                SetProp(P_HANDS, h);
+            }
+
+            if ( !pointerp(h=QueryProp(P_CHANNELS)) )
+                SetProp(P_CHANNELS,({"katzenkrieger"}));
+            else if ( member(h,"katzenkrieger")==-1 )
+                SetProp(P_CHANNELS, h + ({"katzenkrieger"}) );
+
+            Set(P_SIZE,SAVE,F_MODE_AS);
+            Set(P_MATERIAL,SAVE,F_MODE_AS);
+            Set(P_WEIGHT,SAVE,F_MODE_AS);
+
+        default :
+
+            SetProp(P_SHELL_VERSION,1);
+    }
+  SetProp(P_DEFUEL_LIMIT_FOOD,70);
+  SetProp(P_DEFUEL_LIMIT_DRINK,40);
+  SetProp(P_DEFUEL_TIME_FOOD,400);
+  SetProp(P_DEFUEL_TIME_DRINK,300);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,0.55);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,0.4);
+
+}
+
+// Diese Rasse kann derzeit gewaehlt werden:
+int QueryAllowSelect() { return 1; }
+
+// Rassenbezeichnung
+string _query_race()
+{
+    return "Feline";
+}
+
+string _query_real_race()
+{
+    return "Feline";
+}
+
+// Die Rassenbeschreibung, die man beim ersten Einloggen abrufen kann.
+string _query_racedescr()
+{
+    return break_string(
+        "Felinen sind aufrecht gehende Katzenwesen.\n"+
+        "Ihre Heimat ist der Dschungel. Kaum jemand duerfte sich dort "+
+        "besser zurechtfinden als sie. Bedingt durch diese Umgebung "+
+        "haben sie im Laufe der Zeit eine Vorliebe fuer elegante Hoelzer "+
+        "und funkelnde Edelsteine entwickelt. Sie sind zwar nicht so "+
+        "'raffgierig' wie Zwerge, aber dennoch sollte man besser nicht "+
+        "versuchen, einem Felinen einen Edelstein wegzunehmen. Sie "+
+        "benutzen die Edelsteine sehr gerne, um sich damit zu schmuecken. "+
+        "Felinen betreiben sogar einen regelrechten Koerperkult, "+
+        "insbesondere wenn es darum geht, das Fell oder die Krallen zu "+
+        "faerben. Edelsteine kommen da als Accessoires gerade recht.\n"+
+        "Auch im Kampf gegen einen Felinen sollte man sehr vorsichtig "+
+        "sein, da Felinen ihre geringe Ausdauer durch eine hohe "+
+        "Geschwindigkeit sowie Geschick und Intelligenz wettmachen. "+
+        "Auch die Spitzen Krallen sind da nicht zu verachten und so "+
+        "mancher Gegner musste schon als Ersatz fuer einen Kratzbaum "+
+        "herhalten.",78,0,1);
+}
+
+// Geschlechtsabhaengiges Rassenbezeichnungs-Array
+string *_query_racestring()
+{
+    if (QueryProp(P_GENDER)==2)
+        return ({"Felinin","Felinin","Felinin","Felinin"});
+    return ({"Feline","Felinen","Felinen","Felinen"});
+}
+
+// Regeneration der Lebenspunkte ist von der Umgebung abhaengig
+// Im Wald und Dschungel geht es schneller, in der Wueste und im
+// Polargebiet dagegen langsamer.
+int _query_hp_delay()
+{
+    int re;
+    string fn;
+
+    re = Query(P_HP_DELAY);
+    if (environment() && !(environment()->QueryProp(P_INDOORS)) &&
+        fn=object_name(environment()))
+    {
+        if (fn[0..12]=="/d/dschungel/" || fn[0..7]=="/d/wald/")
+            re--;
+        else if (fn[0..9]=="/d/wueste/" || fn[0..8]=="/d/polar/")
+            re++;
+    }
+    return re;
+}
+
+// Regeneration der Magiepunkte ist von der Umgebung abhaengig
+// Im Wald und Dschungel geht es schneller, in der Wueste und im
+// Polargebiet dagegen langsamer.
+int _query_sp_delay()
+{
+    int re;
+    string fn;
+
+    re = Query(P_SP_DELAY);
+    if (environment() && !(environment()->QueryProp(P_INDOORS)) &&
+        fn=object_name(environment()))
+    {
+        if (fn[0..12]=="/d/dschungel/" || fn[0..7]=="/d/wald/")
+            re--;
+        else if (fn[0..9]=="/d/wueste/" || fn[0..8]=="/d/polar/")
+            re++;
+    }
+    return re;
+}
+
+string _query_default_guild(){
+  return "katzenkrieger";
+}
+
+mixed RaceDefault(string arg)
+{
+    if (!arg)
+      return 0;
+    switch(arg)
+    {
+        case P_HANDS :
+            return ({" mit scharfen Krallen",40, ({DT_RIP}) });
+        case P_BODY :
+            return 15;
+    }
+    return base::RaceDefault(arg);
+}
diff --git a/std/shells/goblin.c b/std/shells/goblin.c
new file mode 100644
index 0000000..ed694b3
--- /dev/null
+++ b/std/shells/goblin.c
@@ -0,0 +1,213 @@
+/*
+ * Goblin-Rassenshell
+ * [/std/shells/goblin.c]
+ * (c) 2007 nibel@mg.mud.de
+ *
+ * Werte von Ark abgesegnet am 11.12.2007
+ */
+
+#pragma strong_types,save_types
+
+#include <attributes.h>
+#include <health.h>
+#include <new_skills.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <defuel.h>
+#include <moving.h>
+
+inherit "/std/player/base.c";
+
+static varargs int GoblinCmdWaaagh(string arg);
+
+public void create() {
+  mixed res;
+  base::create();
+
+  SetDefaultHome("/d/wald/kessa/waaagh/room/starthut/hut[" + 
+    getuid(this_object()) +"]");
+  SetDefaultPrayRoom("/d/wald/nibel/lichtung/room/lichtung_45");
+  
+  SetProp(P_AVERAGE_SIZE, 80);
+  SetProp(P_AVERAGE_WEIGHT, 32000);
+  SetProp(P_MATERIAL_KNOWLEDGE, ([MATGROUP_EATABLE:20, MATGROUP_DRUG:40,
+    MATGROUP_PRECIOUS_METAL:25, MATGROUP_JEWEL:25]));
+
+  SetProp(P_BODY, 15);
+  SetProp(P_ATTRIBUTES_OFFSETS, ([A_STR:0, A_INT:1, A_DEX:2, A_CON:2]));
+  SetProp(P_RESISTANCE_STRENGTHS, ([DT_FIRE:-0.15, DT_LIGHTNING:-0.15,
+    DT_SOUND:0.1, DT_HOLY:0.1, DT_AIR:0.1, DT_ACID:0.1]));
+  SetProp(P_MAGIC_RESISTANCE_OFFSET, ([MT_ANGRIFF:600, MT_ILLUSION:500,
+    MT_VERWANDLUNG:-300, MT_HELLSICHT:-750, MT_BEHERRSCHUNG:250]));
+  SetProp(P_SKILL_ATTRIBUTE_OFFSETS, ([SA_ENEMY_SAVE:103, SA_DAMAGE:107]));
+
+  SetProp(P_SP_DELAY, HEAL_DELAY + 2);
+  SetProp(P_HP_DELAY, HEAL_DELAY - 1);
+  SetProp(P_ALCOHOL_DELAY, ALCOHOL_DELAY - 1);
+
+  switch(QueryProp(P_SHELL_VERSION)) {
+    case 0:
+      if(!QueryProp(P_SIZE)) // Maennlein und Weiblein sind gleich "gross"
+        SetProp(P_SIZE, 75 + random(11));
+      if(QueryProp(P_WEIGHT) == 75000) // Dito Gewicht
+        SetProp(P_WEIGHT, (QueryProp(P_SIZE) * 390) +
+          random(QueryProp(P_SIZE) * 10));
+      SetProp(P_MATERIAL,([MAT_MISC_LIVING:100]));
+          
+      Set(P_SIZE, SAVE, F_MODE_AS);
+      Set(P_WEIGHT, SAVE, F_MODE_AS);
+      Set(P_MATERIAL, SAVE, F_MODE_AS);
+
+      if(!pointerp(res = QueryProp(P_HANDS)) || sizeof(res) < 3)
+        res = ({" mit kleinen Faeustchen", 30, ({ DT_BLUDGEON })});
+      SetProp(P_HANDS, res);
+
+      if(!IS_SEER(this_object())) {
+        SetProp(P_MSGIN, "flitzt herein");
+        SetProp(P_MSGOUT, "flitzt");
+      }
+      SetProp(P_SHELL_VERSION, 1);
+    case 1:
+      // /std/player/base setzt ja schon P_WEIGHT...
+      if(QueryProp(P_WEIGHT) == 75000) {
+        SetProp(P_WEIGHT, (QueryProp(P_SIZE) * 390) +
+          random(QueryProp(P_SIZE) * 10));
+      }
+      SetProp(P_SHELL_VERSION, 2);
+    default: break;
+  }
+  
+  SetProp(P_MAX_FOOD, 110);
+  SetProp(P_MAX_DRINK, 80);
+  SetProp(P_MAX_ALCOHOL, 125);
+  SetProp(P_DEFUEL_LIMIT_FOOD, 60);
+  SetProp(P_DEFUEL_LIMIT_DRINK, 60);
+  SetProp(P_DEFUEL_TIME_FOOD, 400);
+  SetProp(P_DEFUEL_TIME_DRINK, 360);
+  SetProp(P_DEFUEL_AMOUNT_FOOD, 0.5);
+  SetProp(P_DEFUEL_AMOUNT_DRINK, 0.4);
+}
+
+static void FinalSetup() {
+  object o;
+  if(QueryProp(P_LEVEL) > 5 || present("\nibel:waldlichtungskarte",
+    this_object())) return;
+  if(!catch(o = clone_object("/d/wald/nibel/lichtung/obj/karte")))
+    o->move(this_object(), M_NOCHECK);
+}
+
+public int QueryAllowSelect() { return 1; }
+
+public string _query_race() { return "Goblin"; }
+public string _query_real_race() { return "Goblin"; }
+
+public string _query_racedescr() {
+  return break_string("Goblins sind winzige, gruenhaeutige Wesen, sogar "
+    "noch kleiner als Hobbits. An ihren zu dick geratenen Koepfchen "
+    "befinden sich lange, selten reglose, Ohren und eine grosse, krumme "
+    "Nase. Ihre kleine Statur sollte jedoch nicht taeuschen, denn ihre "
+    "fehlende Kraft machen sie mit Geschwindigkeit, Praezision und nicht "
+    "zuletzt ihrer unbestrittenen Ruchlosigkeit alleweil wett. Obwohl "
+    "fuer sie Pluendern, lautes Herumbruellen und die gemeinsten Streiche "
+    "spielen zum Alltag gehoert, wuerde sie niemand als boesartig "
+    "bezeichnen. Denn Goblins sind vieles, aber sicherlich nicht die "
+    "intelligentesten Kreaturen. Durch ihren zaehen Willen und die dicke, "
+    "lederne Haut sind sie aussergewoehnlich widerstandsfaehig, und, "
+    "sofern funkelnde Beute winkt, fuer jedes Abenteuer zu haben.", 78);
+}
+
+public string *_query_racestring() {
+  if(QueryProp(P_GENDER) == FEMALE)
+    return ({"Goblinfrau", "Goblinfrau", "Goblinfrau", "Goblinfrau"});
+  return ({"Goblin", "Goblins", "Goblin", "Goblin"});
+}
+
+public string _query_default_guild() {return "abenteurer";}
+
+public string _query_visible_guild() {
+  switch(lower_case(QueryProp(P_GUILD))) {
+    case "abenteurer": return "abentoira";
+    case "wipfellaeufer": return "wiffelloifa";
+    case "chaos": return "kaos";
+    case "zauberer": return "zaubara";
+    case "bierschuettler": return "biaschuettla";
+    case "katzenkrieger": return "kaznkriega";
+	  case "tanjian": return "tanschan";
+	  case "klerus": return "klerikae";	  	
+	  case "dunkelelfen": return "dunklelfn";
+	  case "kaempfer": return "kaempfa";
+	  case "karate": return "karatae";
+	  case "werwoelfe": return "weawoelf";
+	  case "magus": return "magia";
+    case "urukhai": return "urugai";
+  }
+  return QueryProp(P_GUILD);
+}
+
+public mixed RaceDefault(string arg) {
+  if(!arg) return 0;
+  switch(arg) {
+    case P_HANDS:
+      return ({" mit kleinen Faeustchen", 30, ({  DT_BLUDGEON })});
+    case P_BODY:
+      return 15;
+  }
+  return base::RaceDefault(arg);
+}
+
+static mixed _query_localcmds() {
+  return ({({"waaagh", "GoblinCmdWaaagh", 0, 0})}) +
+    base::_query_localcmds();
+}
+
+// "knuddel alle" ist deutlich teurer also who cares :-)
+static varargs int GoblinCmdWaaagh(string arg) {
+  object *obs;
+  string s, w;
+  
+  if(!objectp(environment())) return 0;
+  obs = filter(all_inventory(environment()) - ({this_object()}), #'living);
+  obs = obs - filter_objects(obs, "QueryProp", P_INVIS);
+  // levelabhaengige Anzahl aaaaaaa's
+  w = "W"+ sprintf("%'a'"+ (QueryProp(P_LEVEL) / 10 + 3) +"s", "aaa") +"gh!";
+
+  foreach(object o : obs)
+  {
+    string str=(break_string(Name(WER) +" ballt die Faeustchen und "
+      "kreischt laut: "+ w +"\n"
+      + capitalize(o->QueryDu(WER)) + " zuckst erschrocken zusammen.",
+      78, 0, BS_LEAVE_MY_LFS));
+
+    int res=o->ReceiveMsg(str,MT_LISTEN,MA_EMOTE,0,this_object());
+    if (res<0)
+    {
+      obs-=({o}); // unten nicht mehr mit anzeigen.
+      if (res==MSG_SENSE_BLOCK)
+        ReceiveMsg(o->Name(WER) +" kann Dich nicht hoeren.",
+                   MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
+                   MA_EMOTE,0,this_object());
+      else
+        ReceiveMsg(o->Name(WER) +" ignoriert Dich oder diesen Befehl.",
+                   MT_NOTIFICATION|MSG_DONT_IGNORE|MSG_DONT_STORE,
+                   MA_EMOTE,0,this_object());
+    }
+  }
+  int anzahl=sizeof(obs);
+  if(!anzahl)
+  {
+    ReceiveMsg("Du ballst die Faeustchen und kreischst laut: "
+               + w, MT_NOTIFICATION|MSG_DONT_IGNORE,MA_EMOTE,0,this_object());
+  }
+  else
+  {
+    s = CountUp(map_objects(obs, "name", WER));
+    ReceiveMsg(break_string("Du ballst die Faeustchen und kreischst laut: "
+               + w +"\n"+ capitalize(s) +" zuck"
+               +(anzahl > 1 ? "en" : "t") +" erschrocken zusammen.",
+               78, 0, BS_LEAVE_MY_LFS),
+               MT_NOTIFICATION|MSG_DONT_STORE|MSG_DONT_IGNORE,
+               MA_EMOTE,0,this_object());
+  }
+  return 1;
+}
+
diff --git a/std/shells/hobbit.c b/std/shells/hobbit.c
new file mode 100644
index 0000000..b626b06
--- /dev/null
+++ b/std/shells/hobbit.c
@@ -0,0 +1,139 @@
+// MorgenGrauen MUDlib
+//
+// shells/hobbit.c -- Hobbit Shell
+//
+//   9.April 1995  V1.0 Gundur
+//
+//   15.Juni prayroom und defHome auf Hobbitdorf gesetzt von Gundur
+//
+// $Id: hobbit.c 8920 2014-09-02 20:18:38Z Zesstra $
+
+#pragma strong_types,save_types
+
+inherit "std/player/base";
+
+#include <properties.h>
+#include <attributes.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+#include <language.h>
+#include <combat.h>
+#include <moving.h>
+#include <defuel.h>
+
+
+
+void create(){
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);    
+      return;
+  }
+
+  mixed res;
+
+  base::create();
+  SetDefaultHome("/d/wald/gundur/hobbitdorf/schrein");
+  SetDefaultPrayRoom("/d/wald/gundur/hobbitdorf/schrein");
+  SetProp(P_ATTRIBUTES_OFFSETS,([A_DEX:4,A_CON:2]));
+  SetProp(P_AVERAGE_SIZE, 105);
+  SetProp(P_AVERAGE_WEIGHT, 60000);
+  SetProp(P_MATERIAL_KNOWLEDGE,([MATGROUP_EATABLE:30, 
+                                 MATGROUP_DRUG:30, 
+                                 MATGROUP_POISONOUS:10]));
+  SetProp(P_RESISTANCE_STRENGTHS,
+	  ([ DT_TERROR : -0.1,
+	   DT_MAGIC : -0.1,
+	   DT_SOUND : 0.2,
+	   DT_LIGHTNING : 0.1,
+	   DT_POISON : 0.1 ]));
+  SetProp(P_MAX_FOOD,250);
+  SetProp(P_MAX_DRINK,100);
+  SetProp(P_MAX_ALCOHOL,150);
+
+  SetProp(P_SP_DELAY,HEAL_DELAY+1);
+  SetProp(P_POISON_DELAY,POISON_DELAY-1);
+  SetProp(P_FOOD_DELAY,FOOD_DELAY-2);
+  SetProp(P_ALCOHOL_DELAY,ALCOHOL_DELAY+1);
+
+  SetProp(P_MAGIC_RESISTANCE_OFFSET,
+          ([ MT_ANGRIFF : 500,
+	   MT_HELLSICHT : -500,
+	   MT_PSYCHO : -500 ]));
+
+  if(!(res=QueryProp(P_HANDS)) || !pointerp(res) || (sizeof(res)<3))
+    res=({" mit pelzigen Haenden",25,({DT_BLUDGEON})});
+  res[1]=25;
+  SetProp(P_HANDS,res);
+  SetProp(P_BODY,15);
+
+  /* Groesse wird nur einmal gesetzt */
+  if(!QueryProp(P_SIZE)){
+    SetProp(P_SIZE,95+random(21));
+    Set(P_SIZE,SAVE,F_MODE_AS);
+  }
+
+  /* Dito Gewicht */
+  if(!QueryProp(P_WEIGHT) || (QueryProp(P_WEIGHT) == 75000)){
+    SetProp(P_WEIGHT,50000+random(20001));
+    if(QueryProp(P_GENDER)==FEMALE)
+      SetProp(P_WEIGHT,QueryProp(P_WEIGHT)-5000);
+    Set(P_WEIGHT,SAVE,F_MODE_AS);
+  }
+  SetProp(P_DEFUEL_LIMIT_FOOD,140);
+  SetProp(P_DEFUEL_LIMIT_DRINK,50);
+  SetProp(P_DEFUEL_TIME_FOOD,850);
+  SetProp(P_DEFUEL_TIME_DRINK,450);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,0.8);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,0.6);
+
+}
+
+string _query_race()
+{
+  return "Hobbit";
+}
+
+string _query_real_race()
+{
+  return "Hobbit";
+}
+
+string _query_racedescr()
+{
+  return "Hobbits sind kleine Wesen, die am ehesten den Menschen aehneln.\n"+
+    "Sie zeichnen sich trotz Ihrer Groesse durch ihren Mut und Standfestigkeit "+
+	"aus.\nObwohl sie viel lieber zuhause vorm warmen Kamin sitzen, sind sie "+
+	"immer\nfuer ein Abenteuer zu haben.\n";
+}
+
+int QueryAllowSelect() { return 1; }
+
+string *_query_racestring(){
+  if (QueryProp(P_GENDER) == FEMALE)
+    return ({"Hobbitfrau","Hobbitfrau","Hobbitfrau","Hobbitfrau"});
+  return ({"Hobbit","Hobbits","Hobbit","Hobbit"});
+}
+
+string _query_default_guild(){
+  return "abenteurer";
+}
+
+void FinalSetup() {
+  if(!present("pfeifchen",this_object()))
+    clone_object("/items/pfeifchen")->move(this_object(),M_NOCHECK);
+}
+
+mixed RaceDefault(string arg)
+{
+  if (!arg)
+    return 0;
+  switch(arg)
+  {
+    case P_HANDS :
+      return ({" mit pelzigen Haenden",25,({DT_BLUDGEON}) });
+    case P_BODY :
+      return 15;
+  }
+  return base::RaceDefault(arg);
+}
diff --git a/std/shells/human.c b/std/shells/human.c
new file mode 100644
index 0000000..c18df2d
--- /dev/null
+++ b/std/shells/human.c
@@ -0,0 +1,113 @@
+// MorgenGrauen MUDlib
+//
+// shells/human.c -- Human Shell
+//
+// $Id: human.c 9022 2015-01-10 21:50:50Z Zesstra $
+
+#pragma strong_types,save_types
+
+inherit "/std/player/base";
+
+#include <properties.h>
+#include <attributes.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+#include <language.h>
+#include <defuel.h>
+
+
+
+void create() {
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);    
+      return;
+  }
+
+  base::create();
+  SetDefaultHome("/gilden/abenteurer");
+  SetDefaultPrayRoom("/d/ebene/room/PortVain/pray_room");
+  SetProp(P_ATTRIBUTES_OFFSETS,([A_INT:1,A_STR:1,A_CON:1,A_DEX:3]));
+  SetProp(P_AVERAGE_SIZE,170);
+  SetProp(P_AVERAGE_WEIGHT,75000);
+  SetProp(P_MATERIAL_KNOWLEDGE,([MATGROUP_WOOD:20, 
+                                 MATGROUP_METAL:20, 
+                                 MATGROUP_ELEMENTAL:20, 
+                                 MATGROUP_CLOTH:20]));
+
+  SetProp(P_MAX_FOOD,120);
+  SetProp(P_MAX_DRINK,120);
+  SetProp(P_MAX_ALCOHOL,120);
+
+  SetProp(P_MAGIC_RESISTANCE_OFFSET,
+          ([ MT_ANGRIFF : 500,
+	   MT_ILLUSION : 700,
+           MT_BEHERRSCHUNG : 500,
+	   MT_HELLSICHT : 1000,
+	   MT_VERWANDLUNG : -500,
+	   MT_PSYCHO : -500 ]));
+
+  // Zukuenftig 0, nicht mehr -5. Ark, 04.01.08.
+  SetProp(P_BODY,0);
+
+  /* Groesse wird nur einmal gesetzt */
+  if(!QueryProp(P_SIZE)){
+    SetProp(P_SIZE,160+random(21));
+    Set(P_SIZE,SAVE,F_MODE_AS);
+  }
+
+  /* Dito Gewicht */
+  if(!QueryProp(P_WEIGHT) || (QueryProp(P_WEIGHT) == 75000)){
+    SetProp(P_WEIGHT,65000+random(20001));
+    if(QueryProp(P_GENDER)==FEMALE)
+      SetProp(P_WEIGHT,QueryProp(P_WEIGHT)-5000);
+    Set(P_WEIGHT,SAVE,F_MODE_AS);
+  }
+  SetProp(P_DEFUEL_LIMIT_FOOD,60);
+  SetProp(P_DEFUEL_LIMIT_DRINK,50);
+  SetProp(P_DEFUEL_TIME_FOOD,500);
+  SetProp(P_DEFUEL_TIME_DRINK,245);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,0.75);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,0.5);
+
+}
+
+string _query_race()
+{
+  return "Mensch";
+}
+
+string _query_real_race()
+{
+  return "Mensch";
+}
+
+string *_query_racestring()
+{
+  if (QueryProp(P_GENDER)==2)
+    return ({"Menschenfrau","Menschenfrau","Menschenfrau","Menschenfrau"});
+  return ({"Mensch","Menschen","Mensch","Menschen"});
+}
+
+string _query_racedescr()
+{
+  return "Die Staerke des Menschen ist seine Vielseitigkeit.\n"+
+    "Der Mensch kann zwar nichts besonders gut - dafuer aber eigentlich alles.\n";
+}
+
+int QueryAllowSelect() { return 1; }
+
+void FinalSetup()
+{
+  // Im MG gibt fuer kleine Spieler eine Karte von Port Vain. Die gibt es
+  // woanders meist nicht.
+#if MUDNAME == "MorgenGrauen"
+  if (QueryProp(P_LEVEL)<=3 && !present("portvainkarte",this_object()))
+    clone_object("/d/ebene/obj/pv")->move(this_object(),M_NOCHECK);
+#endif
+}
+
+string _query_default_guild(){
+  return "abenteurer";
+}
diff --git a/std/shells/magier.c b/std/shells/magier.c
new file mode 100644
index 0000000..dc2a150
--- /dev/null
+++ b/std/shells/magier.c
@@ -0,0 +1,326 @@
+// MorgenGrauen MUDlib
+//
+// shells/magier.c -- magier shell
+//
+// $Id: magier.c 9231 2015-05-27 21:53:32Z Zesstra $
+
+//
+// Magiershell Basisfile
+//
+// Ueberarbeitung abgeschlossen am 18.12.2002
+//
+// Dank an Zwirch@PK, Rikus@MG, Zoran@PK, Vanion@MG
+// und viele andere, die ich vergessen habe.
+//
+// Fragen und Bughinweise an Mandragon@MG oder einen
+// Erzmagier Deiner Wahl.
+//
+// Zur Shell gehoeren ausser dieser Datei:
+// admin.c:      Administrative Befehle
+// comm.c:       Kommunikationsbefehle
+// fileedit.c:   Befehle zum Veraendern von Dateien
+// fileview.c:   Befehle zum Lesen von Dateien
+// magier_ext.c: Generelle Magierbefehle
+// moving.c:     Bewegungsbefehle
+// objects.c:    Erzeugen und zerstoeren von Objekten
+// parsing.c     Auswertung von Pfadangaben und Wildcards
+// players.c:    Befehle zur Beeinflussung von Spielern
+// todo.c:       Implementation der Todoliste
+// upd.c:        Der Befehl upd
+// magier.h      Generelle Header-Datei
+//
+
+#pragma strict_types,save_types
+
+inherit "/std/player/base";
+inherit "/std/shells/magier/magier_ext";
+
+#include <wizlevels.h>
+#include <moving.h>
+#include <properties.h>
+#include <new_skills.h>
+#include <config.h>
+
+protected void create()
+{
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);
+      return;
+  }
+
+  base::create();
+
+  Set(P_RACE, SAVE, F_MODE);
+  Set(P_ZAP_MSG, SAVE, F_MODE);
+  Set(P_TRANK_FINDEN, SAVE, F_MODE);
+  Set(P_HANDS, SAVE, F_MODE);
+  Set(P_RACESTRING, SAVE, F_MODE);
+  SetDefaultHome("/gilden/abenteurer");
+  SetProp(P_ENEMY_DEATH_SEQUENCE,
+    ([17:"Der Tod schuettelt verstaendnislos den Kopf.\n\n",
+      18:"Der Tod sagt: WIESO MUSSTEST DU DICH AUCH UNBEDINGT "
+      "MIT EINEM MAGIER ANLEGEN?\n\n"]));
+  SetProp(P_ATTRIBUTES_OFFSETS,([]));
+  SetProp(P_AVERAGE_SIZE,185);
+  if(!QueryProp(P_DEFAULT_GUILD)) SetProp(P_DEFAULT_GUILD,"abenteurer");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+
+protected void heart_beat()
+{
+  mixed *en;
+
+  if (!QueryProp(P_WANTS_TO_LEARN)||((en=QueryEnemies())&&sizeof(en[0])))
+    base::heart_beat();
+  else if (!CheckTelnetKeepAlive()) {
+    // Wenn der Magier kein Telnet Keep-Alive wuenscht, kann der HB ganz
+    // abgeschaltet werden. Sonst muss er aber weiterlaufen, damit
+    // CheckTelnetKeepAlive() regelmaessig gerufen wird.
+    set_heart_beat(0);
+  }
+}
+
+
+public varargs int remove(int silent)
+{
+  string workroom;
+
+  if (IS_WIZARD(this_object()))
+    workroom = "/players/"+getuid()+"/workroom";
+  else
+    workroom = "/secure/merlin";
+  if( !environment() || object_name(environment()) != workroom )
+    catch(move(workroom, M_GO, "nach Hause"));
+  return base::remove(silent);
+}
+
+public string NotifyDestruct(object caller) {
+
+  if (previous_object() != master()
+      || object_name(this_object()) == __FILE__[..<3])
+    return 0;
+  
+  // Nicht-EMs sollen keine EMs zerstoeren koennen, woraufhin auch evtl.
+  // EM-Tools rumliegen koennten.
+  if ( IS_ARCH(this_object()) && caller != this_object() 
+      && getuid(caller) != ROOTID
+      && (process_call() || !ARCH_SECURITY) )
+    return "Das Zerstoeren von EMs ist ein Fehler. ;-)\n";
+
+  return ::NotifyDestruct(caller);
+}
+
+void reset()
+{
+  if (!interactive(this_object()))
+  {
+    quit();
+    if (this_object())
+      remove();
+    if (this_object())
+        destruct(this_object());
+    return;
+  }
+}
+
+//                ####################
+//################# Query-Funktionen ##################
+//                ####################
+
+varargs int id (string str) {
+  if (QueryProp(P_INVIS) &&
+      (!this_interactive() ||!IS_LEARNER(this_interactive())))
+    return 0;
+  return ::id(str);
+}
+
+
+static string *_query_racestring()
+{
+  if (pointerp(Query(P_RACESTRING)))
+    return Query(P_RACESTRING);
+  else
+    return
+    ({QueryProp(P_RACE),QueryProp(P_RACE),
+      QueryProp(P_RACE),QueryProp(P_RACE)});
+}
+
+
+static string _query_default_guild()
+{
+    return (Query(P_DEFAULT_GUILD)||"abenteurer");
+}
+
+
+static string _query_racedescr()
+{
+  return "Magier koennen einfach alles. Aber manche Magier koennen mehr.\n";
+}
+
+
+static string _query_race()
+{
+  if (previous_object() && previous_object()->query_login_object())
+    return 0;
+
+  return Query(P_RACE) ? Query(P_RACE) : Set(P_RACE, "Magier");
+}
+
+
+static mixed _query_localcmds()
+{
+  return
+    base::_query_localcmds()
+    +magier_ext::_query_localcmds();
+}
+
+
+static void upd_my_age()
+{
+  age=_age+absolute_hb_count()-_hbstop;
+  _age=age;
+  _hbstop=absolute_hb_count();
+  return;
+}
+
+
+static int _query_age()
+{
+  upd_my_age();
+  return age;
+}
+
+static int _set_earmuffs(int level)
+{
+  int maxl=1+query_wiz_level(this_object());
+  maxl = max(maxl,99);
+  return Set(P_EARMUFFS,min(maxl,level));
+}
+
+
+//                   ############################
+//#################### Interne Shell-Funktionen ####################
+//                   ############################
+
+int MayAddWeight(int w) { return 0;}
+int MayAddObject(object ob) { return 1; }
+
+
+static void initialize()
+{
+  magier_ext::initialize();
+  return;
+}
+
+
+static void FinalSetup()
+{
+  SetProp(P_CURRENTDIR,"/players/"+getuid());
+  initialize();
+  if (IS_LEARNER(this_player())) cat("/etc/WIZNEWS");
+  _age=age;
+  _hbstop=absolute_hb_count();
+  return;
+}
+
+
+void save_me(int i)
+{
+  upd_my_age();
+  base::save_me(i);
+  return;
+}
+
+
+varargs void Reconnect(int silent,string my_ip)
+{
+  base::Reconnect(silent,my_ip);
+  magier_ext::reconnect();
+  return;
+}
+
+
+void notify_player_change(string who, int rein, int invis)
+{
+  string *list,name;
+  mixed mlist;
+  int vis_change;
+
+  if (invis) name="("+who+")";
+    else name=who;
+
+  if(query_verb() && (query_verb()=="vis" || query_verb()=="invis"))
+    vis_change=1;
+
+  if (Query(P_INFORMME) && !vis_change)
+  {
+    if (rein) 
+      tell_object(this_object(),
+                  sprintf("%s ist gerade ins "MUDNAME" gekommen.\n",name));
+    else
+      tell_object(this_object(),
+                  sprintf("%s hat gerade das "MUDNAME" verlassen.\n",name));
+  }
+
+  if(Query(P_WAITFOR_FLAGS) & (0x01))return ;
+
+  if (pointerp(list=Query(P_WAITFOR))&&sizeof(list))
+    if (member(list,who)!=-1)
+      delayed_write(
+         ({
+           ({sprintf("%s%s   I S T   J E T Z T   %s !!!\n",
+                     (QueryProp(P_VISUALBELL) ? "" : sprintf("%c",7)),
+                     name,
+                     (vis_change?
+                       (rein?"S I C H T B A R":"U N S I C H T B A R"):
+                       (rein?"D A":"N I C H T   M E H R   D A"))),
+           0})
+         }));
+
+  if (rein && (sizeof(mlist=QueryProp(P_WAITFOR_REASON))) &&
+     (mappingp(mlist)) && (mlist[who]))
+        Show_WaitFor_Reason(who,invis);
+  return;
+}
+
+mixed modify_command(string str) {
+  if (previous_object() &&
+      (previous_object()!=this_object() || process_call()) )
+  {
+    if (IS_ARCH(this_object()))
+      tell_object(this_object(),
+        sprintf("Illegal modify_command(%s) from %O\n",
+        str, previous_object()));
+    return 0;
+  }
+  //////////////////////////////////////////////////////////////////////
+  // Magier-Escape-Kommandos werden behandelt 
+  if (str=="\\ESCAPE" && IS_LORD(this_object()))
+  {
+    __set_environment(this_object(),"/room/void");
+    environment()->init();
+    printf("You escaped.\n");
+    return "";
+  }
+  if (str[0..2]=="\\\\\\" && IS_LORD(this_object()))
+  {
+    str = _return_args(str);
+    string* input = explode(str[3..]," ");
+    string verb = input[0];
+    if (verb && verb!="")
+    {
+      string cmd = implode(input[1..]," ");
+      if (!__auswerten(cmd,verb))
+        SoulComm(cmd,verb);
+    }
+    return 1;
+  }
+  //////////////////////////////////////////////////////////////////////
+
+  return ::modify_command(str);
+}
+
diff --git a/std/shells/magier/admin.c b/std/shells/magier/admin.c
new file mode 100644
index 0000000..a41d1b7
--- /dev/null
+++ b/std/shells/magier/admin.c
@@ -0,0 +1,428 @@
+// MorgenGrauen MUDlib
+//
+// admin.c
+//
+// $Id: admin.c 8755 2014-04-26 13:13:40Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <udp.h>
+#include <wizlevels.h>
+#include <input_to.h>
+
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <player.h>
+
+inherit "/std/util/cidr";
+
+mixed _query_localcmds()
+{
+  return ({({"udpq","_udpq",0,LEARNER_LVL}),
+           ({"shutdown","shut_down_game",0,ARCH_LVL}),
+           ({"addmaster","_addmaster",0,GOD_LVL}),
+           ({"removemaster","_removemaster",0,GOD_LVL}),
+           ({"addguildmaster", "_addguildmaster", 0, GOD_LVL}),
+           ({"removeguildmaster", "_removeguildmaster", 0, GOD_LVL}),
+           ({"suender","sinners",0,WIZARD_LVL}),
+           ({"banish","banish", 0, WIZARD_LVL}),
+           ({"mbanish","mbanish", 0, WIZARD_LVL}),
+           ({"tbanish","tbanish", 0, WIZARD_LVL}),
+           ({"sbanish","sbanish", 0, WIZARD_LVL})});
+}
+
+static int _udpq(string str)
+{
+  string ret, mud, type;
+
+  if (!(str=_unparsed_args()) || str=="" || sscanf(str,"%s %s",mud,type)<2)
+  {
+    write("Syntax: udpq mud type\n");
+    return 1;
+  }
+  if (member(({"commands","email","hosts","inetd","list","mud_port","time",
+         "version"}),type)==-1)
+    write("TYPEs: commands, email, hosts, inetd, list, mud_port, time, version\n");
+  if (ret=(string)INETD->_send_udp(mud,([SENDER:getuid(), REQUEST:QUERY, DATA:type]),1))
+    write(ret);
+  else
+    write("Anfrage abgeschickt.\n");
+  return 1;
+}
+
+static int shut_down_game(string str)
+{
+  if (!IS_ARCH(this_object()) || this_object()!=this_interactive())
+    return 0;
+  _notify_fail("Du musst einen Grund dafuer angeben.\n");
+  if (!str) return 0;
+  write( "Direkter shutdown mit Grund \""+str+"\"?\n" );
+  input_to("shut_down_game_2",INPUT_PROMPT, "(ja/nein) :", str);
+  return 1;
+}
+
+static int shut_down_game_2(string arg,string str)
+{
+  if (!IS_ARCH(this_object()) || this_object()!=this_interactive())
+    return 0;
+  if( arg!="ja" ) {
+    write( "Shutdown abgebrochen.\n" );
+  } else {
+    shutdown(str);
+  }
+  return 1;
+}
+
+
+static int _addmaster(string str)
+{
+  string master, domain;
+
+  if (!GOD_SECURITY)
+  {
+    write("Das darfst Du nicht!\n");
+    return 1;
+  }
+  _notify_fail("Syntax: addmaster <user> <domain>\n");
+  if (!str) return 0;
+  if (sscanf(str,"%s %s",master,domain)!=2) return 0;
+  if (!master || !domain) return 0;
+  if (!"/secure/master"->add_domain_master(master,domain))
+    write("Hat nicht funktioniert.\n");
+  else
+    write("Ok.\n");
+  return 1;
+}
+
+static int _removemaster(string str)
+{
+  string master, domain;
+
+  if (!GOD_SECURITY)
+  {
+    write("Das darfst Du nicht!\n");
+    return 1;
+  }
+  _notify_fail("Syntax: removemaster <user> <domain>\n");
+  if (!str) return 0;
+  if (sscanf(str,"%s %s",master,domain)!=2) return 0;
+  if (!master || !domain) return 0;
+  if (!"/secure/master"->remove_domain_master(master,domain))
+    write("Hat nicht funktioniert.\n");
+  else
+    write("Ok.\n");
+  return 1;
+}
+
+static int _addguildmaster(string str)
+{
+  string master, guild;
+  
+  if (!GOD_SECURITY)
+  {
+    write ("Das darfst Du nicht!\n");
+    return 1;
+  }
+
+  _notify_fail("Synatx: addguildmaster <user> <guild>\n");
+  if (!str) return 0;
+  if (sscanf(str, "%s %s", master, guild)!=2) return 0;
+  if (!master || !guild) return 0;
+  if (!"/secure/master"->add_guild_master(master,guild))
+    write("Hat nicht funktioniert.\n");
+  else
+    write ("Ok.\n");
+  return 1;
+}
+
+static int _removeguildmaster(string str)
+{
+  string master, guild;
+  
+  if (!GOD_SECURITY)
+  {
+    write ("Das darfst Du nicht!\n");
+    return 1;
+  }
+  _notify_fail("Syntax: removeguildmaster <user> <guild>\n");
+  if (!str) return 0;
+  if (sscanf(str, "%s %s", master, guild)!=2) return 0;
+  if (!master || !guild) return 0;
+  if (!"/secure/master"->remove_guild_master(master,guild))
+    write("Hat nicht funktioniert.\n");
+  else
+    write("Ok.\n");
+  return 1;
+}
+
+static int sinners(string arg)
+{   string *parts;
+    int    i;
+
+    if ( !IS_DEPUTY(this_object()) )
+      return 0;
+
+    arg=_unparsed_args()||arg;
+
+    notify_fail(
+        "Syntax: suender ?              => Liste aller Eingetragenen\n"+
+        "        suender <name>         => Eintraege lesen\n"+
+        "        suender +<name> <text> => Eintrag hinzufuegen\n"+
+        "        suender -<name> <nr>   => Eintrag loeschen\n"+
+        "        suender !              => Alle Eintraege dumpen\n"+
+        "        suender *              => Alle Eintraege anzeigen\n");
+
+    if ( !stringp(arg) || (sizeof(arg)<1) )
+      return 0;
+
+    if ( arg=="?" )
+    {
+        write(call_other("/secure/sinmaster","ListSinners"));
+        return 1;
+    }
+    if ( arg=="!" )
+    {
+        write(call_other("/secure/sinmaster","Dump"));
+        return 1;
+    }
+    if ( arg=="*" )
+    {
+        More((string)call_other("/secure/sinmaster","Dump",1));
+        return 1;
+    }
+
+    if ( (i=sizeof(parts=explode(arg," ")))<1 )
+      return 0;
+
+    if ( parts[0][0..0]=="-" )
+    {
+      if ( i<2 )
+        return 0;
+      write(call_other("/secure/sinmaster","RemoveSin",
+            lowerstring(parts[0][1..]),
+            to_int(parts[1])));
+    }
+    else if ( parts[0][0..0]=="+" )
+    {
+      if ( i<2 )
+        return 0;
+      write(call_other("/secure/sinmaster","AddSin",
+            lowerstring(parts[0][1..]),
+            implode(parts[1..]," ")));
+    }
+    else
+    {
+      if ( i>1 )
+        return 0;
+      write(call_other("/secure/sinmaster","ListSins",
+            lowerstring(parts[0])));
+    }
+    return 1;
+}
+
+static int banish(string str)
+{
+  string grund, name;
+  int force;
+
+  if ( !LORD_SECURITY && !IS_DEPUTY(secure_euid()) )
+      return 0;
+
+  if ( !str || !stringp(str) || !sizeof(str) ) {
+      write("Syntax: banish [-f] <name> [<grund>]\n");
+      return 1;
+  }
+
+  str = _unparsed_args();
+
+  if ( explode(str, " ")[0] == "-f" ){
+      str = implode( explode(str, " ")[1..], " " );
+      force = 1;
+  }
+
+  if ( sscanf( str, "%s %s", name, grund ) != 2 )
+      name=str;
+
+  if ( !name || !sizeof(name) ){
+    write("Syntax: banish [-f] <name> [<grund>]\n");
+    return 1;
+  }
+
+  name=lower_case(name);
+  "/secure/master"->BanishName( name, grund, force );
+  return 1;
+}
+
+static int mbanish(string str)
+{
+    string grund, name, *namen, txt, *dummy;
+    mapping list;
+    int i;
+
+    if ( !IS_DEPUTY(secure_euid()) )
+        return 0;
+
+    _notify_fail( "Syntax: mbanish <name> [<grund>]\n" );
+
+    if ( !str || !stringp(str) || !sizeof(str) ){
+        if ( !mappingp(list = (mapping)"/secure/merlin"->MBanishList()) ||
+             !(i = sizeof(list)) ){
+            write( "Momentan ist kein Spieler auf der mbanish-Liste.\n" );
+            return 1;
+        }
+
+        txt = "      Name     | gebanisht von |                    Grund\n" +
+            "=============================================================" +
+            "==================\n";
+
+        namen = sort_array( m_indices(list), #'</*'*/ );
+
+        for ( ; i--; ){
+            dummy = explode( break_string( list[namen[i],0] ||
+                                                 "-- keine Begruendung --", 45 ),
+                                   "\n" ) - ({""});
+
+            txt += sprintf( "  %-11s  |  %-11s  |  %s\n",
+                            capitalize( namen[i] ),
+                            capitalize( list[namen[i],1] || "" ),
+                            capitalize( implode( dummy, "\n               |"
+                                                 "               |  " ) ) );
+        }
+
+        More(txt);
+
+        return 1;
+    }
+
+    if ( sscanf( str, "%s %s", name, grund ) !=2 )
+        name = str;
+
+    if ( !name || !sizeof(name) )
+        return 0;
+
+    name = lower_case(name);
+
+    if ( !grund || !stringp(grund) || lower_case(grund) != "loeschen" ){
+        "/secure/merlin"->MBanishInsert( name, grund, this_interactive() );
+        write( "Du setzt "+capitalize(name)+" auf die MBanish-Liste.\n" );
+    }
+    else{
+        if ( !ARCH_SECURITY ){
+            write( "Das duerfen nur Erzmagier.\n" );
+            return 1;
+        }
+        "/secure/merlin"->MBanishDelete( name );
+        write( "Du loescht "+capitalize(name)+" von der MBanish-Liste.\n" );
+    }
+
+    return 1;
+}
+
+
+static int tbanish( string str )
+{
+  string name;
+  int days;
+
+  if ( !IS_DEPUTY(secure_euid()) )
+      return 0;
+
+  _notify_fail("Syntax: tbanish <name> <tage>\n");
+
+  if ( !str || !stringp(str) || !sizeof(str) )
+    return 0;
+
+  if ( sscanf(str,"%s %d",name,days) != 2 )
+      return 0;
+
+  if ( !name || !sizeof(name) )
+    return 0;
+
+  name = lower_case(name);
+
+  if ( !"/secure/master"->TBanishName( name, days ) )
+      return 1;
+
+  if ( !days )
+      write( "Okay, keine Spielpause fuer "+capitalize(name)+" mehr.\n" );
+  else
+      write( "Du verpasst "+capitalize(name)+" eine Spielpause fuer "+
+             (days>0 ? days+" Tage" : "laaaange Zeit")+".\n" );
+  return 1;
+}
+
+static int sbanish( string str )
+{
+    string ip;
+    int days;
+    mapping sites;
+
+    // Mindestens L26 fuer diesen Befehl
+    if ( secure_level() <= DOMAINMEMBER_LVL )
+        return 0;
+
+    if ( !str || !stringp(str) || !sizeof(str) ){
+        if ( !sizeof(sites = (mapping)MASTER->SiteBanish( 0, 0 )) ){
+            write( "Es sind zur Zeit keine Adressen gesperrt!\n" );
+            return 1;
+        }
+
+        ip = "      Adresse      |  gesperrt bis                 |  gesperrt "
+            + "durch\n========================================================"
+            + "==============\n";
+
+        int *keys = sort_array( m_indices(sites), #'</*'*/ );
+
+        foreach(int key : keys) {
+            ip += sprintf( "  %:15-s  |  %:27-s  |  %-s\n",
+                           IPv4_int2addr(key),
+                           sites[key] > 0 ? dtime(sites[key]) :
+                           "St. Nimmerleinstag", 
+                           capitalize(sites[key, 1]) );
+        }
+        write( ip + "\n" );
+        return 1;
+    }
+
+    _notify_fail("Syntax: sbanish <numerische ip> <tage>\n");
+
+    if ( sscanf( this_player()->_unparsed_args(), "%s %d", ip, days ) != 2 )
+        return 0;
+
+    if ( !ip || !sizeof(ip) )
+        return 0;
+
+//    _notify_fail( "Ungueltiges Adress-Format!\n" );
+
+    if ( !days ){
+        int res=(int)MASTER->SiteBanish(ip, 0);
+        if ( res == 1 )
+             printf( "Die Adresse '%s' ist jetzt nicht mehr gesperrt.\n",
+                     ip );
+        else if ( res == 0 )
+             printf( "Die Adresse '%s' war gar nicht gesperrt!\n",
+                     ip );
+        else
+            printf( "Du darfst nur eigene Sperrungen wieder aufheben!\n" );
+    }
+    else {
+        int res;
+        if ( days != 1 && !IS_DEPUTY(secure_euid()) )
+            write( "Du darfst Adressen nur fuer einen Tag sperren!\n" );
+        else if ( (res = (int)MASTER->SiteBanish(ip, days)) == 1 )
+             printf( "Die Adresse '%s' ist jetzt fuer %s gesperrt.\n",
+                     ip, (days > 1 ? sprintf( "%d Tage", days ) :
+                      (days > 0 ? "einen Tag" : "immer")) );
+        else if ( res == -1 )
+            write( "Du darfst " + (LORD_SECURITY ? "255 IP-Adressen"
+                                   : "nur einzelne IP-Adressen") + " sperren!\n" );
+        else if ( res == -2 )
+            write( "Du hast schon genug Adressen gesperrt!\n" );
+    }
+
+    return 1;
+}
+
diff --git a/std/shells/magier/comm.c b/std/shells/magier/comm.c
new file mode 100644
index 0000000..0cbd266
--- /dev/null
+++ b/std/shells/magier/comm.c
@@ -0,0 +1,149 @@
+// MorgenGrauen MUDlib
+//
+// comm.c
+//
+// $Id: comm.c 8755 2014-04-26 13:13:40Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <wizlevels.h>
+#include <magier.h>
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <player.h>
+#include <properties.h>
+
+
+//                           ###########
+//############################ ECHOALL ################################
+//                           ###########
+
+static int _echoall(string str)
+{
+  if (!(str=_unparsed_args()))
+    return USAGE("echoall <text>\n");
+  str=break_string(str);
+  shout(str);
+  printf(str);
+  return 1;
+}
+
+//                           ##########
+//############################ ECHOTO #################################
+//                           ##########
+
+static int _echoto(string str)
+{
+  object ob;
+  string who;
+  string msg;
+
+  if (!(str=_unparsed_args())||sscanf(str, "%s %s", who, msg) != 2)
+    return USAGE("echoto <spieler> <text>\n");
+  ob = find_player(lower_case(who));
+  if (!ob)
+  {
+    printf("echoto: Es ist kein Spieler '%s' eingeloggt.\n",who);
+    return 1;
+  }
+  msg=break_string(msg,78);
+  tell_object(ob,msg);
+  printf("%s->%s",ob->Name(WEN),msg);
+  return 1;
+}
+
+//                           #########
+//############################ MECHO ##################################
+//                           #########
+
+static int _mecho(string str)  {
+  object *who;
+  int i;
+
+  if (!sizeof(str=_unparsed_args()))
+    return USAGE("mecho <text>\n");
+  who=users();
+  i=sizeof(who);
+  while(i--)if (IS_LEARNER(who[i]))
+      tell_object(who[i], break_string(str,78));
+  return 1;
+}
+
+//                            ########
+//############################# PING ##################################
+//                            ########
+
+static int _ping(string str)
+{
+  object pl;
+
+  if (!sizeof(str))
+    return USAGE("ping <spielername>\n");
+  if (!(pl=find_player(lower_case(str))))
+    return
+      _notify_fail(sprintf("ping: Spieler %s nicht gefunden.\n",
+                           capitalize(str))),0;
+  tell_object(pl,sprintf("%s pingt Dich an.\a\n",capitalize(getuid())));
+  return printf("PINGGGGGGG! DAS sollte %s auf Dich aufmerksam "
+                "gemacht haben.\n",capitalize(getuid(pl))),1;
+}
+
+//                           ##########
+//############################ OROPAX #################################
+//                           ##########
+
+int _oropax(string cmdline)
+{
+  int level,old,new;
+
+  cmdline=_unparsed_args();
+  old=QueryProp(P_EARMUFFS);
+  if (!sizeof(cmdline)||cmdline=="0")
+  {
+    if (old)
+    {
+      printf("Du nimmst das Oropax aus Deinen Ohren und hoerst "
+             "wieder allen anderen zu.\n");
+      SetProp(P_EARMUFFS, 0);
+    }
+    else
+      printf("Du hast doch gar kein Oropax in den Ohren.\n");
+    return 1;
+  }
+  if (sscanf(cmdline, "%d", level) == 0 || level < 1)
+    return USAGE("oropax [<magierlevel>]\n");
+  if ((new=SetProp(P_EARMUFFS, level))==level)
+  {
+    if (new==old)
+      return printf("Du hattest Deine Oropaxmenge schon auf Magierlevel "
+                    "%d angepasst.\n",level),1;
+    if (new>old)
+      return printf("Du stopfst Dir soviel Oropax in die Ohren, dass "
+                    "Du nur noch %s ab\nLevel %d hoerst.\n",
+                    level>=LEARNER_LVL?"Magier":"Seher",level),1;
+    return printf("Du nimmst soviel Oropax aus den Ohren, dass Du ab "
+                  "sofort wieder %s ab\nLevel %d verstehst.\n",
+                  level>=LEARNER_LVL?"Magier":"Seher",level),1;
+  }
+  return printf("Du stopfst und stopfst, bis Dir das Oropax wieder zur "
+                "Nase herauskommt.\nLeider musst Du damit Magier ab Level "
+                "%d weiterhin hoeren.\n",new),1;
+}
+
+//                         ###################
+//########################## INITIALISIERUNG #############################
+//                         ###################
+
+static mixed _query_localcmds()
+{
+  return
+    ({({"oropax","_oropax",0,LEARNER_LVL}),
+      ({"echoto","_echoto",0,LEARNER_LVL}),
+      ({"echoall","_echoall",0,LEARNER_LVL}),
+      ({"mecho","_mecho",0,LEARNER_LVL}),
+      ({"ping","_ping",0,LEARNER_LVL})});
+}
diff --git a/std/shells/magier/fileedit.c b/std/shells/magier/fileedit.c
new file mode 100644
index 0000000..f4df0a5
--- /dev/null
+++ b/std/shells/magier/fileedit.c
@@ -0,0 +1,718 @@
+// MorgenGrauen MUDlib
+//
+//fileedit.c
+//
+// $Id: fileedit.c 9142 2015-02-04 22:17:29Z Zesstra $
+#pragma strict_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <wizlevels.h>
+#include <input_to.h>
+
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <player.h>
+#include <files.h>
+
+//                        ###################
+//######################### INITIALISIERUNG #############################
+//                        ###################
+
+mixed _query_localcmds()
+{
+  return ({ ({"cp","_cp",0,WIZARD_LVL}),
+            ({"mv","_cp",0,WIZARD_LVL}),
+            ({"rm","_rm",0,WIZARD_LVL}),
+            ({"rmdir","_rmdir",0,WIZARD_LVL}),
+            ({"mkdir","_mkdir",0,WIZARD_LVL}),
+            ({"ed","_ed",0,WIZARD_LVL})});
+}
+
+//                               ######
+//################################ ED ###################################
+//                               ######
+
+
+//
+// _ed_file: Mehrere Files hintereinander editieren
+//
+
+private nosave string *_ed_cache;
+
+private void _ed_file()
+{
+  if (!sizeof(_ed_cache)) return;
+  printf("ed: Naechste Datei: %s\n",_ed_cache[0]);
+  ed(_ed_cache[0],"_ed_file");
+  _ed_cache=_ed_cache[1..];
+  return;
+}
+
+#if __VERSION__ < "3.2.9"
+private mixed _ed_size_filter(mixed *arg)
+{
+  if (arg[FILESIZE]>=-1) return arg[FULLNAME];
+  printf("%s ist ein Verzeichnis.\n",arg[FULLNAME]);
+  return 0;
+}
+#endif
+
+//
+// _more: Dateien anzeigen
+// cmdline: Kommandozeile
+//
+
+static int _ed(string cmdline)
+{
+  mixed *args,*args2;
+  int flags,i,arg_size;
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,"",1);
+  if (flags==-1||!(arg_size=sizeof(args)))
+    return USAGE("ed <datei> [<datei2>..]");
+  while(arg_size--)
+  {
+    if (sizeof(args2=file_list(args[arg_size..arg_size],MODE_ED,0,"/")))
+      args[arg_size..arg_size]=args2;
+    else
+      args[arg_size]=({ "" , -1, 0 , (string)
+              call_other(master(),"_get_path",args[arg_size],
+                  getuid())});
+  }
+#if __VERSION__ < "3.2.9"
+  args=map(args,#'_ed_size_filter)-({0});
+#else
+  args=map(args,(:
+          if ($1[FILESIZE]>=-1) return $1[FULLNAME];
+          printf("%s ist ein Verzeichnis.\n",$1[FULLNAME]);
+          return 0; :))-({0});
+#endif
+  if (flags==-1||!sizeof(args)) return USAGE("ed <datei> [<datei2>..]");
+  _ed_cache=args;
+  _ed_file();
+  return 1;
+}
+
+
+//                             ###########
+//############################## CP & MV ################################
+//                             ###########
+
+static void _cp_ask_copy(mixed *filedata,int dummy, int flags);
+static void _cp_ask_overwrite(mixed *filedata, int mode, int flags);
+static void _mv_ask_overwrite(mixed *filedata, int mode, int flags);
+
+static mixed cp_file(mixed filedata,int move,int flags, mixed *do_delete)
+{
+  string source,dest;
+  source=(string)filedata[FULLNAME];
+  dest=(string)filedata[DESTNAME];
+  if (source==dest) return ERROR(SAME_FILE,source,RET_FAIL);
+  if (!MAY_READ(source)) return ERROR(NO_READ,source,RET_JUMP);
+  if (!MAY_WRITE(dest)) return ERROR(NO_WRITE,dest,RET_JUMP);
+  if (filedata[FILESIZE]==-1) return ERROR(DOESNT_EXIST,source,RET_JUMP);
+  if (filedata[FILESIZE]==-2) // Quelle ist Verzeichnis
+  {
+    switch(file_size(dest))
+    {
+      case -1:
+        if (move)
+        {
+          if (rename(source,dest)) return ERROR(NO_CREATE_DIR,dest,RET_JUMP);
+          if (flags&CP_V) printf(FILE_MOVED,source);
+          return RET_JUMP;
+        }
+        if (!mkdir(dest)) return ERROR(NO_CREATE_DIR,dest,RET_JUMP);
+        if (flags&CP_V) printf(DIR_CREATED,dest);
+      case -2:
+        if (!move) return RET_OK;
+        if (filedata[SUBDIRSIZE]>0) return RET_DELETE;
+        if (!rmdir(source)) return ERROR(NO_DELETE,source,RET_FAIL);
+        if (flags&MV_V) printf("mv: %s: Quellverzeichnis wurde "
+                               "geloescht.\n",source);
+        return RET_OK;    
+      default:  return ERROR(NO_DIRS,dest,RET_JUMP);
+    }
+  }
+  switch(file_size(dest))
+  {
+    case -2: return ERROR(DEST_IS_DIR,dest,RET_FAIL);
+    default:
+      if (flags&CP_F)
+      {
+        if (!rm(dest)) return ERROR(DEST_NO_DELETE,dest,RET_FAIL);
+        if (flags&CP_V) printf(FILE_DELETED,dest);
+      }
+      else
+      {
+        if (move) return #'_mv_ask_overwrite;
+        else return #'_cp_ask_overwrite;
+      }
+    case -1:
+      if (move)
+      {
+        if (rename(source,dest)) return ERROR(NO_MOVE,source,RET_FAIL);
+        if (flags&CP_V) printf(FILE_MOVED,source);
+        return RET_OK;
+      }
+      if (copy_file(source,dest)) return ERROR(NO_COPY,source,RET_FAIL);
+      if (flags&CP_V) printf(FILE_COPIED,source);
+      return RET_OK;
+  }
+  return 0; // not-reached
+}
+
+static void _cp_ask_overwrite2(string input, mixed *filedata,
+                               int interactive,int flags,int move)
+{
+  if (!sizeof(input)) input=" ";
+  input=lower_case(input);
+  switch(input[0])
+  {
+    case 'q':
+      printf("%s abgebrochen!\n",move?"Bewegen":"Kopieren");
+      return;
+    case 'a':
+      flags|=CP_F;
+      if (!(flags&CP_I))
+      {
+        asynchron(filedata,#'cp_file,move,flags,0);
+        return;
+      }
+    case 'y':
+    case 'j':
+      if (!rm(filedata[0][DESTNAME]))
+        printf(DEST_NO_DELETE "Uebergehe Datei...\n",filedata[0][DESTNAME]);
+      else
+      {
+        if (flags&CP_V) printf(FILE_DELETED,filedata[0][DESTNAME]);
+        if (move)
+        {
+          if (rename(filedata[0][FULLNAME],filedata[0][DESTNAME]))
+            printf(NO_MOVE "Uebergehe Datei...\n",filedata[0][FULLNAME]);
+        }
+        else
+        {
+          if (copy_file(filedata[0][FULLNAME],filedata[0][DESTNAME]))
+            printf(NO_COPY "Uebergehe Datei...\n",filedata[0][FULLNAME]);
+        }
+      }
+    case 'n':
+      if (flags&CP_I)
+        _cp_ask_copy(filedata[1+filedata[0][SUBDIRSIZE]..],move,flags);
+      else
+        asynchron(filedata[1+filedata[0][SUBDIRSIZE]..],
+                                     #'cp_file,move,flags,0);
+      return;
+    default:
+      printf("Kommando nicht verstanden.\n");
+      _cp_ask_overwrite(filedata,interactive,flags);
+      return;
+  }
+
+}
+
+static void _cp_ask_overwrite(mixed *filedata, int interactive, int flags)
+{
+  printf("Die Datei '%s' existiert schon.\n",
+         filedata[0][DESTNAME]);
+  input_to("_cp_ask_overwrite2",INPUT_PROMPT,"Ueberschreiben? (j,n,a,q): ",
+      filedata,interactive,flags,0);
+  return;
+}
+
+static void _mv_ask_overwrite(mixed *filedata, int interactive, int flags)
+{
+  printf("Die Datei '%s' existiert schon.",
+         filedata[0][DESTNAME]);
+  input_to("_cp_ask_overwrite2",INPUT_PROMPT,"Ueberschreiben? (j,n,a,q): ",
+      filedata,interactive,flags,1);
+  return;
+}
+
+static void _cp_ask_copy2(string input,mixed *filedata,int mode,
+                           int flags,int move)
+{
+  if (!sizeof(input)) input=" ";
+  input=lower_case(input);
+  switch(input[0])
+  {
+    case 'y':
+    case 'j':
+      if (mode==1)
+      {
+        if (!(flags&CP_F))
+        {
+          if (move) _mv_ask_overwrite(filedata,1,flags);
+          else      _cp_ask_overwrite(filedata,1,flags);
+          return;
+        }
+        if (!rm(filedata[0][DESTNAME]))
+        {
+          printf(DEST_NO_DELETE "Uebergehe Datei...\n",
+                 filedata[0][DESTNAME]);
+          _cp_ask_copy(filedata[1..],move,flags);
+          return;
+        }
+        if (flags&CP_V) printf(FILE_DELETED,filedata[0][DESTNAME]);
+      }
+      if (mode<2)
+      {
+        if (move) rename(filedata[0][FULLNAME],filedata[0][DESTNAME]);
+        else copy_file(filedata[0][FULLNAME],filedata[0][DESTNAME]);
+        _cp_ask_copy(filedata[1..],move,flags);
+        return;
+      }
+      if (mode==2)
+      {
+        if (move)
+        {
+          if (rename(filedata[0][FULLNAME],filedata[0][DESTNAME]))
+            printf(NO_MOVE "Uebergehe Verzeichnis...\n",
+                   filedata[0][FULLNAME]);
+          _cp_ask_copy(filedata[1+filedata[0][SUBDIRSIZE]..],move,flags);
+          return;
+        }
+        if (mkdir(filedata[0][DESTNAME]))
+        {
+          _cp_ask_copy(filedata[1..],0,flags);
+          return;
+        }
+        printf(NO_CREATE_DIR "Uebergehe Verzeichnis...\n",
+               filedata[0][DESTNAME]);
+      }
+    case 'n':
+      _cp_ask_copy(filedata[(1+filedata[0][SUBDIRSIZE])..],0,flags);
+      return;
+    case 'q':
+      printf("Kopieren abgebrochen!\n");
+      return;
+    case 'a':
+      flags&=~CP_I;
+      asynchron(filedata,#'cp_file,move,flags,0);
+      return;
+    default:
+      printf("Kommando nicht verstanden.\n");
+      _cp_ask_copy(filedata,0,flags);
+      return;
+  }
+}
+
+static void _cp_ask_copy(mixed *filedata,int move, int flags)
+{
+  mixed data;
+  string dest,source;
+  int delete_subs,jump;
+
+  if(!sizeof(filedata))
+  {
+    printf("%s: abgeschlossen.\n",move?"mv":"cp");
+    return; 
+  }
+  dest=filedata[0][DESTNAME];
+  source=filedata[0][FULLNAME];
+  switch(0)   // break wirkt damit wie ein goto end_of_switch
+  {
+    default:
+    case 0: // Sinnlos, aber er compiliert sonst nicht :(
+      jump=filedata[0][SUBDIRSIZE];
+      if (source==dest)
+      {
+        printf(SAME_FILE,source);
+        break;
+      }
+      if (!MAY_READ(source))
+      {
+        printf(NO_READ,source);
+        break;
+      }
+      if (!MAY_WRITE(dest))
+      {
+        printf(NO_WRITE,dest);
+        jump=0;
+        break;
+      }
+      if (filedata[0][FILESIZE]==-1)
+      {
+        printf(DOESNT_EXIST,source);
+        break;
+      }
+      if (filedata[0][FILESIZE]==-2) // Quelle ist Verzeichnis
+      {
+        if (file_size(dest)>-1)
+        {
+          printf(NO_DIRS,dest);
+          break;
+        }
+        if (file_size(dest)==-2)
+        {
+          jump=0;
+          break;
+        }
+        printf("Verzeichnis '%s' %s?\n",source,
+               move?"bewegen":"kopieren");
+        input_to("_cp_ask_copy2",INPUT_PROMPT, "(j,n,a,q) ",
+            filedata,2,flags,move);
+        return;
+      }
+      if (file_size(dest)==-2)
+      {
+        printf(DEST_IS_DIR,dest);
+        break;
+      }
+      printf("'%s' %s?\n",source,move?"bewegen":"kopieren");
+      input_to("_cp_ask_copy2",INPUT_PROMPT, "(j,n,a,q) ",
+          filedata,(file_size(dest)!=-1),flags,move);
+      return;
+  }
+  _cp_ask_copy(filedata[1+jump..],move,flags);
+  return;
+}
+
+
+static int _cp(string cmdline)
+{
+  mixed *args;
+  int flags;
+  string mask;
+  string dest,*dest2;
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,CP_OPTS,0);
+  if (flags==-1||!sizeof(args))
+    return USAGE(query_verb()+" [-" CP_OPTS
+                 "] <datei/verz> [<datei2/verz2> ... ] <ziel> [<maske>]");
+  if (flags&CP_M)
+  {
+    mask=args[<1];
+    args=args[0..<2];
+  }
+  if (!dest=to_filename(args[<1]))
+     return USAGE(query_verb()+" [-" CP_OPTS
+          "] <datei/verz> [<datei2/verz2> ... ] <ziel> [<maske>]");
+  if (file_size(dest)==-1)
+  {
+    dest2=explode(dest,"/");
+    if (file_size(implode(dest2[0..<2],"/"))==-2)
+    {
+      if (dest2[<1]=="*")
+        dest=implode(dest2[0..<2],"/");
+      else
+        if (member(dest2[<1],'*')>-1||
+            member(dest2[<1],'?')>-1)
+         return notify_fail(
+          sprintf("%s: Keine * und ? im Zielnamen erlaubt.\n",query_verb())),0;
+    }
+    else
+      return notify_fail(
+        sprintf("%s: Der angegebene Zielpfad existiert nicht.\n",
+                query_verb())),0;
+  }
+  args=args[0..<2];
+  if (file_size(dest)!=-2&&sizeof(args)>1)
+    return notify_fail(
+        sprintf("%s: Bei mehreren Quellen muss das Ziel ein Verzeichnis "
+                "sein.\n",query_verb())),0;
+  if (!sizeof(args=map(args,#'to_filename)-({0})))
+    return USAGE(query_verb()+" [-" CP_OPTS
+          "] <datei/verz> [<datei2/verz2> ... ] <ziel> [<maske>]");
+  // DEBUG("DEST: " + dest + " : FLAGS: " + flags);
+  args=file_list(args,MODE_CP,(flags&CP_R),dest+"/",mask);
+  if (!sizeof(args))
+    return notify_fail(sprintf("%s: Keine passenden Dateien gefunden.\n",
+                               query_verb())),0;
+
+  if (sizeof(args)>1&&(args[0][FILESIZE]>=0)&&file_size(dest)!=-2)
+      return notify_fail(
+        sprintf("%s: Bei mehreren Quellen muss das Ziel ein Verzeichnis "
+                "sein.\n",query_verb())),0;
+  if (sizeof(args)==1&&file_size(dest)!=-2)
+    args[0][DESTNAME]=dest;
+  if (!(flags&CP_I))
+  {
+    asynchron(args,#'cp_file,(query_verb()=="mv"),flags,0);
+    return 1;
+  }
+  if (query_verb()=="cp")
+    _cp_ask_copy(args,0,flags);
+  else
+    _cp_ask_copy(args,1,flags);
+  return 1;
+}
+
+//                              #########
+//############################### RMDIR #################################
+//                              #########
+
+
+//
+// _rmdir: Verzeichnis loeschen
+// cmdline: Kommandozeilenargumente
+//
+
+
+#if __VERSION__ < "3.2.9"
+
+private int _dir_filter(mixed arg)
+{
+  return (arg[FILESIZE]==-2);
+}
+
+#endif
+
+static int _rmdir(string cmdline)
+{
+  string dest,tmp;
+  int flags;
+  mixed *args;
+  
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,RMDIR_OPTS,1);
+  if (flags==-1||!sizeof(args))
+    return USAGE("rmdir [-" RMDIR_OPTS "] <Verzeichnis>");
+  if (sizeof(args)>1)
+    return
+    notify_fail("Mit 'rmdir' kann nur jeweils EIN Verzeichnis geloescht "
+         "werden.\nDer Befehl 'rm' bietet erweiterte Moeglichkeiten.\n"),0;
+  dest=args[0];
+  if (dest!="/")
+  {
+    args=file_list(({dest}),MODE_RMDIR,0,"/");
+#if __VERSION__ < "3.2.9"
+    args=filter(args,#'_dir_filter);
+#else
+    args=filter(args,(: ($1[FILESIZE]==-2) :));
+#endif
+    if (!sizeof(args))
+      return notify_fail(
+        sprintf("rmdir: %s: Kein solches Verzeichnis gefunden.\n",dest)),0;
+    if (sizeof(args)>1)
+      return notify_fail(
+                sprintf("rmdir: %s: Maske ist nicht eindeutig.\n",dest)),0;
+    dest=args[0][FULLNAME];
+    if (!MAY_WRITE(dest)) return ERROR(NO_WRITE,dest,1);
+    if (!rmdir(dest))
+    {
+      if (sizeof((get_dir(dest+"/*")||({}))-({".",".."})))
+        printf("rmdir: %s: Verzeichnis ist nicht leer.\n",dest);
+    }
+    else
+    {
+      if (flags&&RMDIR_V) printf(FILE_DELETED,dest);
+    }
+    return 1;
+  }
+  return ERROR(NO_DELETE,dest,1);
+}
+
+//                              #########
+//############################### MKDIR #################################
+//                              #########
+
+
+//
+// _mkdir: Verzeichnis erstellen
+// cmdline: Kommandozeilenargumente
+//
+
+static int _mkdir(string cmdline)
+{
+  string dest,tmp;
+  int flags,i;
+  string *args;
+  
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,MKDIR_OPTS,1);
+  if (flags==-1) return 0;
+  if (!sizeof(args))
+    return USAGE("mkdir [-" MKDIR_OPTS "] <Verzeichnis>");
+  if (sizeof(args)>1)
+    return notify_fail("Mit 'mkdir' kann nur jeweils EIN Verzeichnis "
+                       "erstellt werden.\n"),0;
+  dest=args[0];
+  
+  if ((i=file_size(implode((args=explode(dest,"/"))[0..<2],"/")))==FSIZE_DIR)
+  {
+    if (!mkdir(dest)) return ERROR(NO_CREATE_DIR,dest,1);
+    if (flags&MKDIR_V) printf(DIR_CREATED,dest,1);
+    printf("mkdir: abgeschlossen.\n");
+    return 1;
+  }
+  
+  if (i==FSIZE_NOFILE)
+  {
+    if (flags&MKDIR_R)
+    {
+      if (mkdirp(dest) != 1)
+        return ERROR(NO_CREATE_DIR,dest,1);
+      if (flags&MKDIR_V)
+        printf(DIR_CREATED,implode(args[0..i],"/"));
+      printf("mkdir: abgeschlossen.\n");
+      return 1;
+    }
+    return ERROR(DOESNT_EXIST,implode(args[0..<2],"/"),1);
+  }
+  return ERROR(ALREADY_EXISTS,dest,1);
+}
+
+//                               ######
+//################################ RM ###################################
+//                               ######
+
+private void _rm_ask_delete(mixed *filedata, int flags);
+
+static void _rm_ask_delete2(string input,mixed *filedata,int flags)
+{
+  int i;
+  if (!sizeof(input)) input=" ";
+  input=lower_case(input);
+  switch(input[0])
+  {
+    case 'q':
+      printf("Loeschen abgebrochen!\n");
+      return;
+    case 'y':
+    case 'j':
+      if (filedata[0][FILESIZE]==-2)
+      {
+        if (i=filedata[0][SUBDIRSIZE]) // Dir-Eintrag nach hinten schieben
+        {
+          mixed temp;
+          int j;
+          temp=filedata[0];
+          temp[SUBDIRSIZE]=0;
+          for(j=0;j<i;j++) filedata[j]=filedata[j+1];
+          filedata[j]=temp;
+          _rm_ask_delete(filedata,flags);
+          return;
+        }
+        if (!rmdir(filedata[0][FULLNAME]))
+          printf(NO_DELETE,filedata[0][FULLNAME]);
+        else if (flags&RM_V) printf(FILE_DELETED,filedata[0][FULLNAME]);
+      }
+      else // Datei existiert
+      {
+        if (!rm(filedata[0][FULLNAME]))
+          printf(DEST_NO_DELETE "Uebergehe Datei...\n",
+                 filedata[0][FULLNAME]);
+        else if (flags&RM_V) printf(FILE_DELETED,filedata[0][FULLNAME]);
+        
+      }
+    case 'n':
+      _rm_ask_delete(filedata[1+filedata[0][SUBDIRSIZE]..],flags);
+      return;
+    default:
+      printf("Kommando nicht verstanden.\n");
+      _rm_ask_delete(filedata,flags);
+      return;
+  }
+  return;
+}
+
+private void _rm_ask_delete(mixed *filedata, int flags)
+{
+  int i;
+  mixed temp;
+  if (!sizeof(filedata))
+  {
+    printf("rm: abgeschlossen.\n");
+    return;
+  }
+  switch(filedata[0][FILESIZE])
+  {
+    case -1:
+      if (flags&RM_V) printf(DOESNT_EXIST,filedata[0][FULLNAME]);
+      _rm_ask_delete(filedata[1..],flags);
+      return;
+    case -2:
+      if (i=filedata[0][SUBDIRSIZE])
+        printf("Ins Verzeichnis '%s' hinabsteigen?\n",
+          filedata[0][FULLNAME]);
+      else
+        printf("Verzeichnis '%s' loeschen?\n",
+               filedata[0][FULLNAME]);
+      input_to("_rm_ask_delete2",INPUT_PROMPT, "(j,n,q) ",
+          filedata,flags);
+      return;
+    default:
+      printf("'%s' loeschen? (j,n,q)\n",
+         filedata[0][FULLNAME]);
+      input_to("_rm_ask_delete2",INPUT_PROMPT, "(j,n,q) ",
+          filedata,flags);
+      return;
+  }
+}
+
+
+private void rm_file(mixed filedata, mixed notused, int flags)
+{
+  string dest;
+  dest=filedata[FULLNAME];
+  if (!MAY_WRITE(dest))
+  {
+    printf(NO_WRITE,dest);
+    return;
+  }
+  switch(filedata[FILESIZE])
+  {
+    case -1:
+      if (flags&RM_V) printf(DOESNT_EXIST,dest);
+      return;
+    case -2:
+      if (!rmdir(dest)) printf(DEST_NO_DELETE,dest);
+      else
+      {
+        if (flags&RM_V) printf(FILE_DELETED,dest);
+      }
+      return;
+    default:
+      if (!rm(dest)) printf(DEST_NO_DELETE,dest);
+      else
+      {
+        if (flags&RM_V) printf(FILE_DELETED,dest);
+      }
+      return;
+  }
+}
+
+static int _rm(string cmdline)
+{
+  mixed *args,*args2;
+  int flags,i;
+  string mask;
+  
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,RM_OPTS,0);
+  if (flags==-1||!sizeof(args))
+    return USAGE("rm [-" RM_OPTS
+                 "] <datei/verz> [<datei2/verz2> ... ] [<maske>]");
+  if (flags&RM_M)
+  {
+    mask=args[<1];
+    args=args[0..<2];
+  }
+  args=map(args,#'to_filename)-({0});
+  args=file_list(args,MODE_RM,(flags&RM_R),"/",mask);
+  if (!(i=sizeof(args)))
+    return printf("Keine passende Datei gefunden.\n"),1;
+  if (!(flags&RM_I))
+  {
+    if (i>1) // Umdrehen
+    {
+      mixed temp;
+      i>>=1;
+      while(i)
+      {
+        temp=args[<(i--)];
+        args[<(i+1)]=args[i];
+        args[i]=temp;
+      }
+    }
+    asynchron(args,#'rm_file,args,flags,0);
+    return 1;
+  }
+  _rm_ask_delete(args,flags);
+  return 1;
+}
diff --git a/std/shells/magier/fileview.c b/std/shells/magier/fileview.c
new file mode 100644
index 0000000..731de5a
--- /dev/null
+++ b/std/shells/magier/fileview.c
@@ -0,0 +1,728 @@
+// MorgenGrauen MUDlib
+//
+// fileview.c
+//
+// $Id: fileview.c 9142 2015-02-04 22:17:29Z Zesstra $
+#pragma strict_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <ansi.h>
+#include <player/base.h>
+#include <wizlevels.h>
+#include <shells.h>
+#include <daemon/mand.h>
+#include <udp.h>
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <thing/properties.h>
+#include <player.h>
+
+private nosave mapping colorstrings;
+private nosave mapping oldman_result;
+
+//                        ###################
+//######################### INITIALISIERUNG #############################
+//                        ###################
+
+mixed _query_localcmds()
+{
+  return ({({"ls","_ls",0,LEARNER_LVL}),
+           ({"more","_more",0,LEARNER_LVL}),
+           ({"cat","_cat",0,LEARNER_LVL}),
+           ({"head","_cat",0,LEARNER_LVL}),
+           ({"tail","_cat",0,LEARNER_LVL}),
+           ({"man","_man",0,LEARNER_LVL}),
+           ({"rman","_rman",0,LEARNER_LVL}),
+           ({"showprops","_showprops",0,WIZARD_LVL}),
+           ({"grep","_grep",0,LEARNER_LVL})});
+}
+
+
+//                               ######
+//################################ LS ###################################
+//                               ######
+
+//
+// _ls: Der ls-Befehl: Verzeichnisse und Dateien anzeigen
+// cmdline: Kommandozeile
+//
+
+//
+// Funktionen zum Sortieren und fuer filter_ldfied/map_ldfied
+//
+
+
+private string _get_color(string color)
+{
+  return COLORS[lower_case(color)];
+}
+
+
+private void SetColorstrings()
+{
+  string tmp,term;
+  mapping vars;
+  vars=QueryProp(P_VARIABLES);
+  colorstrings=m_allocate(0,2);
+  switch(term=QueryProp(P_TTY))
+  {
+    case "vt100":
+    case "ansi":
+      if(stringp(vars["LS_DIR"]))
+        tmp=implode(map(explode(vars["LS_DIR"],"+"),#'_get_color),
+                    "");
+      else
+        tmp = (term == "ansi") ? ANSI_BLUE+ANSI_BOLD: ANSI_BOLD;
+      colorstrings[DIR] = tmp+"%s"+(term == "vt100"?"/":"")+ANSI_NORMAL;
+      if(term == "vt100") colorstrings[DIR, 1] = 1;
+      if(stringp(vars["LS_OBJ"]))
+        tmp=implode(map(explode(vars["LS_OBJ"],"+"),#'_get_color),
+                    "");
+      else
+        tmp = (term == "ansi") ? ANSI_RED : ANSI_INVERS;
+      colorstrings[OBJ] = tmp+"%s"+ANSI_NORMAL;
+      if(stringp(vars["LS_VC"]))
+        tmp=implode(map(explode(vars["LS_VC"],"+"),#'_get_color),
+                    "");
+      else
+        tmp = (term == "ansi") ? ANSI_PURPLE : ANSI_INVERS;
+      colorstrings[VC] = tmp+"%s"+ANSI_NORMAL;
+      break;
+    case "dumb":
+      colorstrings[DIR] = "%s/"; colorstrings[DIR, 1] = 1;
+      colorstrings[OBJ] = "%s*"; colorstrings[OBJ, 1] = 1;
+      colorstrings[VC]  = "%s*"; colorstrings[VC , 1] = 1;
+      break;
+    default:
+      colorstrings[DIR] = "%s";
+      colorstrings[OBJ] = "%s";
+      colorstrings[VC]  = "%s";
+  }
+  return;
+}
+
+
+private string _ls_output_short(mixed filedata,
+                                int maxlen,int counter,int maxcount)
+{
+  string tmp;
+  tmp=filedata[BASENAME];
+  maxlen-=sizeof(tmp);
+  switch(filedata[FILESIZE])
+  {
+    case -2: tmp=sprintf(colorstrings[DIR],tmp);
+             maxlen-=colorstrings[DIR,1]; break;
+    case -1: tmp=sprintf(colorstrings[VC],tmp);
+             maxlen-=colorstrings[VC,1]; break;
+    default: if (find_object(filedata[FULLNAME]))
+             {
+               maxlen-=colorstrings[OBJ,1];
+               tmp=sprintf(colorstrings[OBJ],tmp); break;
+             }
+  }
+  if (!maxcount) return tmp+"\n";
+  return sprintf("%-*s%s",(maxlen+sizeof(tmp)),tmp,
+	((counter++)==maxcount?(counter=0,"\n"):"  "));
+}
+
+private int _ls_maxlen(mixed filedata,int flags,int maxlen)
+{
+  string base;
+  int size;
+  base=filedata[BASENAME];
+  if (!(flags&LS_A)&&(base[0]=='.')) return 0;
+#if __VERSION__ < "3.2.9"
+  if (sizeof(base)>maxlen) maxlen=sizeof(base);
+#else
+  maxlen=max(maxlen,sizeof(base));
+#endif
+  return 1;
+}
+
+private string _ls_output_long(mixed filedata, int flags,closure valid_read,
+                               closure valid_write,closure creator_file)
+{
+  string *tmp,full,base,path,date,creator,group;
+  int size,dir,ftime;
+  object ob;
+  
+  base=filedata[BASENAME];
+  if (!(sizeof(base))||((!(flags&LS_A))&&(base[0]=='.')))
+    return 0;
+  size=filedata[FILESIZE];
+  path=filedata[PATHNAME];
+  tmp=(string *)call_other(master(),"full_path_array",
+                 filedata[FULLNAME],getuid());
+  full=sprintf("/%s",implode(tmp,"/"));
+  dir=(size==-2);
+  ob=find_object(full);
+  ftime=filedata[FILEDATE];
+  date=dtime(ftime);
+  date=sprintf("%s %s %s",date[9..11],date[5..6],
+               ((time()-ftime)<31536000?date[19..23]:(" "+date[13..16])));
+  creator="";
+  group="";
+  if (flags&LS_U)
+  {
+    creator=(string)call_other(master(),"creator_file",full);
+    switch(creator)
+    {
+      case ROOTID: creator="root"; break;
+      case BACKBONEID: creator="daemon"; break;
+      default: if(!creator) creator="none"; break;
+    }
+  }
+  if (flags&LS_G)
+  {
+    if (sizeof(tmp))
+    {
+      switch(tmp[0])
+      {
+      case WIZARDDIR: group="magier"; break;
+      case "news": group="news"; break;
+      case "mail": group="mail"; break;
+      case "open": group="public"; break;
+      case "p": group="project"; break;
+      case DOMAINDIR: if (sizeof(tmp)>1) { group=tmp[1]; break; }
+      default: group="mud"; break;
+      }
+    }
+    else group="mud";
+  }
+  if (dir) base=sprintf(colorstrings[DIR],base);
+  else
+  {
+    if (ob)
+    {
+      if (size==-1)
+        base=sprintf(colorstrings[VC],base);
+      else
+        base=sprintf(colorstrings[OBJ],base);
+    }
+  }
+  return sprintf(("%c%c%c%c %3d"+((flags&LS_U)?" %-24.24s":"%-0.1s")+
+                 ((flags&LS_G)?" %-8.8s":"%0.1s")+" %8s %s %s\n"),
+                 (dir?'d':'-'),
+                 (!funcall(valid_read,full,getuid(),
+                           "read_file",this_object())?'-':'r'),
+                 (!funcall(valid_write,full,getuid(),
+                           "write_file",this_object())?'-':'w'),
+                 (ob?'x':'-'),
+                 (dir?(sizeof((get_dir(full+"/*")||({}))-({".",".."}))):0),
+                 creator,group,(dir?"-":size==-1?"<vc>":to_string(size)),
+                                    date,base);
+}
+
+
+static int _ls(string cmdline)
+{
+  int flags,cmp,i,arg_size,size;
+  int maxlen,counter,maxcount;
+  string *args,output;
+  mixed *tmp;
+  closure output_fun,v_read,v_write,creator,sort_fun;
+  
+  cmdline=_unparsed_args()||"";
+  args=parseargs(cmdline,&flags,LS_OPTS,1);
+  if (flags==-1)
+    return USAGE("ls [-" LS_OPTS "] [<Verzeichnis>] [<Verzeichnis2> ..]");
+  SetColorstrings();
+  output="";
+  if (!sizeof(args)) args=({ QueryProp(P_CURRENTDIR) });
+  if (!sizeof(args=file_list(args,MODE_LSA,0,"/")))
+    return notify_fail("ls: Keine passenden Verzeichnisse gefunden.\n"), 0;
+// Files sortieren die erste
+  if (flags&LS_T) cmp=FILEDATE;
+  else if (flags&LS_S) cmp=FILESIZE;
+  else cmp=BASENAME; // =0 :-)
+  sort_fun=lambda(({ 'a,'b }),({
+    ((!cmp&&!(flags&LS_R))||(cmp&&(flags&LS_R))?#'>:#'<),
+    ({#'[,'a,cmp}),({#'[,'b,cmp})}));
+  args=sort_array(args,sort_fun);
+// Ausgabeformat bestimmen
+  if (flags&LS_L)
+  {
+    v_read=VALID_READ_CL;
+    v_write=VALID_WRITE_CL;
+    creator=CREATOR_CL;
+  }
+  arg_size=sizeof(args);
+  if (arg_size>1||(arg_size&&args[0][FILESIZE]>=0))
+  {
+    if (flags&LS_L)
+      tmp=map(args,#'_ls_output_long,flags,v_read,
+                    v_write,creator)-({0});
+    else
+    {
+      counter=0;maxlen=0;
+      tmp=filter(args,#'_ls_maxlen,flags,&maxlen);
+      if (maxlen>76) maxlen=76;
+      maxcount=(78/(maxlen+2))-1;
+      tmp=map(tmp,#'_ls_output_short,maxlen,&counter,maxcount);
+    }
+    output=sprintf("\n%d Dateien/Unterverzeichnisse:\n%s\n",
+                   sizeof(tmp),implode(tmp,""));
+  }
+  for(i=0;i<arg_size;i++)
+  {
+    tmp=({});
+    size=args[i][FILESIZE];
+    if (size==-2)
+    {
+      tmp=file_list(({args[i][FULLNAME]+"/*"}),MODE_LSB,0,"/");
+      tmp=sort_array(tmp,sort_fun);
+      if (flags&LS_L)
+        tmp=map(tmp,#'_ls_output_long,flags,v_read,
+                      v_write,creator)-({0});
+      else
+      {
+        counter=0;maxlen=0;
+        tmp=filter(tmp,#'_ls_maxlen,flags,&maxlen);
+        maxcount=(78/(maxlen+2));
+        if (maxcount) maxcount--;
+        tmp=map(tmp,#'_ls_output_short,maxlen,&counter,maxcount);
+      }
+      if (sizeof(tmp))
+      {
+        output+=sprintf("\n%s: Verzeichnis, %d Dateien/Unterverzeichnisse:\n",
+                      args[i][FULLNAME],sizeof(tmp));
+        output+=(implode(tmp,"")+((string)(tmp[<1][<1..<1])=="\n"?"":"\n"));
+      }
+      else
+      {
+        output+=sprintf("\n%s: Leeres Verzeichnis.\n",args[i][FULLNAME]);
+      }
+    }
+  }
+  More(output);
+  return 1;
+}
+
+//                              ########
+//############################### MORE ###################################
+//                              ########
+//
+// _more_file: Mehrere Files hintereinander ausgeben
+//
+
+private void _more_file(string *arg)
+{
+  if (!sizeof(arg)) return;
+  printf("more: Naechste Datei: %s\n",arg[0]);
+  More(arg[0],1,#'_more_file,({ arg[1..]}));
+  return;
+}
+
+
+private mixed _size_filter(mixed *arg)
+{
+  if (arg[FILESIZE]>0) return arg[FULLNAME];
+  if (arg[FILESIZE]==0)
+  {
+    printf("%s: %s: Leere Datei.\n",query_verb()||"more",arg[FULLNAME]);
+    return 0;
+  }
+  if (arg[FILESIZE]==-2)
+    printf("%s: %s ist ein Verzeichnis.\n",query_verb()||"more",arg[FULLNAME]);
+  else
+    printf("%s: %s: Datei existiert nicht.\n", query_verb()||"more",
+           arg[FULLNAME]);
+  return 0;
+}
+
+
+//
+// _more: Dateien anzeigen
+// cmdline: Kommandozeile
+//
+
+static int _more(string cmdline)
+{
+  mixed *args;
+  int flags;
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,"",1);
+  if (flags==-1||!sizeof(args)) return USAGE("more <datei> [<datei2>..]");
+  args=file_list(args,MODE_MORE,0,"/");
+  if (!sizeof(args))
+    return printf("more: %s: Keine passende Datei gefunden.\n",cmdline),1;
+  args=map(args,#'_size_filter)-({0});
+  if (sizeof(args)) _more_file(args);
+  return 1;
+}
+
+//                         ###################
+//########################## HEAD, TAIL, CAT #############################
+//                         ###################
+
+static int _cat(string cmdline)
+{
+  mixed *args;
+  int flags;
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,"",1);
+  if(flags==-1||!sizeof(args))
+    return USAGE(query_verb()+" <dateiname> [<datei2>..]");
+  args=file_list(args,MODE_CAT,0,"/");
+  if (!sizeof(args))
+    return printf("%s: %s: Keine passende Datei gefunden.\n",query_verb(),
+                cmdline),1;
+  args=map(args,#'_size_filter)-({0});
+  if (!sizeof(args)) return 1;
+  while(sizeof(args))
+  {
+    switch(query_verb())
+    {
+      case "cat":
+        if (!cat(args[0]))
+          printf("cat: %s konnte nicht angezeigt werden.\n",args[0]);
+        break;
+      case "head":
+        if (!cat(args[0],0,10))
+          printf("head: %s konnte nicht angezeigt werden.\n",args[0]);
+        break;
+      case "tail": tail(args[0]); break;
+    }
+    args=args[1..];
+  }
+  return 1;
+}
+
+//                              #######
+//############################### MAN ###################################
+//                              #######
+
+static int _man(string cmdline)
+{
+  mixed *args;
+  int i, flags;
+  string *tmp, *tmp2;
+
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,MAN_OPTS,0);
+  
+  if (flags==-1 ||
+      (sizeof(args)!=1 &&
+       (sizeof(args)<2 || sizeof(args[1])>1 || !(i=to_int(args[1])))))
+    return USAGE("man [-" MAN_OPTS "] <hilfeseite>");
+  tmp=explode(args[0],"/");
+
+  if (oldman_result && sizeof(tmp)==1 && sizeof(args)==1 &&
+      sizeof(tmp[0])==1 && (i=to_int(tmp[0])) && member(oldman_result,i)) {
+   tmp=({oldman_result[i,0],oldman_result[i,1]});
+   i=0;
+  }
+  else if (!(flags&(MAN_M|MAN_R))&&sizeof(tmp)>1)
+  {
+    if (file_size(MAND_DOCDIR+args[0])>=0)
+      tmp=({tmp[<1],args[0]});
+    else
+      tmp=({});
+  }
+  else
+  {
+    if (flags&MAN_R)
+    {
+      flags&=(~MAN_M);
+      if (catch(regexp(({""}),args[0]))||
+          !regexp(({""}),args[0]))
+        return printf("man: Ungueltiger Ausdruck in Maske.\n"),1;
+    }
+    tmp=(string *)call_other(MAND,"locate",args[0],flags&(MAN_M|MAN_R));
+  }
+
+  oldman_result=(mapping)0;
+  if(i && sizeof(tmp)>2 && sizeof(tmp)>=(i<<1))
+    tmp=tmp[((i<<1)-2)..((i<<1)-1)];
+  switch(sizeof(tmp))
+  {
+    case 0:
+      printf("Keine Hilfeseite gefunden fuer '%s'.\n",args[0]);
+      break;
+    case 2:
+       /*
+         if (flags&MAN_I)
+         {
+         // BRANCH TO MANUALD
+         return 1;
+         }
+       */
+      printf("Folgende Hilfeseite wurde gefunden: %s\n",tmp[1]);
+      More(MAND_DOCDIR+tmp[1],1);
+      return 1;
+    default:
+      i=sizeof(tmp)>>1;
+      tmp2=allocate(i);
+      oldman_result=m_allocate(i,2);
+      while(i)
+      {
+        tmp2[(i-1)]=sprintf("%d: ",i)+tmp[(i<<1)-2];
+	oldman_result[i,0]=tmp[(i<<1)-2];
+	oldman_result[i,1]=tmp[(i<<1)-1];
+        i--;
+      }
+      printf("Es wurden folgende potentiell passenden Seiten gefunden:\n"
+             "%'-'78.78s\n%s%'-'78.78s\n","",
+             break_string(implode(tmp2," "),78),"");
+      break;
+  }
+  return 1;
+}
+
+//                              ########
+//############################### RMAN ##################################
+//                              ########
+
+static int _rman(string str)
+{
+  int flags;
+  string *args;
+  
+  str=_unparsed_args();
+  args=parseargs(str,&flags,"",0);
+  if (flags==-1||sizeof(args)!=2)
+    return USAGE("rman <hilfeseite> <mudname>");
+  write("man: " +
+        (string)call_other(UDP_CMD_DIR+"man","send_request",args[1],args[0]));
+  return 1;
+}
+
+
+//                            #############
+//############################# SHOWPROPS ###############################
+//                            #############
+
+//
+// _showprops: Zeigt Properties zu einem Objekt an
+//
+
+static int _showprops(string str)
+{
+   int i;
+   string *list, ausgabe;
+
+   if (str) {
+      if (str[0]!='/') str="/"+str;
+      if (str[0..4]!="/std/")
+      {
+         printf("showprops: Es koennen nur Properties von Objekten "
+                "aus /std/ angezeigt werden.\n");
+         return 1;
+      }
+      if (str[<1]=='.') str+="c";
+      if (str[<2..<1]!=".c") str+=".c";
+      if (file_size(str)<0)
+      {
+        printf("showprops: %s: Es gibt kein Objekt diesen Namens.\n",str[0..<3]);
+        return 1;
+      }
+      if (catch(call_other(str[0..<3], "???")))
+      {
+        printf("showprops: %s: Datei konnte nicht geladen werden.\n",str);
+        return 1;
+      }
+   }
+   if (!str || !find_object(str)) {
+      notify_fail("Welche Properties moechtest Du sehen?"
+                  " Bsp.: <showprops /std/npc>\n");
+      return 0;
+   }
+   list=inherit_list(find_object(str));
+#if __VERSION__ < "3.2.9"
+   list=map(list,lambda(({'x}),({#'extract,'x,4,-2})));
+   list+=map(list,lambda(({'x}),({#'[<,({#'old_explode,'x,"/"}),1})));
+   list=map(m_indices(mkmapping(list)),lambda(({'x}),({#'+,({#'+,"/sys/",'x}),"h"})));
+   list=filter(list,lambda(({'x}),({#'>,({#'file_size,'x}),0})) );
+#else
+   list=map(list,(: return $1[5..<2]+"h"; :));
+   list+=map(list,(: return explode($1,"/")[<1]; :));
+   list=map(m_indices(mkmapping(list)),(: return "/sys/"+$1; :));
+   list=filter(list,(: return file_size($1)>0; :));
+#endif
+   list=sort_array(list, #'<);
+   ausgabe="";
+   for (i=sizeof(list);i--;)
+   {
+#if __VERSION__ < "3.2.9"
+     str=implode(filter(old_explode(read_file(list[i]), "\n"),
+        lambda( ({ 'x }), ({#'==, ({#'extract, 'x, 0, 9}), "#define P_"}) ))
+                 , "\n");
+#else
+     str=implode(filter(explode(read_file(list[i]),"\n"),
+                              (: return $1[0..9]=="#define P_";:)),"\n");
+#endif
+     if (str!="") ausgabe+=sprintf("%s\n%s\n\n", list[i], str);
+   }
+   if (ausgabe!="")
+     More(ausgabe);
+   else
+     printf("Keine Property-Definitionen zu diesem Objekt gefunden!\n");
+   return 1;
+}
+
+//                              ########
+//############################### GREP ###################################
+//                              ########
+
+#if __VERSION__ < "3.2.9"
+
+private int _grep_filter(string filename)
+{
+  return (call_other("valid_write",filename,getuid(this_object()),
+                      "write_file",this_object())!=0);
+}
+
+#endif
+
+//
+// grep_file: Datei greppen
+// rexpr: Regular Expression
+// flags: Flags
+//
+
+private int grep_file(mixed filedata, string rexpr, int flags)
+{
+  string fullname,data,carry,*lines,*lines_orig,*tmp,*result;
+  int ptr,count,i,nol,match,index;
+  fullname=filedata[FULLNAME];
+  if ((flags&GREP_F)&&fullname=="/players/"+getuid()+"/grep.out")
+  {	 
+	write_file("/players/"+getuid()+"/grep.out",
+                   "Uebergehe grep.out ...\n");
+	return RET_FAIL;
+  }
+  switch(filedata[FILESIZE])
+  {
+    case -2: return RET_FAIL;
+    case -1: return ERROR(DOESNT_EXIST,fullname,RET_FAIL);
+    case 0:  return RET_FAIL;
+    default: break;
+  }
+  if (!MAY_READ(fullname)) return ERROR(NO_READ,fullname,RET_FAIL);
+  carry=""; result=({});
+  if (flags&GREP_I)
+    rexpr=lower_case(rexpr);
+  do
+  {
+    data=read_bytes(fullname,ptr,MAXLEN)||"";
+    ptr+=MAXLEN;
+    lines=explode(carry+data,"\n");
+    switch(sizeof(lines))
+    {
+      case 0: continue;
+      case 1:
+        carry="";
+        break;
+      default:
+        carry=lines[<1];
+        lines=lines[0..<2];
+        break;
+    }
+    lines_orig=lines;
+    if (flags&GREP_I)
+      lines=map(lines,#'lower_case);
+    nol=sizeof(lines);
+    for (i=0;i<nol;i++)
+    {
+      match=sizeof(regexp(lines[i..i],rexpr));
+      if (flags&GREP_V) match=!match;
+      if (match)
+      {
+        if (!(flags&GREP_C))
+        {
+          if (flags&GREP_N)
+            result+=({ sprintf("%4d %s",index+i+1,lines_orig[i])});
+          else
+            result+=lines_orig[i..i];
+        }
+        count++;
+      }
+    }
+    index+=nol;
+  }
+  while(sizeof(data)==MAXLEN);
+  if (carry!="")
+  {
+    if (flags&GREP_I) carry=lower_case(carry);
+    match=sizeof(regexp(({ carry }),rexpr));
+    if (flags&GREP_V) match=!match;
+    if (match)
+    {
+      if(!(flags&GREP_C))
+      {
+        if(flags&GREP_N)
+           result+=({ sprintf("%4d %s",index+1,carry) });
+        else
+           result+=({carry});
+      }
+      count++;
+    }
+  }
+  carry="";
+  if (count)
+  {
+    if (flags&GREP_L) carry=sprintf("%s\n",fullname);
+    else if (flags&GREP_H) data="";
+    else data=sprintf(" %s:",fullname);
+    if (flags&GREP_C) carry=sprintf("%s %d passende Zeile%s.\n",data,count,
+                             (count==1?"":"n"));
+    else
+      carry=(data+"\n"+implode(result,"\n")+"\n");
+  }
+  if (flags&GREP_F)
+    return write_file("/players/"+getuid()+"/grep.out",carry);
+  write(carry);
+  return RET_OK;
+}
+
+static int _grep(string cmdline)
+{
+  string rexpr,mask;
+  mixed *args;
+  int flags;
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,GREP_OPTS,0);
+  if ((flags==-1)||!sizeof(args))
+    return USAGE("grep [-" GREP_OPTS
+          "] <regexp> <datei/verz> [<datei2> ... ] [<maske>]");
+  rexpr=args[0];
+  if (catch(regexp(({""}),rexpr))||!regexp(({""}),rexpr))
+    return printf("grep: Ungueltiger Suchausdruck: %s\n",rexpr) ,1;
+  args=args[1..];
+  if (flags&GREP_M)
+  {
+    mask=args[<1];
+    args=args[0..<2];
+  }
+  if (!sizeof(args))
+      return USAGE("grep [-" GREP_OPTS
+          "] <regexp> <datei/verz> [<datei2> ... ] [<maske>]");
+  args=map(args,#'to_filename)-({0});
+  /*
+#if __VERSION__ < "3.2.9"
+  args=filter(args,#'_grep_filter);
+#else
+  args=filter(args,(: return (valid_write($1,
+            getuid(this_object()),"write_file",this_object())!=0):));
+#endif
+  */
+  args=file_list(args,MODE_GREP,(flags&GREP_R?1:0),"/",mask);
+  if (!sizeof(args))
+    return printf("Keine passenden Dateien gefunden.\n"),1;
+  if (flags&GREP_I) rexpr=lower_case(rexpr);
+  if (flags&GREP_F)
+  {
+    if (file_size("/players/"+getuid()+"/grep.out")==-2||
+	  !MAY_WRITE("/players/"+getuid()+"/grep.out"))
+      return printf("grep: Datei /players/%s/grep.out kann nicht "
+		      "geschrieben werden.\n",getuid()),1;
+    else
+      write_file("/players/"+getuid()+"/grep.out",
+                 "Ausgabe von \"grep " + _unparsed_args() + "\":\n"); 
+  }
+  asynchron(args,#'grep_file,rexpr,flags,0);
+  return 1;
+}
diff --git a/std/shells/magier/magier_ext.c b/std/shells/magier/magier_ext.c
new file mode 100644
index 0000000..a5d30b2
--- /dev/null
+++ b/std/shells/magier/magier_ext.c
@@ -0,0 +1,733 @@
+// $Id: magier_ext.c 9555 2016-05-03 20:42:46Z Zesstra $
+#pragma strict_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <wizlevels.h>
+#include <logging.h>
+#include <magier.h>
+#include <snooping.h>
+#include <driver_info.h>
+#define NEED_PROTOTYPES
+#include <player/telnetneg.h>
+#include <player/base.h>
+#undef NEED_PROTOTYPES
+#include <properties.h>
+#include <files.h>
+#include <events.h>
+#include <player/comm.h>
+
+inherit "/std/shells/magier/parsing";
+inherit "/std/shells/magier/upd";
+inherit "/std/shells/magier/fileview";
+inherit "/std/shells/magier/objects";
+inherit "/std/shells/magier/fileedit";
+//inherit "/std/shells/magier/todo";
+inherit "/std/shells/magier/players";
+inherit "/std/shells/magier/admin";
+inherit "/std/shells/magier/moving";
+inherit "/std/shells/magier/comm";
+
+
+//                              #######
+//############################### SET #################################
+//                              #######
+
+
+//
+// _set(): Shell-Befehl 'set'
+// args:   Kommandozeilenargumente
+//
+
+static int _set(mixed args)
+{
+  int pos;
+  string var;
+  mixed val;
+  mapping vars;
+
+  if(!args=_unparsed_args())
+  {
+    if(!sizeof(vars=QueryProp(P_VARIABLES))) 
+      printf("Du hast noch keine Variablen definiert!\n");
+    else
+    {
+      printf("Du hast die folgenden Variablen definiert:\n");
+      walk_mapping(vars,((: printf(" %-20s = %s\n",$1,
+                                 pointerp($2)?implode($2,":"):$2) :)));
+
+    }
+    return 1;
+  }
+  pos = member(args,' ');
+  if(pos == -1)
+    if(sizeof(args))
+      {
+        m_delete(QueryProp(P_VARIABLES), args);
+        printf("Variable %s wurde geloescht.\n",args);
+        return 1;
+      }
+    else return USAGE("set <variable> <wert>");
+  var = args[0..pos-1];
+  val = (member(args[pos+1..],':')>-1?
+         explode(args[pos+1..], ":") :
+         args[pos+1..]);
+  vars=QueryProp(P_VARIABLES);
+  vars+= ([var : val]);
+  printf("Variable gesetzt: %s = %s\n",var,args[pos+1..]);
+  return 1;
+}
+
+//                            ############
+//############################# CD + PWD ###################################
+//                            ############
+
+static int _pwd()
+{
+  printf("Aktuelles Verzeichnis: %s\n",QueryProp(P_CURRENTDIR));
+  return 1;
+}
+
+static int _cd2(string cmdline)
+{
+  string dest,tmp;
+  int flags;
+  mixed *args;
+  
+  args=parseargs(cmdline,&flags,CD_OPTS,1);
+  if (flags==-1) return 0;
+  if(flags&CD_B)
+  {
+    dest=QueryProp(P_LASTDIR);
+  }
+  if (sizeof(args)>1)
+    return notify_fail("Man kann sich (leider) nur in einem "
+                       "Verzeichnis gleichzeitig befinden.\n"),0;
+  if(!dest)
+  {
+    if (!sizeof(args))
+    {
+      if (IS_WIZARD(this_object()))
+        dest="/players/"+getuid(this_object());
+      else
+        dest="/doc";
+    }
+    else
+    {
+      dest=args[0];
+    }
+  }
+  
+  if (dest!="/")
+  {
+    args=file_list(({dest}),MODE_CD,0,"/");
+    args = filter(args, function int (mixed arr)
+        { return arr[FILESIZE] == FSIZE_DIR; } );
+
+    if (!sizeof(args))
+      return notify_fail("cd: "+dest+
+                       ": Kein solches Verzeichnis gefunden.\n"),0;
+    if (sizeof(args)>1)
+      return notify_fail("cd: "+dest+": Maske ist nicht eindeutig.\n"),0;
+    dest=args[0][FULLNAME];
+  }
+  if (!IS_WIZARD(this_object())&&dest[0..3]!="/doc") dest="/doc";
+  SetProp(P_LASTDIR,QueryProp(P_CURRENTDIR));
+  printf("Aktuelles Verzeichnis ist jetzt: %s\n",SetProp(P_CURRENTDIR,dest));
+  tmp="";
+  if (((flags&CD_L)||
+       ((!m_contains(&tmp,QueryProp(P_VARIABLES),"CD_SHORT")||
+         tmp!="1")&&(!(flags&CD_S)))))
+    tell_object(this_object(),
+                read_file(dest+(dest[<1]=='/'?".readme":"/.readme"))||"");
+  return 1;
+}
+
+static int _cd(string cmdline)
+{
+  return _cd2(_unparsed_args());
+}
+
+static string _set_currentdir(string path)
+{
+  Set(P_CURRENTDIR, path);
+  this_object()->modify_prompt();  // Prompt mit neuem Pfad setzen, telnetneg
+  return path;
+}
+
+private string _query_currentdir()
+{
+  string c;
+  if(!c=Query(P_CURRENTDIR))
+    return Set(P_CURRENTDIR, "/players/"+getuid(this_object()),F_VALUE);
+  return c;
+}
+
+//                           ##########
+//############################ PROMPT #################################
+//                           ##########
+
+//
+// _prompt_subst: Mudnamen, Usernamen, Zeilenvorschub in Eingabeauf-
+//                forderung einbauen
+// str:           Uebergebener Kommandozeilenabschnitt
+//
+
+private string _prompt_subst(string str)
+{
+  switch(str)
+  {
+    case "\\h": return sizeof(MUDNAME)?MUDNAME:"Mud ohne Namen";
+    case "\\u": return capitalize(getuid(this_object()));
+    case "\\n": return "\n";
+    case "\\t": return "\t";
+    case "\\w": return "\w";
+  }
+  return str;
+}
+
+
+//
+// _prompt: Eingabeaufforderung setzen
+// arg:     Kommandozeile
+//
+
+static int _prompt(string args)
+{
+  string *pargs;
+
+  args=(extern_call()?_unparsed_args():args);
+  if (!sizeof(args)) args="> ";
+  if (args[0]=='"') args=args[1..<2];     //");
+  if(!sizeof(pargs = regexplode(args, "\\\\[thuwn]")))
+    return USAGE("prompt \"<Eingabeaufforderungsdefinition>\"");
+  pargs=map(pargs,#'_prompt_subst);
+
+  SetProp(P_PROMPT,implode(pargs,""));
+  return 1;
+}
+
+static string _set_prompt(string prompt) {
+  Set(P_PROMPT, prompt, F_VALUE);
+  this_object()->modify_prompt(); // neuen Prompt setzen (telnetneg.c)
+  return prompt;
+}
+
+
+//                           ##############
+//############################ SHOWPRESAY ###############################
+//                           ##############
+
+static int _showpresay(string cmdline)
+{
+  int presay;
+  presay=QueryProp(P_CAN_FLAGS)&CAN_PRESAY;
+  cmdline=_unparsed_args(0);
+  if (!sizeof(cmdline))
+  {
+    printf("Dein Presay wird im Moment %sangezeigt.\n"
+           "%sschalten mit \'showpresay %s\'.\n",
+           presay?"":"nicht ",presay?"Aus":"Ein",
+           presay?"aus":"ein");
+    return 1;
+  }
+  if (cmdline=="ein"||cmdline=="an")
+  {
+    printf("Dein Presay wird jetzt angezeigt.\n");
+    SetProp(P_CAN_FLAGS,QueryProp(P_CAN_FLAGS)|CAN_PRESAY);
+    return 1;
+  }
+  if (cmdline=="aus")
+  {
+    printf("Dein Presay wird jetzt nicht mehr angezeigt.\n");
+    SetProp(P_CAN_FLAGS,QueryProp(P_CAN_FLAGS)&~CAN_PRESAY);
+    return 1;
+  }
+  return USAGE("showpresay [ein|an|aus]");
+}
+
+//                              ########
+//############################### EXEC ###############################
+//                              ########
+
+static int _exec(string filename)
+{
+  object ob;
+  if (!IS_LORD(this_object())) return 0;
+  if (this_player()!=this_interactive()) return 0;
+  if (this_player()!=this_object()) return 0;
+  if (!(filename=_unparsed_args())) return USAGE("exec <objektname>");
+  filename=(string)"secure/master"->_get_path(filename,getuid());
+  if (file_size(filename)<0&&(!to_filename(filename+".c"))||
+      file_size(to_filename(filename+".c"))<0)
+  {
+    printf("exec: %s: Datei nicht vorhanden oder ein Verzeichnis.\n",filename);
+    return 1;
+  }
+  if (catch(call_other(filename,"????"))) 
+  {
+    printf("exec: Fehler beim Laden von %s.\n",filename);
+    return 1;
+  }
+  if (!(ob=clone_object(filename))) return 0;
+  if (getuid(ob) != getuid(this_object()))
+  {
+    printf("exec: UID-Konflikt: %s <-> %s.\n",getuid(ob),
+           getuid(this_object()));
+    destruct(ob);
+    return 1;
+  }
+  log_file(SHELLLOG("EXEC"),
+         sprintf("%12.12s %40.40s %25.25s\n",
+                 capitalize(getuid(this_object())),filename,dtime(time())[4..]));
+  disable_commands();
+  exec(ob,this_object());
+  if (interactive(this_object())||!interactive(ob))
+  {
+    enable_commands();
+    printf("Fehler bei EXEC: Uebertragung der Shell "
+           "nicht moeglich.\n");
+    return 1;
+  }
+  ob->start_player(capitalize(getuid(this_object())));
+  remove();
+  return 1;
+}
+//                            ############
+//############################# SNOOPING ###############################
+//                            ############
+
+private nosave string snoop_allowed;
+private nosave object snoopee;
+
+nomask int QueryAllowSnoop(object who)
+{
+  return (getuid(who) == snoop_allowed);
+}
+
+static int _sallow(string str)
+{
+  object ob;
+
+  if (!sizeof(str))
+  {
+    if (!snoop_allowed) return USAGE("sallow [<name>]\n");
+    str=snoop_allowed;
+    snoop_allowed=0;
+    printf("Du entziehst %s die Erlaubnis zum Snoopen.\n",capitalize(str));
+    ob=query_snoop(this_player());
+    if (!ob||(getuid(ob)!=str)) return 1;
+    tell_object(ob,break_string(sprintf("%s entzieht Dir die "
+            "Erlaubnis zum snoopen und zwingt Dich so dazu, mit "
+            "dem Snoopen aufzuhoeren.\n",capitalize(getuid()))));
+    snoop(ob);
+    return 1;
+  }
+  if (snoop_allowed) _sallow(""); // Erstmal abschalten
+  ob=find_player(lower_case(str));
+  str=capitalize(str);
+  if (!ob) return
+      printf("sallow: Spieler %s konnte nicht gefunden werden.\n",str),1;
+  if (query_wiz_grp(ob)>query_wiz_grp(this_player()))
+    return printf("sallow: %s hat einen hoeheren Rang als Du und kann "
+                  "Dich daher ohnehin snoopen.\n",str),1;
+  snoop_allowed=getuid(ob);
+  return printf("sallow: Du erlaubst %s, Dich zu snoopen.\n",str),1;
+}
+
+static int _snoop(string cmdline)
+{
+  object ob;
+  int flags;
+  string *args;
+
+  if (!sizeof(cmdline=_unparsed_args())||
+      sizeof(args=parseargs(cmdline,&flags,SNOOP_OPTS,0))!=1||flags==-1)
+  {
+    if (!snoop(this_object())) return USAGE("snoop [-" SNOOP_OPTS
+                                            "] [<spieler>]\n");
+    if (snoopee)
+      printf("Du snoopst %s jetzt nicht mehr.\n",capitalize(getuid(snoopee)));
+    else
+    {
+      printf("Du hoerst auf zu snoopen.\n");
+    // evtl. irgendetwas loggen ... sollte eigentlich nicht passieren.
+    }
+    snoopee=(object)0;
+    return 1;
+  }
+  SetProp(P_SNOOPFLAGS,flags); // FUNKTIONIERT NUR, WENN magier.h und
+                               // snooping.h abgeglichen sind
+  if (!(ob = find_player(args[0])))
+    return printf("snoop: Konnte keinen Spieler '%s' finden.\n",
+                  capitalize(args[0])),1;
+  if (!snoop(this_player(),ob))
+    return printf("snoop: Der Versuch, %s zu snoopen, ist "
+                  "fehlgeschlagen.\n%s",capitalize(args[0]),
+                  ((~flags)&SNOOP_F)?"Eventuell funktioniert es mit dem "
+                  "Flag '-f'.\n":""),1;
+  snoopee=ob;
+  return printf("Du snoopst jetzt %s.\n",capitalize(getuid(ob))),1;
+}
+
+
+//                           ##########
+//############################ MSCHAU #################################
+//                           ##########
+
+static int _mschau(string str)
+{
+  if (this_interactive()!=this_object()) return 0;
+  if (str=="ja"||str=="an"||str=="ein")
+  {
+    if ( QueryProp(P_WANTS_TO_LEARN) )
+      printf("Du hast den Magier-Modus doch schon eingeschaltet!\n");
+    else
+    {
+      printf("Du hast jetzt den Magier-Modus eingeschaltet.\n" );
+      SetProp(P_WANTS_TO_LEARN, 1);
+    }
+    return 1;
+  }
+  if (str=="nein"||str=="aus")
+  {
+    if (QueryProp(P_WANTS_TO_LEARN))
+    {
+      printf( "Du schaltest den Magier-Modus aus.\n");
+      set_heart_beat(1);
+      SetProp(P_WANTS_TO_LEARN,0);
+    }
+    else
+      printf("Du hast den Magier-Modus doch schon ausgeschaltet.\n");
+    return 1;
+  }
+  if (str=="+debug")
+  {
+    printf("Du nimmst jetzt Debugausgaben wahr.\n");
+    SetProp(P_WIZ_DEBUG,1);
+    return 1;
+  }
+  if (str=="-debug")
+  {
+    printf("Du nimmst jetzt keine Debugausgaben mehr wahr.\n");
+    SetProp(P_WIZ_DEBUG,0);
+    return 1;
+  }
+  return USAGE("mschau [an|ein|ja|aus|nein|+debug|-debug]\n");
+}
+
+//                           ###########
+//############################ PROTECT ################################
+//                           ###########
+
+static int _protect(string str)
+{
+ 
+  if (this_object()!=this_interactive()) return 0;
+  if (!sizeof(str))
+    return USAGE("protect <propertyname>\n");
+  Set(str, PROTECTED, F_MODE);
+  return printf("Deine Property %s ist jetzt %sgeschuetzt.\n",
+                str,(Query(str,F_MODE) & PROTECTED?"":"nicht mehr ")),1;
+}
+
+
+//                         ###############
+//########################## VIS + INVIS ##############################
+//                         ###############
+
+static int _hbstop;
+static int _age;
+
+static int _invis(string inform)
+{
+
+  if (QueryProp(P_INVIS))
+    return printf("Du bist doch schon unsichtbar!\n"),1;
+  tell_room(environment(),sprintf("%s %s.\n",capitalize(Name()),
+            QueryProp(P_MMSGOUT)),({ this_object() }));
+  if (inform=="e") {
+    // Logout-event ausloesen
+    EVENTD->TriggerEvent(EVT_LIB_LOGOUT, ([
+            E_OBJECT: this_object(),
+            E_PLNAME: getuid(this_object()),
+            E_ENVIRONMENT: environment() ]) );
+
+    call_notify_player_change(0);
+  }
+
+  SetProp(P_INVIS, QueryProp(P_AGE));
+  printf("Du bist jetzt unsichtbar.\n");
+  return 1;
+}
+
+static int _vis(string inform)
+{
+  if (!QueryProp(P_INVIS))
+    return printf("Du bist doch schon sichtbar.\n"),1;
+   tell_room(environment(),sprintf("%s %s.\n",capitalize(Name()),
+            QueryProp(P_MMSGIN)),({ this_object() }));
+  SetProp(P_INVIS, 0);
+  if (inform=="e") {
+    // Login-event ausloesen
+    EVENTD->TriggerEvent(EVT_LIB_LOGIN, ([
+          E_OBJECT: this_object(),
+          E_PLNAME: getuid(this_object()),
+          E_ENVIRONMENT: environment() ]) );
+
+    call_notify_player_change(1);
+  }
+  printf("Du bist jetzt sichtbar.\n");
+  return 1;
+}
+
+//                          ############
+//########################### LOCALCMD ###############################
+//                          ############
+
+
+static int _localcmd()
+{
+  int size,more;
+  string *verbs,result;
+
+  // Per Umweg ueber Mapping doppelte Werte einstampfen
+  size=sizeof(verbs=m_indices(mkmapping(query_actions(this_object()))));
+  verbs-=({""});
+  more=(size>sizeof(verbs));
+  if (!sizeof(verbs))
+  {
+    if (more)
+      printf("Die vorhandenen Befehle koennen nicht angezeigt werden, "
+             "da sie per 'add_action(\"funktion\",\"\",1)' definiert "
+             "wurden.\n");
+    else
+      printf("Dir stehen keine Befehle zur Verfuegung ... eigenartig!\n");
+    return 1;
+  }
+  verbs=sort_array(verbs,#'>);
+  result=break_string(sprintf("\'%s\'",implode(verbs,"' '")),78);
+  return printf("\n%'-'78.78s\nDie fuer Dich aktuell verfuegbaren "
+                "Befehle sind:\n\n%s\n%s%'-'78.78s\n","",result,
+                more?"Zudem wurden Befehle per 'add_action("
+                "\"funktion\",\"\",1)' definiert.\n":"",""),1;
+}
+
+//                           ###########
+//############################ TRAENKE ################################
+//                           ###########
+
+static int _traenke(string str)
+{
+  if(SetProp(P_TRANK_FINDEN, !QueryProp(P_TRANK_FINDEN)))
+    write("Du kannst jetzt Zaubertraenke finden.\n");
+  else
+    write("Du findest jetzt keine Zaubertraenke mehr.\n");
+  return 1;
+}
+
+static int norm_rusage() {
+	mixed* r;
+  r = rusage();
+  return r[0]/100 + r[1]/100;
+}
+
+//                           #############
+//############################ ASYNCHRON #################################
+//                           #############
+
+//
+// asynchron(): Asynchrone Abarbeitung eines Arrays
+// array:     Zu bearbeitendes Array
+// cmd:       Auszufuehrende Closure
+// data:      Extraargumente
+// flags:     Zusatzdaten (normalerweise Flags)
+// c:         schon asynchron?
+//
+
+static varargs void asynchron(mixed* array, closure cmd, mixed data, mixed flags,int c)
+{
+  int i, j, k;
+  mixed ret_val;
+  string cmd_string;
+
+  k = norm_rusage()+5;
+  j = sizeof(array);
+  i=0;
+  
+  switch (cmd_string=explode(sprintf("%O",cmd),"->")[1])
+  {
+    case "_make":     cmd_string=(data&UPD_LOAD?"load":"upd");break;
+    case "cp_file":   cmd_string =(data?"mv":"cp"); break;
+    case "grep_file": cmd_string = "grep"; break;
+    case "rm_file":   cmd_string = "rm"; break;
+    default: break;
+  }
+  
+  while(i < j && get_eval_cost() > 200000 && norm_rusage()<k)
+    // Sowohl Too-Long-Eval als auch Lag verhindern
+  {
+    ret_val=apply(cmd,array[i],data, flags);
+    if (closurep(ret_val))
+    {
+      if(c) tell_object(this_object(),
+               sprintf("%s: Verlasse Asynchronen Modus.\n",cmd_string));
+      funcall(ret_val,array[i..],data,flags);
+      return;
+    }
+    if (ret_val==RET_DELETE)
+    {
+      array[i+array[i][SUBDIRSIZE]+1..i+array[i][SUBDIRSIZE]]=
+        ({ (array[i][0..5]+({0})) });
+    }
+    if (ret_val==RET_JUMP) i+=array[i][SUBDIRSIZE];
+    i++;
+  }
+  if(sizeof(array = array[i..])) 
+  {  
+    if(!c) tell_object(this_object(),
+              sprintf("%s: Asynchroner Modus aktiviert!\n", cmd_string));
+    call_out(#'asynchron, 1, array, cmd, data,flags, 1);
+  }
+  else
+  {
+    if(c) tell_object(this_object(),
+                      sprintf("%s: abgeschlossen.\n",cmd_string));
+      else if (query_verb()) tell_object(this_object(),
+            sprintf("%s: abgeschlossen.\n",query_verb()));
+  }
+  return;                                                    
+}
+
+//
+// ######################## Driver Status Infos ##########################
+//
+static int _driver_malloc(string str)
+{
+    if(!sizeof(str))
+    {
+        write(efun::driver_info(DI_STATUS_TEXT_MALLOC));
+        return 1;
+    }
+    if(str == "extstats")
+    {
+        write(efun::driver_info(DI_STATUS_TEXT_MALLOC_EXTENDED));
+        return 1;
+    }
+    return 0;
+}
+
+static int _driver_dumpallobj(string str)
+{
+    write("Dumping to /OBJ_DUMP ... ");
+    efun::dump_driver_info(DDI_OBJECTS);
+    efun::dump_driver_info(DDI_OBJECTS_DESTRUCTED);
+    write("done\n");
+    return 1;
+}
+
+static int _driver_opcdump(string str)
+{
+    efun::dump_driver_info(DDI_OPCODES);
+    return 1;
+}
+
+static int _driver_status(string str)
+{
+    int opt;
+
+    switch(str || "")
+    {
+        case "":
+            opt = DI_STATUS_TEXT_MEMORY;
+            break;
+
+        case "tables":
+        case " tables":
+            opt = DI_STATUS_TEXT_TABLES;
+            break;
+
+        case "swap":
+        case " swap":
+            opt = DI_STATUS_TEXT_SWAP;
+            break;
+
+        case "malloc":
+        case " malloc":
+            opt = DI_STATUS_TEXT_MALLOC;
+            break;
+
+        case "malloc extstats":
+        case " malloc extstats":
+            opt = DI_STATUS_TEXT_MALLOC_EXTENDED;
+            break;
+
+        default:
+            return 0;
+    }
+    write(efun::driver_info(opt));
+    return 1;
+}
+
+//                         ###################
+//########################## INITIALISIERUNG #############################
+//                         ###################
+
+//
+// _query_localcmds: Welche Kommandos werden in dieser Datei implementiert?
+//
+
+static mixed _query_localcmds()
+{
+  return ({
+    ({"set","_set",0,LEARNER_LVL}),
+    ({"pwd","_pwd",0,LEARNER_LVL}),
+    ({"cd","_cd",0,LEARNER_LVL}),
+    ({"prompt","_prompt",0,LEARNER_LVL}),
+    ({"showpresay","_showpresay",0,LEARNER_LVL}),
+    ({"exec","_exec",0,ARCH_LVL}),
+    ({"sallow","_sallow",0,LEARNER_LVL}),
+    ({"snoop","_snoop",0,WIZARD_LVL}),
+    ({"mschau","_mschau",0,LEARNER_LVL}),
+    ({"protect","_protect",0,WIZARD_LVL}),
+    ({"invis","_invis",0,LEARNER_LVL}),
+    ({"vis","_vis",0,LEARNER_LVL}),
+    ({"localcmd","_localcmd",0,LEARNER_LVL}),
+    ({"traenke","_traenke",0,DOMAINMEMBER_LVL}),
+    ({"malloc","_driver_malloc",0,LEARNER_LVL}),
+    ({"dumpallobj","_driver_dumpallobj",0,ARCH_LVL}),
+    ({"opcdump","_driver_opcdump",0,ARCH_LVL}),
+    ({"status","_driver_status",0,LEARNER_LVL}),
+  })
+    +fileview::_query_localcmds()
+    +upd::_query_localcmds()
+    +objects::_query_localcmds()
+    +fileedit::_query_localcmds()
+//    +todo::_query_localcmds()
+    +players::_query_localcmds()
+    +admin::_query_localcmds()
+    +moving::_query_localcmds()
+    +comm::_query_localcmds();
+}
+
+//
+// initialize: Initialisierung der Shell
+//
+
+static void initialize()
+{
+  Set(P_PROMPT, SAVE, F_MODE_AS);
+  Set(P_VARIABLES,SAVE,F_MODE_AS);
+  Set("filesys",SAVE,F_MODE_AD);   // P_FILESYS ist obsolet
+  Set("short_cwd",SAVE,F_MODE_AD); // P_SHORT_CWD auch
+  if(!mappingp(QueryProp(P_VARIABLES)))
+    SetProp(P_VARIABLES, ([]));
+  _prompt(QueryProp(P_PROMPT));
+//  todo::initialize();
+  _cd2("");
+  return;
+}
+
+static void reconnect() { _cd(QueryProp(P_CURRENTDIR)); }
diff --git a/std/shells/magier/moving.c b/std/shells/magier/moving.c
new file mode 100644
index 0000000..3095a3f
--- /dev/null
+++ b/std/shells/magier/moving.c
@@ -0,0 +1,232 @@
+// MorgenGrauen MUDlib
+//
+// moving.c
+//
+// $Id: moving.c 9142 2015-02-04 22:17:29Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <thing/properties.h>
+#include <living/moving.h>
+#include <player.h>
+#undef NEED_PROTOTYPES
+#include <wizlevels.h>
+#include <moving.h>
+#include <properties.h>
+
+private mixed verfolger()
+{
+  mixed *pur;
+
+  if (!pointerp(pur=QueryProp(P_PURSUERS))) return 0;
+  else return pur[0];
+}
+
+//                         #########
+//########################## IN+AT ############################
+//                         #########
+
+static int _move_base(object target, object old_room, string cmd)
+{
+  if (environment()!=target)
+    if (IS_ARCH(this_object()))
+    {
+      __set_environment(this_object(),target);
+      printf("%s: Bewegung hat nicht geklappt. Versuche es mit "
+             "set_environment... %s.\n.",query_verb(),
+             environment()==target?"hat geklappt":"erfolglos");
+    }
+  else
+    printf("%s: Bewegung hat nicht geklappt.\n",query_verb());
+  if (environment()!=target) return 1;
+  command(cmd);
+  if (old_room) move_object(old_room);
+  else
+    return printf("%s: Ursprungsraum wurde zerstoert.\n",query_verb()),1;  
+  if (environment()!=old_room)
+  {
+    if (IS_ARCH(this_object()))
+    {
+      __set_environment(this_object(),old_room);
+      printf("%s: Zurueckbewegen hat nicht geklappt. Versuche es mit "
+             "set_environment ... %s.\n",query_verb(),
+             environment()==old_room?"hat geklappt":"erfolglos");
+    }
+    else
+      printf("at: Zurueckbewegen hat nicht geklappt.\n");
+  }
+  return 1;
+}
+
+static int _in_room(string str)
+{
+  string room;int size;
+  object old_room;
+  string cmd,err;
+
+  if (!sizeof(str=_unparsed_args()) ||
+      !sizeof(str=regreplace(str,"^ *","",1)) ||
+      sscanf(str, "%s %s", room, cmd) != 2)
+    return USAGE("in <raum> <befehl>\n");
+  old_room = environment();
+  room=(string)call_other(master(),"_get_path",room,getuid());
+  if (err=catch(move_object(room)))
+  {
+    if (catch(size=file_size(room+".c"))||size<1)
+      printf("%s: %s.c: Datei nicht vorhanden.\n",query_verb(),room);
+    else
+      printf("%s: Bewegung nach %s hat nicht funktioniert: %s\n",
+             query_verb(),room,err);
+    return 1;
+  }
+  return _move_base(find_object(room),old_room,cmd);
+}
+
+static int _at_player(string dest)
+{
+  object ob,old_room;
+  mixed tmp;
+  string cmd;
+
+  if (!sizeof(dest=_unparsed_args()) ||
+      !sizeof(dest=regreplace(dest,"^ *","",1)) ||
+      sscanf(dest, "%s %s", dest, cmd) != 2)
+    return USAGE("at <lebewesen> <befehl>\n");
+  if (!(ob=find_living(dest)))
+  {
+    tmp=match_living(dest,1);
+    if (stringp(tmp)) ob = find_living(tmp);
+  }
+  if (!ob||!(ob=environment(ob)))
+    return _notify_fail(sprintf("at: Lebewesen \'%s\' nicht gefunden.\n",
+                                dest)),0;
+  old_room=environment();
+  move_object(ob);
+  return _move_base(ob,old_room,cmd);
+}
+
+//                           ########
+//############################ GOTO ############################
+//                           ########
+
+static object find_living_nr(string str)
+{ string name,check;
+  int nr;
+  object*livings;
+  if(sscanf(str,"%s %d%s",name,nr,check)<2||sizeof(check))
+    return find_living(str);
+  if(!sizeof(livings=filter((find_livings(name)||({})),#'environment))
+     ||nr<1||sizeof(livings)<nr)
+    return 0;
+  return livings[nr-1];
+}
+
+static int _goto(string dest){
+  mixed target;
+  string target2,err;
+
+  if (!sizeof(dest=_unparsed_args()))
+    return USAGE("goto [lebewesen|filename]\n");
+  if (!((target=find_living_nr(dest)) && (target=environment(target))))
+  {
+     target2=target=(mixed)call_other(master(),"_get_path",dest,getuid());
+     if (!find_object(target))
+     {
+       if (target2[<1]=='.') target2+="c";
+       if (target2[<2..<1]!=".c") target2+=".c";
+       notify_fail(sprintf("goto: Datei %O nicht vorhanden.\n",target));
+       if (!(file_size(target2)>-1||
+           file_size(implode(explode(target,"/")[0..<2],"/")+
+               "/virtual_compiler.c")>-1)||(err=catch(call_other(target,"?"))))
+       {
+         if (err)
+              notify_fail(sprintf("goto: Fehler beim Teleport nach %O:\n%s\n",
+                      dest,implode(explode(err,"\n")," ")));
+         target=match_living(dest,1);
+         if (!(stringp(target)&&(target=find_living(target))&&
+               (target=environment(target))))
+           return 0;
+       }
+     }
+  }
+  if (verfolger()) _verfolge("");
+  if (move(target,M_TPORT|M_NOCHECK)<0)
+    printf("Bewegung fehlgeschlagen!\n");
+  return 1;
+}
+
+//                           ########
+//############################ HOME ############################
+//                           ########
+
+static int _home()
+{
+  string dest;
+  if (verfolger()) _verfolge("");
+  dest="/players/" + getuid() + "/workroom";
+  if (file_size(dest+".c")<1||catch(call_other(dest,"???")))
+  {
+    printf("Fehler beim Laden Deines Workrooms.\n"
+           "Gehe zum Magiertreff.\n");
+    dest="/secure/merlin";
+  }
+  
+  if (move(dest,M_TPORT|M_NOCHECK)<0)
+    printf("Bewegung fehlgeschlagen!\n");
+  return 1;
+}
+
+//                        ###############
+//######################### +MAGIERNAME ##########################
+//                        ###############
+
+static int _go_wiz_home(string str)
+{
+  _notify_fail("Syntax: '+magiername'\n");
+  if(sizeof(query_verb())>1) str=query_verb()[1..];
+  if(!sizeof(str)) return 0;
+  if(query_verb()[0]!='+') return 0;
+  str=(old_explode(str," ")-({0}))[0];
+  if(!sizeof(str)) return 0;
+  str=lower_case(str);
+  if (str=="merlin")
+  {
+    move("/secure/merlin",M_TPORT|M_NOCHECK);
+    return 1;
+  }
+  if ((!call_other(master(),"get_userinfo",str))||
+      !IS_LEARNER(str))
+  {
+    printf("Es gibt keinen Magier namens %s.\n",
+           capitalize(str));
+    return 1;
+  }
+  if (file_size("/players/"+str+"/workroom.c")<1)
+  {
+    printf("%s hat keinen Workroom.\n",capitalize(str));
+    return 1;
+  }
+  if (catch(call_other("/players/"+str+"/workroom","???")))
+  {
+    printf("Der Workroom von %s hat Fehler.\n",capitalize(str));
+    return 1;
+  }
+  move("/players/"+str+"/workroom",M_TPORT|M_NOCHECK);
+  return 1;
+}
+
+
+static mixed _query_localcmds()
+{
+  return
+    ({({"goto","_goto",0,LEARNER_LVL}),
+      ({"in","_in_room",0,LEARNER_LVL}),
+      ({"at","_at_player",0,LEARNER_LVL}),
+      ({"home","_home",0,WIZARD_LVL}),
+      ({"+","_go_wiz_home",1,LEARNER_LVL})});
+}
diff --git a/std/shells/magier/objects.c b/std/shells/magier/objects.c
new file mode 100644
index 0000000..ed74f56
--- /dev/null
+++ b/std/shells/magier/objects.c
@@ -0,0 +1,168 @@
+// $Id: objects.c 8848 2014-06-11 22:05:04Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <wizlevels.h>
+#include <moving.h>
+#define NEED_PROTOTYPES
+#include <thing/language.h>
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <living/put_and_get.h>
+#include <player.h>
+#include <magier.h>
+
+//                        ###################
+//######################### INITIALISIERUNG #############################
+//                        ###################
+
+
+static mixed _query_localcmds()
+{
+  return ({({"clone","_clone",0,WIZARD_LVL}),
+           ({"setcmsg","_setcmsg",0,WIZARD_LVL}),
+           ({"setdmsg","_setdmsg",0,WIZARD_LVL}),
+           ({"destruct","_destruct",0,WIZARD_LVL}),
+          });
+}
+
+//                              #########
+//############################### CLONE ##############################
+//                              #########
+
+static int _clone(string cmdline)
+{
+  mixed *tmp;
+  int flags;
+  object ob;
+  string *args,*args2,err;
+  
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,CLONE_OPTS,1);
+  if (flags==-1||sizeof(args)!=1)
+    return USAGE("clone [-" CLONE_OPTS "] <objektname>");
+  if (flags&CLONE_F)
+    cmdline=args[0];
+  else
+  {
+    // etwas umstaendlich, aber so kann man auch Dateien clonen,
+    // wenn man keine Leserechte hat. Man muss aber im Verzeichnis
+    // lesen koennen
+    args2=explode(args[0],"/");
+    if (args2[<1][<1]=='.') args2[<1]+="c";
+    else if (args2[<1][<2..<1]!=".c") args2[<1]+=".c";
+    tmp=file_list(({implode(args2[0..<2],"/")+"/*"}),MODE_CLONE,0,"",
+                  args2[<1]);
+    if (!sizeof(tmp)||tmp[0][FILESIZE]<0)
+      return printf("clone: %s: Datei existiert nicht.\n",args[0]),1;
+    cmdline=tmp[0][FULLNAME];
+  }
+
+  if (err=catch(ob=clone_object(cmdline))||!ob)
+    return printf("clone: %s: Objekt konnte nicht erzeugt werden.\n"
+                  "Grund: %O",
+                  args[0],err||"unbekannt"),1;
+  if (!objectp(ob))
+    return printf("clone: %s: Objekt beim Erzeugen zerstoert.\n",
+                  args[0]),1;
+  if ((ob->move(this_object(),M_GET)>0) || 
+      (!objectp(ob)||ob->move(environment(),M_NOCHECK)>0)||!objectp(ob))
+  {
+    if (!objectp(ob))
+      return printf("clone: %s: Objekt beim Erzeugen zerstoert.\n",
+                  args[0]),1;
+    printf("Clone: %s erzeugt.\n",object_name(ob));
+    tell_room(environment(this_object()),
+              sprintf("%s %s.\n",Name(WER,1),QueryProp(P_CLONE_MSG)),
+                      ({ this_object()}));
+    return 1;
+  }
+  tell_room(environment(this_object()),
+            sprintf("%s malt wilde Zeichen in die Luft und "
+                    "murmelt vor sich hin, aber nichts "
+                    "passiert...\n",Name(WER,1)),
+            ({ this_object()}));
+  destruct(ob);
+  printf("Clone: %s: Objekt konnte nicht bewegt werden.",args[0]);
+  return 1;
+}
+
+
+//                            ############
+//############################# DESTRUCT ##############################
+//                            ############
+
+//
+// _destruct: Objekte zerstoeren
+//
+
+static int _destruct(string cmdline)
+{
+  int flags;
+  mixed *args;
+  object ob;
+
+  if (!sizeof(cmdline=_unparsed_args()))
+    return USAGE(query_verb()+" <objektname>");
+  args=find_obs(lower_case(cmdline),PUT_GET_NONE);
+  if (!args||!sizeof(args))
+  {
+    if (!(ob=find_object(cmdline)))
+    {
+      notify_fail(query_verb()+": Objekt \"" +cmdline+
+                  "\" nicht gefunden.\n");
+      return 0;
+    }
+  }
+  else
+    ob=args[0];
+  cmdline=capitalize(to_string(ob->name(WER)));
+  flags=(int)ob->QueryProp(P_PLURAL); // Missbrauch von flags :o)
+  if (query_verb()=="destruct")
+  {
+    if (!ob->remove())
+    {
+      notify_fail(cmdline+" will nicht zerstoert werden!\n");
+      return 0;
+    }
+  }
+  else destruct(ob);
+  if (!ob)
+  {
+    if (!QueryProp(P_INVIS))
+    {
+      tell_room(environment(this_object()),
+                sprintf("%s %s.\n",cmdline,QueryProp(P_DESTRUCT_MSG)),
+                ({ this_object() }));
+    }
+    printf("%s w%s von Dir zerstaeubt.\n",cmdline,(flags?"erden":"ird"));
+  }
+  else
+    printf("%s kann nicht zerstoert werden.\n",cmdline);
+  return 1;
+}
+
+//                        ####################
+//######################### SetCMsg, SetDMsg ############################
+//                        ####################
+
+static int _setcmsg(string str)
+{
+  printf("Beim Clonen von Objekten sehen die Anderen nun:\n"
+         "<Dein Name> %s.\n",
+         (SetProp(P_CLONE_MSG, _unparsed_args()||"zaubert etwas aus "
+          + QueryPossPronoun(MALE,WEM) + " Aermel hervor")));
+  return 1;
+}
+
+static int _setdmsg(string str)
+{
+  printf("Beim Zerstoeren von Objekten sehen die Anderen nun:\n"
+         "<Objekt> %s.\n",
+         SetProp(P_DESTRUCT_MSG, _unparsed_args()||"wird von " + name(WER,1)
+          + " zerstaeubt"));
+  return 1;
+}
diff --git a/std/shells/magier/parsing.c b/std/shells/magier/parsing.c
new file mode 100644
index 0000000..555332d
--- /dev/null
+++ b/std/shells/magier/parsing.c
@@ -0,0 +1,370 @@
+// $Id: parsing.c 9142 2015-02-04 22:17:29Z Zesstra $
+#pragma strict_types
+#pragma save_types
+//#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <files.h>
+#include <wizlevels.h>
+#include <logging.h>
+#include <regexp.h>
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <thing/properties.h>
+#include <player.h>
+
+//
+// glob2regexp: Argument von glob in Regexp umwandeln
+// str: Argument (als Referenz)
+//
+
+static string glob2regexp(string str)
+{
+  str=regreplace(str,"([\\.\\^\\$\\[\\]\\(\\)])","\\\\\\1",RE_TRADITIONAL|1);
+  str=regreplace(str,"\\*",".*",RE_TRADITIONAL|1);
+  return sprintf("^%s$",regreplace(str,"?",".",RE_TRADITIONAL|1));
+}
+
+//
+// to_filename: Argument in Dateinamen umwandeln
+// str:  Argument
+// Rueckgabe: Dateiname
+//
+
+static mixed to_filename(string str)
+{
+  string *tmp,p,newfn;
+  int i;
+// Testen ob .. in einem Filenamenabschnitt, falls Version <3.2.5
+  tmp=explode(str,"/");
+// Testen auf Pfadvariable
+  if (sizeof(tmp[0]) && tmp[0][0]=='$' 
+      && m_contains(&p,QueryProp(P_VARIABLES),tmp[0][1..]))
+    tmp[0]=p;
+// Pfad absolut machen (Hat danach noch Wildcards drinnen) oder auch nicht
+  return master()->make_path_absolute(implode(tmp,"/"));
+}
+
+
+//
+// _parseargs(): Kommandozeilenabschnitt parsen
+// str:         Kommandozeilenabschnitt
+// line:        Array von geparsten Kommandozeilenabschnitten (Referenz)
+// flags:       Als Referenz uebergebener Flag-Wert
+// opts:        Erlaubte Flags
+// build_fn:    Filenamen aendern
+//
+
+private void _parseargs(string str, string *line,int flags,string opts,
+                     int build_fn )
+{
+
+// Strings in "" erhalten
+  if(str[0] == "\""[0])  
+  { 
+    line += ({ str[1..<2] });
+    return; 
+  }  
+//  Flags parsen
+  if(str[0] == '-')  
+  { 
+    int i,j;
+    i=sizeof(str);
+    while(i--)
+      if (str[i]!='-')
+      {
+        if((j = member(opts, str[i])) != -1)
+          flags |= (1 << j);
+        else
+        {
+          flags=-1;
+          printf("Das Flag '-%c' wird von dem Befehl '%s' nicht "
+                 "unterstuetzt.\n",str[i],query_verb()||"");
+        }
+      }
+    return;
+  }
+  if (build_fn)
+  {
+    if (str=(string)to_filename(str)) line+=({ str });
+  } 
+  else
+    line+= ({ str });
+}
+
+
+//
+// parseargs() - zerlegt Kommandozeile in ein Array:
+// cmdline:     Kommandozeile
+// flags:       Als Referenz uebergebener leerer Integerwert. Enthaelt danach
+//              die Flags
+// opts:        String mit erlaubten Optionen
+// build_fn:    Filenamen umbauen?
+// Rueckgabe: Array der Kommandozeilenargumente (ausser Flags)
+//
+
+static string *parseargs(string cmdline,int flags, string opts,int build_fn)
+{
+  int i;
+  string *line;
+  line=({});
+  if (!sizeof(cmdline)) return ({});
+  map(regexplode(cmdline,"[\"][^\"]*[\"]| ", RE_TRADITIONAL)-({" ", ""}),
+            #'_parseargs, &line, &flags,opts, build_fn);
+  return line - ({""});
+}
+
+//
+// _vc_map: VC-Objektnamen zu Arrays umwandeln ({ name, -1, program_time() })
+//
+
+private int *_vc_map(object ob,mixed *list)
+{
+  list+=({ explode(object_name(ob),"/")[<1],-1,program_time(ob) });
+  return 0;
+}
+
+
+//
+// _get_files: Filedaten beim Rekursiven kopieren erstellen
+// dirname:    Bearbeitetes Verzeichnis
+// mask:       Maske, der Dateinamen genuegen muessen
+// mode:       Welche Daten werden benoetigt? (siehe magier.h)
+// dest:       In welches Verzeichnis soll kopiert werden?
+// Rueckgabe:  Alist mit den Dateiinformationen
+//
+
+private varargs mixed *_get_files(string dirname,string mask,int mode,string dest)
+{
+  mixed *files,*tmp,*data;
+  string fullname,base;
+
+  //DEBUG("_GF: DIRNAME " + dirname);
+  data=get_dir(dirname+"*",7);
+  if(!sizeof(data)) return ({});
+  files=({});
+ 
+  while(sizeof(data))
+  {
+    tmp=({});
+    base=data[BASENAME];
+    fullname=dirname+base;
+    if (base!="."&&base!=".."&&(!(mode==MODE_GREP&&base=="RCS"))&&
+        ((data[FILESIZE]==-2&&
+       sizeof(tmp=_get_files(fullname+"/",mask,mode,
+      dest+base+"/"))&&mode!=MODE_RM)||!mask||sizeof(regexp(({ base }),mask, RE_TRADITIONAL))))
+    {
+      //DEBUG("_GF: ADDING FILE " + fullname);
+      files+= ({ data[0..2]+({ fullname,dirname,dest+base,
+                               sizeof(tmp) }) });
+    }
+    if (sizeof(files)+sizeof(tmp)>MAX_ARRAY_SIZE)
+       raise_error("Zu viele Files (>3000)!! Abgebrochen!\n");
+    files+=tmp;
+    data=data[3..];
+  }
+  
+  if(sizeof(files)>300&&!IS_ARCH(this_object()))
+    // Tod allen Laggern :o)
+    raise_error("Zu viele Files (>300)!! Abgebrochen!\n");
+  return files;
+}
+ 
+
+//
+// _get_matching: Rekursive Funktion zum ermitteln der Files, wenn mit Maske
+//                gearbeitet wird (z.B. cp -m /bla/*/* /ziel *.c)
+// pathmask: Array, das die einzelnen Elemente der Maske (Arg1) enthaelt
+//                   (per efun:old_explode(arg1,"/"))
+// depth:     Aktuelles Element von pathmask
+// path:      implode(pathmask[0..depth],"/");
+// mode:      Welche Daten werden benoetigt? (siehe magier.h)
+// flags:     Welche Flags wurden gesetzt?
+// dest:      Zielverzeichnis (beim kopieren/moven)
+// filemask:  Maske, der die Files genuegen muessen
+// Rueckgabe: Alist mit den Dateiinformationen
+//
+
+private mixed *_get_matching(string *pathmask, int depth, string path, 
+                    int mode, int recursive, string dest,string filemask)
+{
+  mixed *data,*tmp,*files;
+  string base,full;
+
+  //DEBUG("_GM: PM: " + pathmask[depth]);
+  //DEBUG("_GM: FM: " + filemask);
+  data=get_dir(path+pathmask[depth++],GETDIR_NAMES|GETDIR_SIZES|GETDIR_DATES)||({});
+  if (!sizeof(data)) return ({});
+  files=({});
+  while(sizeof(data))
+  {
+    if ((base=data[BASENAME])=="."||base=="..")
+    {
+      data=data[3..];
+      continue;
+    }
+    full=path+base;
+    //DEBUG("_GM: FULL: " + full);
+    if ((data[FILESIZE]==-2)&&(sizeof(pathmask)>depth)&&
+        (!(mode==MODE_GREP&&base=="RCS")))
+    {
+      //DEBUG("DESCEND INTO " + full);
+      tmp=_get_matching(pathmask,depth,full+"/",mode,recursive,
+                        (recursive?dest+base+"/":dest),filemask);
+    }
+    else tmp=({});
+    //DEBUG("DEPTH: " + depth + " : " + sizeof(pathmask));
+    if((!filemask&&(depth==sizeof(pathmask)))||
+        (filemask&&(depth+2>sizeof(pathmask))&&
+        sizeof(regexp(({ base }),filemask,RE_TRADITIONAL)))||
+       ((mode==MODE_CP||mode==MODE_MV||(filemask&&
+        (mode==MODE_RM)&&sizeof(regexp(({ base}),filemask,RE_TRADITIONAL))))&&
+        sizeof(tmp)))
+    {
+      //DEBUG("ADDING: " + base+ " : "+ full );
+      files+=({ data[0..2]+({ full, path, dest+base,sizeof(tmp)}) });
+    }
+    if (sizeof(files)+sizeof(tmp)>MAX_ARRAY_SIZE)
+       raise_error("Zu viele Files (>3000)!! Abgebrochen!\n");
+    files+=tmp;
+    data=data[3..];
+  }
+  if(sizeof(files)>300&&!IS_ARCH(this_object()))
+    // Tod allen Laggern!
+    raise_error("Zu viele Files (>300)!! Abgebrochen!\n");
+  return files;
+}
+
+
+//
+// get_files: Basisroutine zum Ermitteln der zu bearbeitenden Dateien
+// filename:  Pfadmaske, Verzeichnisname, Dateiname, der bearbeitet werden
+//            soll
+// mode:      Welche Daten werden benoetigt? (siehe magier.h)
+// recursive: Auch Unterverzeichnisse bearbeiten?
+// dest:      Wenn kopiert werden soll: wohin?
+// filemask:  Maske, der die Dateinamen genuegen muessen
+// Rueckgabe: Alist mit den Dateiinformationen
+//
+
+static varargs mixed *get_files(string filename, int mode, int recursive, string dest, string filemask)
+{ 
+  string full,path,base,*patharray,*vrooms,dest2;
+  object vcompiler;
+  mixed *files,*data;
+
+  // DEBUG("GF: " + filename);
+  // DEBUG("REC: " + recursive + " MODE: " + mode);
+  // if (dest[<1..<1]!="/") DEBUG("DEST: " + dest);
+  if (filename=="/")
+    {
+      switch (mode)
+      {
+        case MODE_LSA: return ({({ "", -2, 0,"","","",0 })});
+        default: if (!recursive) return ({});
+                 break;
+      }
+    }
+  patharray=explode(filename,"/");
+  if(!sizeof(data=get_dir(filename,7)||({}))&&
+     (mode==MODE_UPD||mode==MODE_MORE||mode==MODE_ED))
+    data=get_dir(filename+".c",7)||({});
+  if ((mode==MODE_LSA||mode==MODE_LSB)&&
+      (vcompiler = find_object(implode(patharray[0..<2],"/")+"/virtual_compiler")) &&
+      pointerp(vrooms=(mixed *)vcompiler->QueryObjects()))
+    map(vrooms,#'_vc_map,&data);
+  files=({});
+  if (sizeof(data)) // passende files
+  {
+    mixed *subfiles;
+    subfiles=({});
+    path=implode(patharray[0..<2],"/")+"/";
+    while (sizeof(data))
+    {
+      subfiles=({});
+      base=data[BASENAME];
+      if (mode==MODE_LSB||(base!="."&&base!=".."))
+      {
+        //DEBUG("PATH: " + path+" BASE: " + base + " MODE: " + mode);
+        full=path+base;
+        dest2=((dest=="/"||file_size(dest[0..<2])==-2)?
+               (dest+base):(dest=="/"?"/":dest[0..<2]));
+        //DEBUG("DEST: " + dest);
+        if (recursive&&data[FILESIZE]==-2) // Verzeichnis, Rekursiv
+          subfiles=_get_files(full+"/",filemask,mode,dest2+"/");
+        if (!(filemask&&!sizeof(subfiles)&&!sizeof(regexp(({ base }),filemask,RE_TRADITIONAL))))
+        {
+          if (!filemask||mode!=MODE_RM)
+            files+=({ data[0..2]+({ full, path, dest2,sizeof(subfiles)}) });
+          if (sizeof(files)+sizeof(subfiles)>MAX_ARRAY_SIZE)
+            raise_error("Zu viele Files (>3000)!! Abgebrochen!\n");
+          files+=subfiles;
+        }
+      }
+      data=data[3..];
+    }
+    return files;
+  }
+// File existiert nicht -> Wildcard oder tatsaechlich nicht existent
+// Behandeln je nach mode
+  switch(mode)
+  {
+    case MODE_CP:
+    case MODE_MV:
+    case MODE_CD:
+    case MODE_LSA:
+      files=_get_matching(patharray+(filemask?({ "*" }):({})),1,"/",mode,
+                           recursive,dest,filemask);
+      break;
+    default: break; 
+  }
+  return files;
+}
+
+
+//
+// file_list(): Liste der Fileinformationen
+// files:     Array der Filenamen MIT Wildcards
+// mode:      Welche Daten werden benoetigt
+// recursive: Rekursiv listen?
+// dest:      Zielverzeichnis
+// mask:      Maske (regexp, wenn definiert)
+// Rueckgabe: Liste der betroffenen Files
+//
+
+static varargs mixed *file_list(string *files, int mode, int recursive, string dest, string mask)
+{
+  string *list,err,*result;
+  int i,j;
+  list=({});
+  if (mask) mask=glob2regexp(mask);
+  j=sizeof(files);
+  for(i=0;i<j;i++)
+  {
+    // Abschliessenden / von Pfadnamen abschneiden, weil in diesem Fall 
+    // die Inhalte der ersten Unterverzeichnisebene mit ausgegeben
+    // wurden. Ursache hierfuer ist ein Fix an full_path_array() im 
+    // Masterobjekt, der dazu fuehrte, dass get_dir() den abschliessenden /
+    // des uebergebenen Pfades jetzt korrekt behandelt. \o/
+    if ( sizeof(files[i]) > 1 && files[i][<1] == '/' )
+    {
+      files[i] = files[i][0..<2];
+      if ( !sizeof(files[i]) )
+        continue;
+    }
+    if (err=catch(list+=get_files(files[i],mode,recursive,dest,mask)))
+    {
+      printf("Fehler aufgetreten: %s\n",err);
+      log_file(SHELLLOG("FILE_LIST"),
+               sprintf("%s fuehrte folgendes Kommando aus: (Zeit: %s)\n"
+                       "  >>%s %s<<\n"
+                       "  Folgender Fehler trat dabei auf:\n"
+                       "  %s\n\n",
+                       capitalize(getuid())||"<Unbekannt>",dtime(time()),
+                       query_verb()||"*",_unparsed_args()||"*",err||"*"));
+      return ({});
+    }
+  }
+   return list;
+}
diff --git a/std/shells/magier/players.c b/std/shells/magier/players.c
new file mode 100644
index 0000000..c563d9d
--- /dev/null
+++ b/std/shells/magier/players.c
@@ -0,0 +1,529 @@
+// MorgenGrauen MUDlib
+//
+// players.c
+//
+// $Id: players.c 9551 2016-04-20 22:54:58Z Arathorn $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <wizlevels.h>
+#include <ansi.h>
+#include <logging.h>
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <living/comm.h>
+#include <player.h>
+#undef NEED_PROTOTYPES
+#include <properties.h>
+#include <moving.h>
+
+static mixed _query_localcmds()
+{
+  return ({({"zap","_zap",0,WIZARD_LVL}),
+           ({"verfolge","_verfolge",0,LEARNER_LVL}),
+           ({"trans","_trans",0,LEARNER_LVL}),
+           ({"peace","_frieden",0,LEARNER_LVL}),
+           ({"frieden","_frieden",0,LEARNER_LVL}),
+           ({"pwho","_pwho",0,WIZARD_LVL}),
+           ({"zwinge","_zwinge",0,WIZARD_LVL}),
+           ({"heal","_heile",0,WIZARD_LVL}),
+           ({"heil","_heile",1,WIZARD_LVL}),
+           ({"people","_people",0,LEARNER_LVL}),
+           ({"spieler","_spieler",0,WIZARD_LVL})});
+}
+
+//                               #######
+//################################ ZAP ##################################
+//                               #######
+
+private string _zap_message(string str, object obj)
+{
+  str=regreplace(str,"@@wer@@",(string)obj->name(WER,2),1);
+  str=regreplace(str,"@@wessen@@",(string)obj->name(WESSEN,2),1);
+  str=regreplace(str,"@@wem@@",(string)obj->name(WEM,2),1);
+  str=regreplace(str,"@@wen@@",(string)obj->name(WEN,2),1);
+  str=regreplace(str,"@@ich@@",name(WER,2),1);
+  return capitalize(str);
+}
+
+static int _zap(string str)
+{
+    object opfer;
+    string *message, dummy;
+    int spieler;
+
+    if (!str) return USAGE("zap <name>");
+    if (sscanf( str, "spieler %s", dummy ))
+    {
+      str = dummy;
+      spieler = 1;
+    }
+    if (opfer=present(str,environment()))
+    {
+      if ( !living(opfer) )
+      {
+        printf("%s ist doch gar kein Lebewesen!\n",capitalize(str) );
+        return 1;
+      }
+      if (query_once_interactive(opfer)&&!spieler )
+      {
+        printf( "Spieler kannst Du nur mit der Syntax 'zap spieler <name>' "
+                "toeten!\n" );
+        return 1;
+      }
+      else
+        if ( !query_once_interactive(opfer) && spieler )
+        {
+          printf( "Aber %s ist doch gar kein Spieler!\n",capitalize(str));
+          return 1;
+        }
+
+      message = QueryProp(P_ZAP_MSG);
+
+      if ( !pointerp(message) || sizeof(message) != 3 ){
+          tell_room(environment(),sprintf("%s beschwoert einen Blitz "
+                "vom Himmel.\n",capitalize(getuid())),({ this_object() }));
+          printf("Du toetest %s.\n",opfer->name( WEN,2));
+      }
+      else
+      {
+        printf(_zap_message(message[0],opfer));
+        tell_room(environment(),_zap_message(message[1],opfer),
+             ({this_player(),opfer}));
+        tell_object(opfer,_zap_message(message[2],opfer));
+      }
+
+      opfer->die();
+      return 1;
+  }
+  else{
+      printf("Sowas siehst Du hier nicht.\n");
+      return 1;
+  }
+}
+
+
+//                             ############
+//############################## VERFOLGE ################################
+//                             ############
+
+static int _verfolge(string str)
+{
+  // Wenn nichts eingegeben wurde, wird ver Verfolgungsmodus beendet, sofern
+  // er zuvor eingeschaltet war. Ansonsten wird eine Fehlermeldung 
+  // ausgegeben.
+  if (!sizeof(str))
+  {
+    mixed *pur = Query(P_PURSUERS);
+    if ( pointerp(pur) && sizeof(pur) && objectp(pur[0]) )
+    {
+      pur[0]->RemovePursuer(this_object());
+      ReceiveMsg("Verfolgungsmodus abgeschaltet.", MT_NOTIFICATION);
+    }
+    else
+    {
+      ReceiveMsg("Du verfolgst doch ueberhaupt niemanden.", MT_NOTIFICATION);
+    }
+    return 1;
+  }
+  str=lower_case(str);
+  
+  // match_living() erlaubt die Pruefung, ob die Angabe eindeutig war.
+  int|string lv = match_living(str);
+  if ( intp(lv) )
+  {
+    if ( lv == -2 )
+      ReceiveMsg("Kein solches Wesen gefunden.", MT_NOTIFICATION);
+    else
+      ReceiveMsg(sprintf("verfolge: '%s' ist nicht eindeutig.\n", str),
+        MT_NOTIFICATION);
+    return 1;
+  }
+  
+  // Spieler zuerst auswaehlen, danach im Raum anwesende Lebewesen.
+  object ziel = find_player(lv) || present(lv, environment(this_player()));
+  
+  // Wurde kein Lebewesen gefunden, wird das erste Element aus der Liste der
+  // Lebewesen dieses Namens gewaehlt, falls vorhanden. Nur Lebewesen mit
+  // Environment kommen in Frage, denn sonst gibt es keinen Raum, in den der
+  // neue Verfolger bewegt werden koennte.
+  if ( !objectp(ziel) ) {
+    object* eligible_livings = filter(find_livings(lv), #'environment);
+    if (sizeof(eligible_livings))
+      ziel = eligible_livings[0];
+  }
+  
+  // Endlich etwas gefunden? Dann Verfolger eintragen und zum Ziel bewegen.
+  if ( objectp(ziel) )
+  {
+    if ( ziel == this_player() ) 
+    {
+      ReceiveMsg("Du kannst Dich nicht selbst verfolgen.", MT_NOTIFICATION);
+    }
+    else
+    {
+      ReceiveMsg(sprintf(
+        "Du verfolgst jetzt %s. [%s]", ziel->name(WEN), object_name(ziel)),
+        MT_NOTIFICATION, MA_MOVE);
+      ziel->AddPursuer(this_object());
+      ziel->TakeFollowers();
+    }
+  }
+  else
+  {
+    ReceiveMsg("Kein Wesen mit dem Namen '"+lv+"' gefunden, oder nur "
+      "solche ohne Environment.", MT_NOTIFICATION, 0, "verfolge: ");
+  }
+  return 1;
+}
+
+
+//                              #########
+//############################### TRANS #################################
+//                              #########
+
+static int _trans(string str)
+{
+  object living;
+
+  if (!sizeof(str))
+    return _notify_fail("Syntax: trans <spielername>\n"),0;
+  str=match_living(str,0);
+  if (intp(str))
+    switch (str)
+    {
+      case -1: write("Das war nicht eindeutig.\n"); return 1;
+      case -2: write("So ein Wesen gibt es nicht.\n"); return 1;
+    }
+  if(living=find_living(str))
+  {
+    if (living->move(object_name(environment()),
+                     M_TPORT|M_NOCHECK)<=0)
+    {
+      printf("Teleportieren von %s fehlgeschlagen.\n",living->Name(WEM));
+      if (IS_LEARNER(living))
+        tell_object(living,sprintf("%s wollte Dich teleportieren, "
+             "hat aber versagt!\n",capitalize(getuid())));
+      return 1;
+    }
+    tell_object(living,sprintf(
+    "Ein seltsames Gefuehl ueberkommt Dich ...\n"
+    "Du verlierst die Orientierung ...\n"
+    +(QueryProp(P_INVIS)?"":"%s holt Dich zu sich.\n"),
+    capitalize(getuid())));
+    printf("%s wurde herbeizitiert.\n",living->Name(WER));
+    return 1;
+  }
+  printf("Das Lebewesen '%s' konnte nicht gefunden werden.\n",
+         capitalize(str));
+  return 1;
+}
+
+//                             ###########
+//############################## FRIEDEN #################################
+//                             ###########
+
+static int _frieden(string sname)
+{
+  object *enem,obj;
+  int i;
+  string him;
+  
+  if (!sname)
+  {
+    enem=all_inventory(environment());
+    map_objects(enem,"StopHuntingMode");
+    tell_room(environment(),sprintf("%s stiftet Frieden.\n",capitalize(getuid())),
+              ({ this_object()}));
+    printf("Du stiftest Frieden.\n");
+    return 1;
+  }
+  else
+  {
+    if (!obj=find_living(sname))
+      return printf("Kein solches Lebewesen im Spiel.\n"),1;
+    him=(string)obj->name(WEM);
+    i=sizeof(enem=(object *)(((mixed *)obj->StopHuntingMode())[0]));
+    // Mistdriver ... object** waere richtig gewesen ... *seufz*
+    while(i--)
+    {
+      enem[i]->StopHuntFor(obj);
+      tell_object(obj,sprintf("%s beendet Deinen Kampf mit %s.\n",
+                              capitalize(getuid()),enem[i]->Name(WEM)));
+      tell_object(enem[0],sprintf("%s beendet Deinen Kampf mit %s.\n",
+                                  capitalize(getuid()),him));
+    }
+  }
+  printf("%s und alle Gegner wurden befriedet.\n",obj->Name(WER));
+  return 1;
+}
+
+//                              ########
+//############################### PWHO ##################################
+//                              ########
+
+#if __VERSION__ < "3.2.9"
+private int _pwho_learner_test(object ob)
+{
+  return !IS_LEARNER(ob);
+}
+#endif
+
+static int _pwho()
+{
+  mixed* spieler, res, *hands;
+  int i;
+#if __VERSION__ < "3.2.9"
+  spieler = filter(users(),#'_pwho_learner_test);
+#else
+  spieler = filter(users(),(: return !IS_LEARNER($1); :));
+#endif
+  spieler = sort_array(spieler, function int (object a, object b)
+      { return a->QueryProp(P_LEVEL) > b->QueryProp(P_LEVEL); } );
+  
+  res = "Lvl Name         Erfahrung   QP  Int Str Dex Con WC   "
+    "AC   HANDS HP  (max)\n"
+    "--------------------------------------------------------------"
+    "-----------------\n";
+  for (i=sizeof(spieler)-1; i>=0; i--)
+    res += sprintf("%3d %-12s %9d %5d %3d %3d %3d %3d %4d %4d  %5d "
+                   "%4d (%4d)\n",
+     spieler[i]->QueryProp(P_LEVEL),
+     capitalize(getuid(spieler[i])),
+     spieler[i]->QueryProp(P_XP),
+     spieler[i]->QueryProp(P_QP),
+     spieler[i]->QueryAttribute(A_INT),
+     spieler[i]->QueryAttribute(A_STR),
+     spieler[i]->QueryAttribute(A_DEX),
+     spieler[i]->QueryAttribute(A_CON),
+     spieler[i]->QueryProp(P_TOTAL_WC),
+     spieler[i]->QueryProp(P_TOTAL_AC),
+     (sizeof(hands=((int *)spieler[i]->QueryProp(P_HANDS)))?hands[1]:0),
+     spieler[i]->QueryProp(P_HP),
+     spieler[i]->QueryProp(P_MAX_HP));
+  More(res);
+  return 1;
+}
+
+//                             ##########
+//############################## ZWINGE #################################
+//                             ##########
+
+static int _zwinge(string str)
+{
+  object living;
+  string what, rest;
+  string living_name;
+
+  str = _unparsed_args();
+  if(!str|| sscanf( str, "%s %s", living_name, what ) != 2 )
+    return _notify_fail("Zwinge WEN zu WAS?\n"),0;
+  if( sscanf( what, "zu %s", rest ) == 1 ) what = rest;
+  if (!(living = find_living(living_name)))
+    return printf ("Ein Lebewesen namens '%s' konnte nicht gefunden werden!\n",
+                   capitalize(living_name)),1;
+  if (living->command_me(what))
+  {
+    printf("Du zwingst %s zu \"%s\".\n",capitalize(living_name),what);
+    if (!IS_ARCH(this_object())&&getuid()!=(string)living->QueryProp(P_TESTPLAYER))
+      log_file(SHELLLOG("ZWINGE"),
+               sprintf("%s zwingt %s (%s) zu %s [%s]\n",
+                       capitalize(getuid()),living->Name(),capitalize(getuid(living)),
+                       what,dtime(time())));
+  }
+  else
+    write("Hat leider nicht geklappt!\n");
+  return 1;
+}
+
+//                              #########
+//############################### HEILE #################################
+//                              #########
+
+static int _heile(string name)
+{
+  object ob;
+  int lpv, mpv;
+
+  if (!name) return USAGE("heile <name>");
+  name = lower_case(name);
+  if ((!(ob = present(name,environment())))
+      ||!living(ob))
+    ob = find_living(name);
+  if (!ob)
+  {
+    printf("'%s' ist momentan nicht da.\n",capitalize(name));
+    return 1;
+  }
+  lpv = (int)ob->QueryProp(P_HP);
+  mpv = (int)ob->QueryProp(P_SP);
+  ob->heal_self(1000000);
+  if (!IS_LEARNER(ob) && (!ob->QueryProp(P_TESTPLAYER)||
+          (((string)ob->QueryProp(P_TESTPLAYER))[<5..<1]=="Gilde")))
+    log_file(SHELLLOG("HEAL"),
+       sprintf("%s heilt %s (%s) %s (LP: %d -> %d, MP: %d -> %d)\n",
+         capitalize(geteuid(this_player())),
+         call_other(ob,"name"), capitalize(geteuid(ob)),
+         dtime(time()), lpv, (int)ob->QueryProp(P_HP),
+               mpv,(int)ob->QueryProp(P_SP)));
+  tell_object(ob, QueryProp(P_NAME) + " heilt Dich.\n");
+  printf("Du heilst %s.\n",capitalize(name));
+  return 1;
+}
+
+//                             ##########
+//############################## PEOPLE #################################
+//                             ##########
+
+private string _people_filename(object obj)
+{
+  string str;
+  str=object_name(environment(obj));
+  if (!str) return 0;
+  if (str[0..2] == "/d/") return sprintf("+%s",str[3..<1]);
+  if (str[0..8] == "/players/") return sprintf("~%s",str[9..<1]);
+  return str;
+}
+
+static int _people()
+{
+  mixed *list, res;
+  int i,j, a;
+  string a_age,a_ipnum,a_name,a_level,a_idle,a_room,a_end, a_title;
+
+  switch(QueryProp("tty"))
+  {
+    case "vt100":
+      a_ipnum = ""; a_name = ANSI_BOLD;
+      a_level = ANSI_NORMAL; a_idle = ANSI_BLINK;
+      a_room = ANSI_NORMAL; a_end = ANSI_NORMAL;
+      a_title = ANSI_INVERS; a_age = ANSI_NORMAL;
+      break;
+    case "ansi":
+      a_ipnum = ANSI_BLUE; a_name = ANSI_BOLD;
+      a_level = ANSI_RED; a_idle = ANSI_BLACK+ANSI_BOLD;
+      a_room = ANSI_BOLD+ANSI_BLUE; a_end = ANSI_NORMAL;
+      a_title = ANSI_INVERS; a_age = ANSI_PURPLE;
+      break;
+    default:
+      a_title = a_ipnum = a_name = a_level = a_idle = a_room = a_end = "";
+      a_age = "";
+  }
+  list = sort_array(users(), function int (object a, object b) {
+      return query_ip_number(a)>query_ip_number(b);} ); 
+
+  j=sizeof(list);
+  a=0;res="";
+  for(i=0; i<sizeof(list); i++) {
+    string name_; 
+    name_ = capitalize(list[i]->query_real_name()||"<logon>");
+    res += sprintf( "%s%-15s%s %s%-13s%s %s%3d%s %s %s %s%s%s%s %s%s\n",
+                    a_ipnum, query_ip_number(list[i]),a_end,a_name,
+                    (list[i]->QueryProp(P_INVIS)?"("+name_+")":name_),
+                    a_end,a_level, MASTER->get_wiz_level(getuid(list[i])),
+                    a_end,a_age,
+                    time2string("%4x %0X",((int)list[i]->QueryProp(P_AGE))*2),
+                    query_idle(list[i])>=300?(a++,(a_idle+"I")):" ",
+                    a_end,
+                    query_editing(list[i])?a_idle+"E"+a_end:" ",
+                    query_input_pending(list[i])?a_idle+"M"+a_end:" ",
+                    environment(list[i])?a_room+_people_filename(list[i]):"",
+                    a_end);
+  }
+  if (a)
+    res = sprintf("%s%d Spieler anwesend (%d aktiv). %s%s\n",a_title,j,
+                  (j-a),query_load_average(),a_end)+res;
+  else
+    res = sprintf("%s%d Spieler anwesend. %s%s\n",a_title,j,
+                  query_load_average(),a_end)+res;
+  More(res);
+
+  return 1;
+}
+
+
+//                             ###########
+//############################## SPIELER #################################
+//                             ###########
+
+private string _spieler_time2string(int time)
+{
+  string ret;
+
+  ret="";
+  if (time>=86400)
+  {
+    ret+=time/86400+"d ";
+    time%=86400;
+  }
+  if(time<36000) ret+="0";
+  ret+=time/3600+":";
+  time%=3600;
+  if(time<600) ret+="0";
+  ret+=time/60+":";
+  time%=60;
+  if(time<10) ret+="0";
+  ret+=time+"";
+  return ret;
+}
+
+
+static int _spieler(string arg)
+{
+  string dummy,ip;
+  object *spieler,pl;
+  int i;
+  
+  arg=_unparsed_args();
+  if(!sizeof(arg) || sscanf(arg,"aus ip %s",dummy)!=1)
+    return USAGE("spieler aus ip [von <spieler>|<ip>]");
+  arg=dummy;
+  if (sscanf(arg,"von %s",dummy)==1)
+  {
+    dummy=lower_case(dummy);
+    if (!(pl=find_player(dummy)))
+      return notify_fail(sprintf("Spieler '%s' konnte nicht gefunden "
+                                 "werden.\n",capitalize(dummy))),0;
+    ip=query_ip_number(pl);
+  }
+  else ip=arg;
+  ip=implode((explode(ip,".")-({""})+({"*","*","*","*"}))[0..3],".");
+if (catch(
+  spieler=filter(users(),
+    (: return sizeof(regexp(({query_ip_number($1)}),$2)); :),"^"+glob2regexp(ip)+"$")
+
+                                      ))
+  return printf("In der IP duerfen nur Zahlen(0-255), Punkte (.) und "
+                "Sterne (*) vorkommen.\n"),1;
+  if (!sizeof(spieler))
+    return printf("Es konnte kein Spieler mit der IP '%s' gefunden "
+                  "werden.\n",ip),1;
+  arg=sprintf("\nFolgende Spieler haben die IP %s:\n"
+       "================================================================"
+              "===========\n"
+       "Name:       Zweitie von:      Eingeloggt:                  "
+              "Idle seit:\n"
+       "----------------------------------------------------------------"
+              "-----------\n",ip);
+  i=sizeof(spieler);
+  while(i--)
+  {
+    arg+=sprintf("%-11s %-17s %26s  %-15s\n",
+                 capitalize(getuid(spieler[i])),
+                 ((dummy=(string)spieler[i]->QueryProp(P_SECOND))?
+                  (sizeof((mixed *)call_other(master(),
+                                              "get_userinfo",dummy))?
+                   capitalize(dummy):"*ungueltig*"):""),
+                 dtime(spieler[i]->QueryProp(P_LAST_LOGIN)),
+                 _spieler_time2string(query_idle(spieler[i])));
+  }
+  arg+="==============================================================="
+    "============\n\n";
+  More(arg);
+  return 1;
+}
diff --git a/std/shells/magier/todo.c b/std/shells/magier/todo.c
new file mode 100644
index 0000000..f4120e7
--- /dev/null
+++ b/std/shells/magier/todo.c
@@ -0,0 +1,222 @@
+// $Id: todo.c 9142 2015-02-04 22:17:29Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <wizlevels.h>
+#include <defines.h>
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <player.h>
+
+#define SAVEFILENAME sprintf("/players/%s/.todoliste",getuid())
+
+private nosave status todo_initialized;
+private nosave mixed *todo_data;
+mixed *todo_data_public;
+static mixed _query_localcmds()
+{
+  return ({
+    ({"todo","_todo",0,WIZARD_LVL})});
+}
+
+private void todo_save()
+{
+
+  int i,j;
+  string a,b;
+  a=SAVEFILENAME+".o";
+  rm(a);
+  if (j=sizeof(todo_data))
+  {
+#if !__EFUN_DEFINED__(save_value)   
+    write_file(a,"#0:0\ntodo_data_public ({");
+    for (i=0;i<j;i++)
+    {
+      b=regreplace(todo_data[i][1],"\"","\\\"",1);
+      b=regreplace(b,"\n","\\n",1);
+      write_file(a,sprintf("({%d,\"%s\",}),",
+                           todo_data[i][0],b));
+    }
+    write_file(a,"})\n");
+#else
+    todo_data_public=todo_data;
+    write_file(a,save_value(todo_data_public));
+    todo_data_public=0;
+#endif
+  }
+  return;
+
+}
+
+static void initialize()
+{
+  if(!IS_WIZARD(this_object())) return;
+  if (!restore_object(SAVEFILENAME)) todo_data=({});
+  else
+    {
+      todo_data=todo_data_public;
+      todo_data_public=0;
+    }
+  return;
+}
+
+static void _todo_neu2(string input,string carry)
+{
+  if (input=="q"||(input=="."&&!sizeof(carry)))
+  {
+    printf("Abbruch!");
+    return;
+  }
+  if (input!=".")
+  {
+    printf("]");
+    input_to("_todo_neu2",0,carry+input+"\n");
+    return;
+  }
+  todo_data+=({ ({ time(), carry }) });
+  todo_save();
+  printf("Eintrag angelegt. Laufende Nummer ist %d.\n",sizeof(todo_data));
+  return;
+}
+
+private int _todo_neu(string cmdline)
+{
+  if (cmdline!="neu") return 0;
+  printf("Was musst Du noch machen? (Abbruch mit 'q', "
+         "Ende mit '.')\n]");
+  input_to("_todo_neu2",0,"");
+  return 1;
+}
+
+private int _todo_loeschen(string cmdline)
+{
+  int nr;
+  if (!sscanf(cmdline,"loeschen %d",nr))
+    return notify_fail("Welchen Eintrag moechtest Du loeschen?\n"),0;
+  if (!sizeof(todo_data))
+    return notify_fail("Deine Todo-Liste hat ja ueberhaupt keinen "
+                       "Eintrag!\n"),0;
+  if (nr>sizeof(todo_data))
+  {
+    printf("Deine Todo-Liste hat nur %d Eintra%s.\n",sizeof(todo_data),
+           (sizeof(todo_data)==1?"g":"ege"));
+    return 1;
+  }
+  todo_data[nr-1]=0;
+  todo_data-=({0});
+  todo_save();
+  printf("Eintrag Nummer %d wurde geloescht. Da hast Du ja ordentlich "
+         "was geschafft...\noder geschickt delegiert ;-)\n",nr);
+  return 1;
+}
+
+private int _todo_anzeigen(string cmdline)
+{
+  int nr1,nr2;
+  string output;
+  if (!sizeof(todo_data))
+    return notify_fail("Deine Todo-Liste hat keine Eintraege.\n"),0;
+  switch(sscanf(cmdline,"anzeigen %d bis %d",nr1,nr2)) 
+  {
+  case 0: nr1=1; nr2=sizeof(todo_data); break;
+  case 1: nr2=nr1; break;
+  case 2: break;
+  }
+  if (nr1<0) nr1=sizeof(todo_data)+nr1+1;
+  if (nr2<0) nr2=sizeof(todo_data)+nr2+1;
+  if (nr1<1||nr2<1||nr1>sizeof(todo_data)||nr2>sizeof(todo_data))
+    return notify_fail(sprintf("Deine Todo-Liste hat (gluecklicherweise) "
+                               "nur %d Eintra%s.\n",sizeof(todo_data),
+                               (sizeof(todo_data)>1?"ege":"g"))),0;
+  output="\n-----------------------------------------------------------------------------\n";
+  if (nr1>nr2) // Rueckwaerts
+  {
+    while(nr1>=nr2)
+      output+=sprintf("Eintrag Nr. %d von %s:\n%s\n\n",nr1--,
+           dtime(todo_data[nr1][0]),break_string(todo_data[nr1][1],78,5,
+                                       BS_LEAVE_MY_LFS));
+  }
+  else
+  {
+    nr1--;
+    while (nr1<nr2)
+      output+=sprintf("\nEintrag Nr. %d von %s:\n%s\n",(nr1+1),
+           dtime(todo_data[nr1][0]),break_string(todo_data[nr1++][1],78,5,
+                                       BS_LEAVE_MY_LFS));
+  }
+  More(output+"-----------------------------------------------------------------------------\n");
+  return 1;
+}
+
+private int _todo_verschieben(string cmdline)
+{
+  int from;
+  mixed to,dummy;
+  if (!(sscanf(cmdline,"verschieben %d nach %d",from,to)==2||
+        sscanf(cmdline,"verschieben %d nach %s",from,to)==2))
+    return 0;
+  from--;
+  if (stringp(to))
+  {
+    switch(to)
+    {
+      case "oben": to=from-1; break;
+      case "unten": to=from+1; break;
+      default: return 0;
+    }
+  }
+  else to--;
+  if (to==from) return notify_fail("Da ist der Eintrag ja schon!\n"),0;
+  if (from<0||from>=sizeof(todo_data)) return notify_fail(
+        "Wie willst Du einen nicht existenten Eintrag verschieben?\n"),0;
+  if (to<0||to>=sizeof(todo_data)) return notify_fail(
+        "Dahin kannst Du den Eintrag nicht verschieben.\n"),0;
+  dummy=todo_data[from];
+  if (to>from)
+    while (from++<to) todo_data[from-1]=todo_data[from];
+  else
+    while(from-->to) todo_data[from+1]=todo_data[from];
+  todo_data[to]=dummy;
+  printf("Eintrag wurde verschoben.\n");
+  return 1;
+}
+
+private int _todo_hilfe()
+{
+  printf(
+  "-------------------------------------------------------------------------------\n\n"
+  " Die Todo-Liste laesst sich mit den folgenden Befehlen steuern:\n\n"
+  " todo neu                                - Legt einen neuen Eintrag an.\n"
+  " todo loeschen <nummer>                  - Loescht einen Eintrag.\n"
+  " todo anzeigen [<nummer> [bis <nummer>]] - Zeigt die Todo-Liste an.\n"
+  " todo verschieben <nummer> nach <ziel>   - Verschiebt einen Eintrag.\n\n"
+  " <ziel> kann eine Zahl oder die Worte 'oben' oder 'unten' sein.\n"
+  "-------------------------------------------------------------------------------\n");
+  return 1;
+}
+
+
+static int _todo(string cmdline)
+{
+  string *args;
+  
+  cmdline=_unparsed_args(1);
+  notify_fail("Falsche Syntax. 'todo hilfe' zeigt eine Hilfe an.\n");
+  if (sizeof(cmdline))
+  {
+    args=explode(cmdline," ");
+    switch(args[0])
+    {
+      case "neu":         return _todo_neu(cmdline);
+      case "loeschen":    return _todo_loeschen(cmdline);
+      case "anzeigen":    return _todo_anzeigen(cmdline);
+      case "verschieben": return _todo_verschieben(cmdline);
+      case "hilfe":       return _todo_hilfe();
+     default:            return 0;
+    }
+  }
+  return 0;
+}
diff --git a/std/shells/magier/upd.c b/std/shells/magier/upd.c
new file mode 100644
index 0000000..f09ea9a
--- /dev/null
+++ b/std/shells/magier/upd.c
@@ -0,0 +1,563 @@
+// MorgenGrauen MUDlib
+//
+// upd.c
+//
+// $Id: upd.c 8850 2014-06-13 21:34:44Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <magier.h>
+#include <player.h>
+#undef NEED_PROTOTYPES
+#include <debug_info.h>
+#include <wizlevels.h>
+#include <moving.h>
+#include <properties.h>
+#include <logging.h>
+#include <thing/properties.h>
+
+varargs static int _make(string file, int flags, int recursive);
+
+static mixed _query_localcmds()
+{
+  return ({({"upd","_upd",0,LEARNER_LVL}),
+           ({"load","_load",0,LEARNER_LVL})});
+}
+
+//
+// _save(): Spieler in Rettungsraum retten
+// obj:      Spielerobjekt(?)
+// inv_save: Rettungsraumname 
+// Rueckgabe: 0 wenn kein Spielerobjekt
+//            Spielerobjekt, falls doch
+//
+
+static mixed _save( object obj, object inv_saver )
+{ 
+    if ( query_once_interactive(obj) )
+      { 
+        obj->move( inv_saver, NO_CHECK );
+        return obj;
+      }
+    return 0;
+}
+
+
+//
+// _reload(): Objekt laden
+// file:  Filename. Muss in der Form /xx/xx/xx.c vorliegen
+// clone:  > 0 -> Es soll geclont werden, enthaelt Objektnummer des
+//                Vorgaengerobjektes
+// flags: Kommandozeilenoptionen
+// err:   Leerstring als Referenz uebergeben. Enthaelt nach dem
+//        Aufruf vom _reload() die Fehlermeldungen als String.
+// Rueckgabe: Das neu erzeugte Objekt bzw. das schon vorhandene Objekt
+//            bzw. 0, wenn Fehler auftrat.
+//
+
+private object _reload(string file, int clone, int flags, string err)
+{
+  object obj;
+  
+  if (!obj=find_object(file[0..<3]+(clone?("#"+clone):"")))
+  {
+    int pos,pos2;
+    string bt;
+
+    if(file_size(file)<0)
+    {
+      if (file_size(file)==-1)
+        err = sprintf("upd: %s: Datei existiert nicht.\n", file);
+      else // directory
+        err = sprintf("upd: %s: Verzeichnisse koennen nicht geladen "
+                      "werden.\n",file);
+      return obj; // 0
+    }
+    pos = max(file_size(__DEBUG_LOG__),0);
+
+    if ((err = (clone?catch(obj = clone_object(file)):
+                catch(load_object(file)) )) && (flags & UPD_B))        
+    {     
+      if (( pos2=file_size(__DEBUG_LOG__)) > pos )
+        err+=sprintf("\nBacktrace:\n%s",
+                     read_bytes(__DEBUG_LOG__,pos, pos2-pos ));
+      else
+        err+=sprintf("\nKEIN BACKTRACE VERFUEGBAR!\n");
+    }
+    if (!err&&!obj&&(!obj = find_object(file[0..<3])))
+      err += sprintf( "upd: %s: Blueprint nach dem Laden zerstoert.\n",file );
+  }
+  else
+    err=sprintf("upd: Objekt existiert schon: %O\n",obj);
+  return obj;
+}
+
+
+//
+// _update(): File updaten -> Blueprint destructen
+// file:  Filename
+// dummy: simulieren? (1->wird nicht aktualisiert)
+// flags: Kommandozeilenoptionen
+// Rueckgabe: -1: Keine Vollzugriff
+//             0: Objekt ist nicht geladen
+//             1: Operation wird durchgefuehrt
+//
+
+private varargs int _update(string file, int dummy, int flags)
+{
+  object obj;
+  string err;
+  if (!dummy && !objectp(obj = find_object(file))) return 0;
+  if (!IS_ARCH(this_object()))
+  {
+    // Schreibrechte nur pruefen, wenn echt aktualisiert werden soll.
+    if(!dummy && !MAY_WRITE(file))
+      return (printf("upd: %s: Keine Schreibrechte!\n",file), -1);
+    if(!MAY_READ(file)) 
+      return (printf("upd: %s: Keine Leserechte!\n", file), -1);
+  }
+  if (dummy) return 1;
+  
+  if ( flags & UPD_D )
+  {
+    object *inv;
+    if (sizeof(inv = deep_inventory(obj)))
+    {
+      printf("upd: %s: Entferne Objekte im Inventar\n", file );
+      if (!(flags&UPD_H))
+      {
+        if(err=catch(filter_objects( inv, "remove", 1 )))
+          printf("upd: %s: Fehlgeschlagen. Grund:\n%s\n",
+                 file,err);
+      }
+      if (sizeof(inv = deep_inventory(obj)))
+        filter(inv, function void (object ob)
+            {destruct(ob);});
+    }
+  }
+  if (!(flags&UPD_H))
+  {
+    if(err = catch(obj->remove()))
+      printf("upd: %s: remove() fehlgeschlagen. Aufruf von " +
+             "destruct().\n",file);
+  }
+  if(objectp(obj)) destruct(obj);
+  return 1;
+}
+
+
+//
+// _instance_upd(): Alle Objekte nach Clones des Objekts durchsuchen
+// file:  Filename des Objektes
+// flags: Kommandozeilenargumente
+// obj:   Aktuelles Objekt
+// instances: Zahl der gefundenen Instanzen
+//
+
+private void _instance_upd(string file, int flags, mixed obj, int instances,
+                           int firstcall)
+{
+ int i;
+ if (firstcall)
+    printf("upd: %s: %s Instanzen.\n",file,flags&UPD_A?"Aktualisiere":"Suche");
+  
+  while (get_eval_cost()>220000 && i < sizeof(obj))
+  {
+    if (!objectp(obj[i]))
+      instances--;
+    else
+    {
+      if (flags&UPD_F&&!(flags&UPD_S))
+        printf( "upd: %O gefunden in %s\n", obj[i],
+                environment(obj[i])?object_name(environment(obj[i])) 
+                : "keiner Umgebung" );
+      if (flags&UPD_A) _make(object_name(obj[i]), flags & ~(UPD_A|UPD_F),1 );
+    }
+    i++;
+  }
+  if (i < sizeof(obj)) 
+    call_out( #'_instance_upd/*'*/,2,file,flags,obj[i..],instances,0);
+  else
+    printf( "upd: %s: %d Instanzen %s\n", file, instances,
+            (flags & UPD_A) ? "aktualisiert" : "gefunden" );
+  return;
+}
+  
+  
+//
+// _do_make(): Alle geerbten Objekte bearbeiten (fuer -m/-v)
+// file:  Name des Files
+// clone: 0, wenn blueprint, ansonsten Clonenummer
+// flags: Kommandozeilenparameter
+// dep:   geschachteltes Array mit Meldungen (wg. Rekursion)
+// ready: Array der schon bearbeiteten Objekte (wg. Rekursion)
+// Rueckgabe: Array der Objektnamen, die bearbeitet wurden
+//            (Array in Array in ...)
+// 
+
+varargs private int _do_make( string file,int clone,int flags,mixed dep,
+                              string *ready )
+{
+  object obj;
+  string err;
+  string *ilist;
+  mixed downdeps;
+  int ret;
+  
+  if (!pointerp(ready)) ready = ({});
+  ready += ({ file });
+  
+  if ( !(obj = _reload(file,clone,flags,&err)))
+  {
+    dep += ({ err });
+    return 0;
+  }
+  
+  ilist = inherit_list(obj)-ready;
+  
+  downdeps = ({});
+  
+  while (sizeof(ilist))
+  {
+    ret = _do_make( ilist[0],0,flags, &downdeps, &ready )||ret;
+    ilist[0..0] = ({});
+    ilist -= ready;
+  }
+  
+  if ( ret||file_time(file)>program_time(obj)||(flags &UPD_I))
+    if ( _make( file, flags & ~(UPD_M|UPD_I) ,1) < 0 ) 
+      dep = ({ "{" + explode(file,"/")[<1] + "}", downdeps });
+    else{
+      dep = ({ "[" + explode(file,"/")[<1] + "]", downdeps });
+      ret = 1;
+    }
+  else if (flags&UPD_V) dep += ({ explode(file,"/")[<1], downdeps });
+  return ret;
+}
+
+
+//
+// _make_dep(): Ausgabe des Ererbungsbaumes
+// Objekte im Array dep
+// prefix enthaelt Zeilenanfang (fuer Rekursion)
+// Rueckgabe: String mit dem Vererbungsbaum des Objektes
+//
+
+private string _make_dep( mixed dep, string prefix )
+{
+  string ret;
+  int i, size;
+  
+  ret="";
+  size=sizeof(dep);
+  for (i=0; i<size;i++)
+    if (pointerp(dep[i]))
+      ret += _make_dep(dep[i],prefix + (i < (size-1) ? "| ":" "));
+    else 
+      ret += prefix + "+-" + dep[i] + "\n";
+  return ret;
+}
+
+
+//
+// _illegal_closure(): ist closure in arg an objekt gebunden?
+// arg: closure(-array/mapping)
+// Rueckgabe: 0 wenn alles okay
+//            1 wenn closure geloescht werden muss
+//
+
+private int _illegal_closure( mixed arg )
+{
+  int i, j;
+  string *indices;
+  
+  if ( closurep(arg) && !objectp(query_closure_object(arg)) )
+    return 1;
+  
+  if ( pointerp(arg) ){
+    for ( i = sizeof(arg); i--; )
+      if ( _illegal_closure(arg[i]) )
+        return 1;
+  }
+  else if ( mappingp(arg) ){
+    indices = m_indices(arg);
+    for ( i = sizeof(indices); i--; )
+      for ( j = get_type_info( arg, 1 ); j--; )
+        if ( _illegal_closure(arg[indices[i], j]) )
+          return 1;
+  }
+  return 0;
+}
+
+//
+// _make(): Update file
+// file:  Filename
+// flags: Kommandozeilenargumente
+//
+
+varargs static int _make(string file, int flags,int recursive)
+{
+  string msg, err, blue;
+  int inst;
+  object obj, inv_saver;
+  mixed tmp;
+  
+  msg = "";
+
+  if (!file) return printf( "upd: Kein Filename uebergeben!\n" ), RET_FAIL;
+    
+// Filename in Blue, Objektname in blue, Instanznummer in inst
+
+  if (sscanf(file,"%s#%d",blue,inst)==2) blue += ".c";
+  else blue = file + (file[<2..]==".c" ? "" : ".c");
+
+// Alle Instanzen durchsuchen im Falle von -a oder -f 
+
+  if ((flags & UPD_LOAD)&&find_object(file))
+    return printf("load: %s: Objekt ist schon geladen.\n",file),RET_OK;
+  
+  if ( flags & (UPD_F|UPD_A))
+  {
+    if (inst) return printf( "upd: %s: Eine Instanz kann keine " +
+                             "Clones haben.\n",file ), RET_FAIL;
+    if ((tmp=_update(file, 1,flags))==-1) 
+      return printf( "upd: %s: Kein Vollzugriff auf die " +
+                     "Datei erlaubt.\n",file), RET_FAIL;
+    if (tmp==0) return RET_FAIL;
+
+   tmp=clones(blue[0..<3],2);
+    if (sizeof(tmp))
+      call_out( #'_instance_upd/*'*/, 0, file,flags,tmp,sizeof(tmp),1);
+    else
+      printf( "upd: %s: Keine Clones vorhanden!\n", blue[0..<3]);
+
+    if ( (flags & UPD_F) && !(flags &(UPD_R|UPD_L|UPD_LOAD))) return RET_OK; 
+    // Nichts laden -> Auch kein Backup
+  }
+
+// Backupraum festlegen
+   
+  if( blue==INV_SAVE ) {
+                printf("upd: Achtung: Raum zum Zwischenspeichern soll geladen werden?!\n");
+        } 
+  if ( !(inv_saver=load_object(INV_SAVE)) )
+  {
+    printf("upd: %s: Raum zum Zwischenspeichern des " +
+           "Rauminhalts ist nicht ladbar.\n" +
+           "         %s\n",file,INV_SAVE);
+    return RET_FAIL;
+  }
+
+// Wenn das Objekt existiert bzw. Deep aktualisiert werden soll
+
+  if ( (!(flags&UPD_LOAD))&&
+       ((obj = find_object(file)) || (flags & (UPD_M|UPD_I))))
+  {
+    object *inv, env, *pl_inv;
+    mapping pro;
+    int i;
+    mixed configdata;
+    int restore_config;
+
+    // Wenn Objekt existiert, dann Inhalt und ggf. Daten aus Configure() oder
+    // Props sichern:
+    if (obj)
+    {
+      // im Fall UPD_C erfolgt _kein_ Auslesen und _keine_ Restauration
+      // mittels Configure()
+      if ( ! (flags & UPD_C ) )
+      {
+        catch(restore_config=(mixed)call_resolved(&configdata,obj,
+                                                  "Configure",0);
+              publish);
+        // Wenn UPD_CONF gesetzt wird, _muss_ das Objekt ein oeffentliches
+        // Configure() definieren, sonst erfolgt Abbruch.
+        if ((flags & UPD_CONF) && !restore_config)
+        {
+          printf("upd: %s: hat kein Configure(), Zerstoerung abgebrochen.\n",file);
+          return RET_FAIL;
+        }
+      }
+      if (!(flags&UPD_D)&&(flags&(UPD_L|UPD_R)))
+      {
+        if (i=sizeof(inv=(all_inventory(obj)-({0}))))
+        {
+          mixed items;
+          // Herausbekommen, ob hier Items existieren, die per AddItem 
+          // erzeugt werden. Die duerfen nicht gesichert werden.
+          items=(mixed)obj->QueryProp(P_ITEMS); // mixed, da array of arrays
+          if (pointerp(items)&&sizeof(items))
+          {
+            items=transpose_array(items)[0];
+            while (i--)
+              if (member(items, inv[i])==-1)
+                    inv[i]->move(inv_saver,NO_CHECK);
+          }
+          else // In diesem Objekt sind keine Items gesetzt.
+            while (i--) inv[i]->move(inv_saver,NO_CHECK);
+        }
+      }
+      else
+      {
+        inv=map( deep_inventory(obj), #'_save/*'*/, inv_saver )-({0});
+      }
+      env = environment(obj);
+      if ( flags & UPD_C )
+      {
+        pro = (mapping)(obj->QueryProperties());
+      }
+    }
+    else inv = ({});
+
+// Ererbte Objekte durchsuchen.
+    if ( flags & (UPD_M|UPD_I) )
+    {
+      mixed dep;
+      dep = ({});
+      _do_make( blue, inst, flags & ~(UPD_M|UPD_L|UPD_R|UPD_F|UPD_A), &dep );
+      printf( _make_dep( dep, "" ) + "\n" );
+    }
+   
+// Tatsaechlichen Update durchfuehren
+   
+    if ( _update(file, 0, flags)< 0) return RET_FAIL;
+    msg += (inst ? "zerstoert" : "aktualisiert");
+    
+// Neu laden ??
+    if ( flags & (UPD_R|UPD_L) )
+    {
+      if ( obj = _reload( blue,inst,flags, &err ) )
+        msg += ", " + (inst ? "neu geclont" : "neu geladen");
+     
+// Neu geladen: Properties wiederherstellen, Closures filtern 
+      if (!err)
+      {
+        if (!obj) obj = find_object(file);
+        // Wenn gewuenscht, Props zurueckschreiben (hat Prioritaet vor
+        // Configure(), weil explizit vom Magier angefordert).
+        if ( pro && (flags & UPD_C) )
+        {
+          string *names;
+          
+          names = m_indices(pro);
+          
+          // Closures in (mittlerweile) zerstoerten Objekten
+          // rausfiltern, damit die (hoffentlich korrekten) Closures
+          // im neu geclonten Objekt erhalten bleiben
+          for ( i = sizeof(names); i--; )
+            if ( _illegal_closure(pro[names[i], F_VALUE]) ||
+                 _illegal_closure(pro[names[i], F_QUERY_METHOD]) ||
+                 _illegal_closure(pro[names[i], F_SET_METHOD]) )
+              m_delete( pro, names[i] );
+          
+          obj->SetProperties(pro);
+          msg += ", Properties gesetzt";
+        }
+        // Wenn kein UPD_C, wird ggf. das Ergebnis von Configure() wieder
+        // uebergeben.
+        else if (restore_config)
+        {
+          int conf_res;
+          if (!catch(conf_res=(int)obj->Configure(configdata); publish)
+              && conf_res == 1)
+          {
+            msg += ", (re-)konfiguriert";
+          }
+          else
+          {
+            msg += ", (Re-)Konfiguration fehlgeschlagen";
+            if (flags & UPD_CONF)
+              printf("upd: Daten von %s konnten nicht rekonfiguriert werden: "
+                           "%O\n", file, configdata);
+          }
+        }
+        if (env)
+        {
+          if ( obj->move( env, NO_CHECK ) <= 0 )
+            printf( "upd: /%O konnte nicht in /%O zurueckbewegt werden\n",
+                    obj, env );
+          else
+            msg += ", bewegt";
+        }
+        if (i=sizeof(inv))
+        {
+          while(i--) if (inv[i]) inv[i]->move(obj, NO_CHECK );
+          msg += ", Inhalt zurueckbewegt";
+        }
+      }
+      else 
+        return printf( "upd: %s: %s", file, err ), RET_FAIL;
+    }
+  }
+  else 
+    if ( !_update(file, 0, flags) && (flags & (UPD_L|UPD_LOAD)) )
+      if ( !_reload( blue, inst, flags, &err ) )
+        return printf( "%s: %s: %s", (flags&UPD_LOAD?"load":"upd"),file, err ),
+          RET_FAIL;
+      else
+        msg += "geladen";
+  
+  if ( sizeof(msg)&&!(flags&UPD_S&&recursive) )
+    printf("%s: %s: %s.\n",(flags&UPD_LOAD?"load":"upd"),file,msg);
+  return RET_OK;
+}
+
+//
+// _upd: Objekte laden, zerstoeren und aktualisieren
+//
+
+static int _upd(string cmdline)
+{
+  int flags;
+  mixed *args;
+
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,UPD_OPTS,1);
+  if(flags==-1||!sizeof(args))
+    return USAGE("upd [-"+UPD_OPTS+"] <datei> [<datei> ..]");
+  if ((flags & UPD_C) && (flags & UPD_CONF))
+  {
+    printf("upd: -c und -C gleichzeitig werden nicht unterstuetzt.\n");
+    return 1;
+  }
+  args=file_list(args,MODE_UPD,0,"/");
+  if(!sizeof(args)) return printf("upd: Keine passende Datei gefunden!\n"),1;
+
+  args=map(args,(: $1[FULLNAME] :))-({0});
+
+  if(!sizeof(args))
+  {
+    printf("upd: Verzeichnisse koennen nicht aktualisiert werden!\n");
+    return 1;
+  }
+  asynchron(args,#'_make,flags,0,0);
+  return 1;
+}
+
+//
+// _load: Objekte laden
+//
+
+static int _load(string cmdline)
+{
+  int flags;
+  mixed *args;
+
+  cmdline=_unparsed_args();
+  args=parseargs(cmdline,&flags,"",1);
+  if(flags==-1||!sizeof(args))
+    return USAGE("load <datei> [<datei> ..]");
+  args=file_list(args,MODE_UPD,0,"/");
+  if(!sizeof(args)) return printf("load: Keine passende Datei gefunden!\n"),1;
+  args=map(args,(: (($1[FILESIZE]!=-2||find_object($1[FULLNAME]))?
+                          $1[FULLNAME]:0) :))-({0});
+
+  if(!sizeof(args))
+    return printf("load: Verzeichnisse koennen nicht geladen werden!\n"),1;
+  asynchron(args,#'_make,UPD_LOAD,0,0);
+  return 1;
+}
diff --git a/std/shells/orc.c b/std/shells/orc.c
new file mode 100644
index 0000000..95c73f6
--- /dev/null
+++ b/std/shells/orc.c
@@ -0,0 +1,203 @@
+#pragma strong_types,save_types
+
+inherit "std/player/base";
+
+#include <properties.h>
+#include <attributes.h>
+#include <wizlevels.h>
+#include <health.h>
+#include <new_skills.h>
+#include <language.h>
+#include <combat.h>
+#include <defines.h>
+#include <defuel.h>
+
+/*
+ * Orks:
+ * Orks sind eigentlich boese und blutruenstig, was auch oft genug zum 
+ * Vorschein tritt :) Wenn ein Ork zu heftig forscht, quengelt er rum 
+ * und weigert sich, bis er nicht wieder ein bisschen Blut verspritzt
+ * hat,
+*/ 
+#define F_MAX 500
+#define F_DEG 3 
+#define NO_EXAMINE ({ \
+    "Du knurrst: Ich will Blut, keine Bluemchen.", \
+    "Du grummelst: Bin ich ein Forscher, oder was?" \
+    })
+
+static int f_cnt, f_deg;
+
+int 
+QueryFCnt() {
+  return f_cnt;
+}
+
+int 
+SetFCnt(int fc) {
+  if(fc > -1 && fc < F_MAX)
+    f_cnt = fc;
+  return f_cnt;
+}
+  
+int
+QueryFDeg() {
+  return f_deg;
+}
+
+void 
+create() {
+  if (!clonep() || object_name(this_object()) == __FILE__[0..<3]) {
+      set_next_reset(-1);    
+      return;
+  }
+
+  mixed res;
+
+  base::create();
+
+  f_cnt=0;
+  f_deg=F_DEG;
+  
+  SetDefaultHome("/d/vland/morgoth/room/city/rathalle");
+  SetDefaultPrayRoom("/d/vland/morgoth/room/city/c0606");
+  SetProp(P_ATTRIBUTES_OFFSETS,([A_STR:3,A_INT:-1,A_CON:2]));
+  /* Kleine aeh grosse Muskelpakete */
+  SetProp(P_SKILL_ATTRIBUTE_OFFSETS,([SA_DAMAGE:110]));
+  SetProp(P_AVERAGE_SIZE,195);
+  SetProp(P_AVERAGE_WEIGHT,125000); // ziemlich schwer, viele Muskeln
+  SetProp(P_MATERIAL_KNOWLEDGE,([MATGROUP_DEAD:60,
+                                 MATGROUP_BIO:40, 
+                                 MATGROUP_ELEMENTAL: 20, 
+                                 MAT_BLOOD:100]));
+  SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({"Uruk-Hai"}));
+  SetProp(P_RESISTANCE_STRENGTHS,
+	  ([ 
+     DT_FIRE : -0.2,
+     DT_HOLY :0.3,
+     DT_UNHOLY : -0.2,
+	   DT_ACID : 0.2 ]));
+
+  SetProp(P_MAX_FOOD,110);
+  SetProp(P_MAX_DRINK,110);
+  SetProp(P_MAX_ALCOHOL,150);
+  SetProp(P_DEFUEL_LIMIT_FOOD,50);
+  SetProp(P_DEFUEL_LIMIT_DRINK,70);
+  SetProp(P_DEFUEL_TIME_FOOD,300);
+  SetProp(P_DEFUEL_TIME_DRINK,400);
+  SetProp(P_DEFUEL_AMOUNT_FOOD,0.4);
+  SetProp(P_DEFUEL_AMOUNT_DRINK,0.35);
+
+  /* SP regenerieren sie nich ganz so schnell, dafuer sind sie
+   * ein bischen schneller bei Gift und Futter */
+  SetProp(P_SP_DELAY,HEAL_DELAY+1);
+  SetProp(P_POISON_DELAY,POISON_DELAY-1);
+  SetProp(P_FOOD_DELAY,FOOD_DELAY-1);
+
+  SetProp(P_MAGIC_RESISTANCE_OFFSET,
+          ([ MT_ANGRIFF : 500,
+        	   MT_ILLUSION : -250,
+             MT_BEHERRSCHUNG : -250,
+        	   MT_VERWANDLUNG : 500 ]));
+
+
+  if(!(res=QueryProp(P_HANDS)) || !pointerp(res) || (sizeof(res)<3))
+    res=({" mit starken Haenden",35,({DT_BLUDGEON, DT_RIP}) });
+  SetProp(P_HANDS,res);
+  /* Orks haben dicke Haut */
+  SetProp(P_BODY,20);
+
+  /* Groesse wird nur einmal gesetzt */
+  if(!QueryProp(P_SIZE)){
+    SetProp(P_SIZE,180+random(31));
+    Set(P_SIZE,SAVE,F_MODE_AS);
+  }
+
+  /* Dito Gewicht */
+  if(!QueryProp(P_WEIGHT) || (QueryProp(P_WEIGHT) == 75000)){
+    SetProp(P_WEIGHT,100000+random(25001)+random(25001));
+    Set(P_WEIGHT,SAVE,F_MODE_AS);
+  }
+}
+
+string 
+_query_race() {
+  return "Ork";
+}
+
+string 
+_query_real_race() {
+  return "Ork";
+}
+
+string 
+_query_racedescr() {
+  return break_string("Ein Ork. Die brutale Macht des Boesen. Ein "
+      "erbitterter Kaempfer, ohne Furcht vor dem Tod. So stellst Du "
+      "Dir einen Ork vor.\nDiese Orks sehen vielleicht ausserlich so "
+      "aus: Eine dicke und dunkle, lederartige Haut, die sicher "
+      "einiges an Schlaegen abhaelt. Lange Eckzaehne, die sicher boese "
+      "Wunden reissen koennen.\nDu spuerst, dass diese Orks hier ein "
+      "wenig anders sind. Ihren Drang nach dem Blut ihrer Feinde scheinen "
+      "sie recht gut unter Kontrolle zu haben, die meiste Zeit zumindest. "
+      "Sie sind in der Lage, friedlich zwischen den anderen Rassen zu "
+      "wandeln. Dennoch sind vor allem die Uruk-Hai Orks als Gegner nicht "
+      "zu unterschaetzen, sollte es doch einmal zu einem Kampf kommen.",
+      78,0,BS_LEAVE_MY_LFS);
+}
+
+int 
+QueryAllowSelect() { 
+  return 1; 
+}
+
+string 
+*_query_racestring() {
+  if (QueryProp(P_GENDER)==FEMALE)
+    return ({"Orkin","Orkin","Orkin","Orkin"});
+  return ({"Ork","Orkes","Ork","Ork"});
+}
+
+string 
+_query_default_guild(){
+  return "urukhai";
+}
+
+mixed 
+RaceDefault(string arg) {
+  if (!arg)
+    return 0;
+  switch(arg) {
+    case P_HANDS :
+      return ({" mit starken Haenden",35,({DT_BLUDGEON,DT_RIP}) });
+    case P_BODY :
+      return 25;
+  }
+  return base::RaceDefault(arg);
+}
+
+protected void heart_beat() {
+  ::heart_beat();
+
+  if(f_cnt > 0 && !--f_deg) {
+    f_cnt--;
+    f_deg=F_DEG;
+  }
+}
+
+void
+Attack(object enemy) {
+  if(f_cnt > 0)
+    f_cnt--;
+  return ::Attack(enemy);
+}
+
+varargs int 
+_examine(string str, int mode) {
+
+  if(++f_cnt > F_MAX) {
+    tell_object(this_object(),break_string(NO_EXAMINE[random(sizeof(NO_EXAMINE))],78));
+    return 1;
+  }
+  return ::_examine(str,mode);
+}
diff --git a/std/shells/testie.c b/std/shells/testie.c
new file mode 100644
index 0000000..18c42af
--- /dev/null
+++ b/std/shells/testie.c
@@ -0,0 +1,6 @@
+#pragma strong_types,save_types
+
+inherit "/std/shells/human";
+
+int QueryAllowSelect() { return 0; }
+
diff --git a/std/shop.c b/std/shop.c
new file mode 100644
index 0000000..d4cf690
--- /dev/null
+++ b/std/shop.c
@@ -0,0 +1,38 @@
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/room";
+inherit "/std/room/shop";
+
+#include <properties.h>
+
+protected void create()
+{
+  if (object_name(this_object()) == __FILE__[0..<3])
+  {
+    set_next_reset(-1);
+    return;
+  }
+  room::create();
+  shop::create();
+
+  SetProp(P_LIGHT, 1);
+  SetProp(P_INDOORS,1);
+  SetProp(P_INT_SHORT, "StdLaden");
+  SetProp(P_INT_LONG,
+   "Der Magier, der diesen Laden erschuf, fand keine Beschreibung dafuer.\n");
+  AddItem("/obj/entsorg",REFRESH_REMOVE);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void reset()
+{
+  room::reset();
+  shop::reset();
+}
diff --git a/std/spellbook.c b/std/spellbook.c
new file mode 100644
index 0000000..24f824f
--- /dev/null
+++ b/std/spellbook.c
@@ -0,0 +1,785 @@
+// MorgenGrauen MUDlib
+//
+// spellbook.c -- Grundlegende Funktionen fuer Zaubersprueche
+//
+// $Id: spellbook.c 9142 2015-02-04 22:17:29Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma no_shadow
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+//#define NEED_PROTOTYPES
+inherit "/std/thing";
+inherit "/std/restriction_checker";
+inherit "/std/player/pklog";
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <wizlevels.h>
+#include <new_skills.h>
+#include <spellbook.h>
+#define ME this_object()
+
+void create() {
+  seteuid(getuid());
+  // diese Prop sollte von aussen nicht direkt geaendert werden koennen.
+  Set(P_SB_SPELLS,([]),F_VALUE);
+  Set(P_SB_SPELLS,PROTECTED,F_MODE_AS);
+
+  ::create();
+}
+
+mapping _query_sb_spells() {
+    // Kopie liefern, da sonst versehentlich schnell eine Aenderung des
+    // originalen Mappings hier passieren kann.
+    return(deep_copy(Query(P_SB_SPELLS, F_VALUE)));
+}
+
+mapping QuerySpell(string spell) {
+  mapping spi;
+
+  //Query-Methode umgehen, damit nur ein Spellmapping kopiert werden muss und
+  //nicht das ganzen Mapping mit allen Spells.
+  if (!spell
+      || !(spi=Query(P_SB_SPELLS))
+      || !(spi=spi[spell]))
+    return 0;
+  return deep_copy(spi);
+}
+
+varargs int
+AddSpell(string verb, int kosten, mixed ski) {
+  int level;
+  mapping spells;
+
+  if (!verb || kosten<0)
+    return 0;
+  
+  verb=lower_case(verb);
+  level=(intp(ski))?ski:0;
+
+  if (!mappingp(ski)) ski=([]);
+  // Zur Sicherheit eine Kopie machen, wer weiss, was da uebergeben wird, bzw.
+  // was der Setzende mit ski hinterher noch macht.
+  else ski=deep_copy(ski);
+
+  ski=AddSkillMappings(QueryProp(P_GLOBAL_SKILLPROPS),ski);
+  ski+=([SI_SPELLCOST:kosten]);
+  // SI_SPELL-Submapping hinzufuegen, wenn nicht da.
+  if (!member(ski,SI_SPELL))
+      ski+=([SI_SPELL:([]) ]);
+  // ski[SI_SPELL][SP_NAME] ergaenzen, ggf. aus ski verschieben
+  if (!stringp(ski[SI_SPELL][SP_NAME]) && stringp(ski[SP_NAME])) {
+      ski[SI_SPELL][SP_NAME] = ski[SP_NAME];
+      m_delete(ski, SP_NAME);
+  }
+  else if (!stringp(ski[SI_SPELL][SP_NAME])) {
+      ski[SI_SPELL][SP_NAME] = verb;
+  }
+  if (stringp(ski[SP_NAME])) {
+      m_delete(ski, SP_NAME);
+  }
+
+  if (level)
+    ski=AddSkillMappings(ski,([SI_SKILLRESTR_LEARN:([P_LEVEL:level])]));
+  
+  // Abfrage per Query(), um das Mapping als Referenz, nicht als Kopie zu
+  // bekommen, das spart etwas Zeit.
+  if (!(spells=Query(P_SB_SPELLS,F_VALUE))) {
+    spells=([]);
+    SetProp(P_SB_SPELLS,spells);
+  }
+  // Spell setzen...
+  spells[verb]=ski;
+  // Set() nicht noetig, da Query() an spell das Originalmapping geliefert hat
+  // und dieses bereits jetzt geaendert wurde. ;-)
+  //Set(P_SB_SPELLS,spells+([verb:ski]),F_VALUE);
+
+  // hat wohl geklappt...
+  return(1);
+}
+
+int
+TryAttackSpell(object victim, int damage, mixed dtypes,
+               mixed is_spell, object caster, mapping sinfo) {
+  mixed no_attack;
+  int nomag;
+
+  if (no_attack = (mixed)victim->QueryProp(P_NO_ATTACK)) {
+    if (stringp(no_attack))
+      write(no_attack);
+    else
+      write(victim->Name(WER,1)+" laesst sich nicht angreifen!\n");
+    return 0;
+  }
+  if (victim->QueryProp(P_GHOST)) {
+    write(victim->Name(WER,1)+" ist ein Geist!\n");
+    return 0;
+  }
+
+  damage=(damage*(int)caster->QuerySkillAttribute(SA_DAMAGE))/100;
+
+  // ggf. Loggen von PK-Versuchen und ggf. ausserdem Angriff abbrechen.
+  // BTW: Aufruf von InsertEnemy() gibt 0 zurueck, wenn der Gegner nicht
+  // eingetragen wurde. Allerdings tut es das auch, wenn der Gegner schon
+  // Feind war. Daher muss noch geprueft werden, ob nach dem InsertEnemy() die
+  // beiden Spieler Feinde sind. Wenn nicht, wird der Spell auch abgebrochen
+  // und eine Meldung ausgegeben.
+  if (query_once_interactive(caster) && query_once_interactive(victim)
+      && CheckPlayerAttack(caster, victim, 
+        sprintf("Spellbook: %O, Spell: %O\n",
+                object_name(this_object()),
+                ((mappingp(sinfo) && is_spell) ? sinfo[SP_NAME]  : "")))
+      && !caster->IsEnemy(victim)
+      && !caster->InsertEnemy(victim)
+      )
+  {
+    tell_object(ME, "Ein goettlicher Einfluss schuetzt Deinen Gegner.\n");
+    return 0;
+  }
+
+  nomag=(int)victim->QueryProp(P_NOMAGIC);
+  if ((sinfo[SI_NOMAGIC] < nomag) &&
+      nomag*100 > random(100)*(int)caster->QuerySkillAttribute(SA_ENEMY_SAVE)) {
+    printf("%s wehrt Deinen Zauber ab.\n", capitalize((string)victim->name(WER, 1)));
+    return 0;
+  }
+  else {
+    return (int)victim->Defend(damage, dtypes, is_spell, caster);
+  }
+}
+
+varargs int
+TryDefaultAttackSpell(object victim, object caster, mapping sinfo, 
+                      mixed si_spell) {
+  object team;
+  int row;
+  
+  if (!si_spell) si_spell=sinfo[SI_SPELL];
+  // Wenn der Spieler in einem Team ist, die Teamreihen-Boni
+  // beruecksichtigen. Wenn nicht, eben nicht.
+  if (!team=((object)caster->QueryProp(P_TEAM)))
+  return TryAttackSpell(victim,
+                        GetRandFValueO(SI_SKILLDAMAGE,sinfo,caster),
+                        GetData(SI_SKILLDAMAGE_TYPE,sinfo,caster),
+                        si_spell,
+                        caster,
+                        sinfo);
+  else
+  {
+	  row=(int)caster->PresentPosition();
+	  return TryAttackSpell(victim,
+		  GetRandFValueO(SI_SKILLDAMAGE,sinfo,caster)+
+		  // Nur wenn SI_SKILLDAMAGE_BY_ROW ein mapping ist, 
+		  // randomisiert addieren
+		  (mappingp(sinfo[SI_SKILLDAMAGE_BY_ROW])?
+		   random(sinfo[SI_SKILLDAMAGE_BY_ROW][row]):0)+
+		  // Nur wenn das OFFSET davon ein mapping ist,
+		  // nicht randomisiert addieren.
+		  (mappingp(sinfo[OFFSET(SI_SKILLDAMAGE_BY_ROW)])?
+		   sinfo[OFFSET(SI_SKILLDAMAGE_BY_ROW)][row] : 0),
+		   GetData(SI_SKILLDAMAGE_TYPE,sinfo,caster),
+		   si_spell,caster,sinfo);
+   }
+}
+
+#define SMUL(x,y) ((x<0 && y<0)?(-1*x*y):(x*y))
+int
+SpellSuccess(object caster, mapping sinfo) {
+  int cval,abil,res;
+
+  abil=sinfo[SI_SKILLABILITY];
+  cval=(int)caster->UseSkill(SK_CASTING);
+  res=abil + (SMUL(abil,cval)) / MAX_ABILITY - random(MAX_ABILITY);
+  if (cval && res>MAX_ABILITY) // besonders gut gelungen?
+    caster->LearnSkill(SK_CASTING,1+(res-MAX_ABILITY)/2000);
+  return res;
+}
+
+int
+CanTrySpell(object caster, mapping sinfo) {
+  mapping rmap;
+  string res;
+
+  if (caster->QueryProp(P_GHOST)) {
+    write("Als Geist kannst Du nicht zaubern.\n");
+    return 0;
+  }
+  if ((rmap=sinfo[SI_SKILLRESTR_USE])
+      && (res=check_restrictions(caster,rmap))) {
+    write(res);
+    return 0;
+  }
+  return 1;
+}
+
+void
+Learn(object caster, string spell, mapping sinfo) {
+  int val,diff,attval;
+  mapping attr;
+ 
+  // gar nicht lernen?
+  if (sinfo[SI_NO_LEARN])
+    return;
+
+ // Attribut sollte int sein, wenn nicht anders angegeben
+ if (mappingp(sinfo[SI_LEARN_ATTRIBUTE]))
+ {
+	attr=sinfo[SI_LEARN_ATTRIBUTE];
+	// Direkt in einem durch berechnen.
+	// Ich gehe davon aus, dass wir nie nie nie
+	// ein weiteres Attribut ins MG einfuegen.
+	// Wir berechnen den Mittelwert
+	attval=((int)caster->QueryAttribute(A_INT)*attr[A_INT]+
+		(int)caster->QueryAttribute(A_DEX)*attr[A_DEX]+
+		(int)caster->QueryAttribute(A_STR)*attr[A_STR]+
+		(int)caster->QueryAttribute(A_CON)*attr[A_CON])
+		/(attr[A_CON]+attr[A_INT]+attr[A_STR]+attr[A_DEX]);
+		
+	 
+ } else {
+	 attval=(int)caster->QueryAttribute(A_INT);
+ }
+
+  
+  val=((attval/2)*GetFValue(SI_SKILLLEARN,sinfo,caster)+
+       GetOffset(SI_SKILLLEARN,sinfo,caster));
+  if (!(diff=GetFValueO(SI_DIFFICULTY,sinfo,caster)))
+    diff=GetFValueO(SI_SPELLCOST,sinfo,caster);
+  caster->LearnSkill(spell,val,diff);
+}
+
+void
+Erfolg(object caster, string spell, mapping sinfo) {
+  object env;
+  if(env=environment(caster))
+     env->SpellInform(caster,spell,sinfo);
+}
+
+void
+Misserfolg(object caster, string spell, mapping sinfo) {
+  write("Der Zauberspruch ist missglueckt.\n");
+  write("Du lernst aus Deinem Fehler.\n");
+  Learn(caster,spell,sinfo);
+}
+
+string
+SelectSpell(string spell, mapping sinfo) {
+  if (sinfo && sinfo[SI_SKILLFUNC])
+    return sinfo[SI_SKILLFUNC];
+  return spell;
+}
+
+varargs void
+prepare_spell(object caster, string spell, mapping sinfo) {
+  string txt;
+
+  if (!caster || !spell) return ;
+  if (!mappingp(sinfo) || !stringp(txt=sinfo[SI_PREPARE_MSG]))
+    txt="Du bereitest Dich auf den Spruch ``%s'' vor.\n";
+  tell_object(caster,sprintf(txt,spell));
+}
+
+varargs int
+UseSpell(object caster, string spell, mapping sinfo) {
+  mapping ski,tmp;
+  string spellbook,sname,fname,txt;
+  int res,fat,cost;
+  mixed ps;
+
+  if (!caster || !spell)
+    return 0;
+  // Spell kann in der Gilde anderen Namen haben
+  sname=SelectSpell(spell,sinfo);
+  if (!(ski=QuerySpell(sname))) // Existiert dieser Spell?
+    return 0;
+  // Gildeneigenschaften sollen Spelleigenschaften ueberschreiben koennen
+  ski=AddSkillMappings(ski,sinfo);
+  // Fuer verschiedene Rassen unterschiedliche Eigenschaften
+  ski=race_modifier(caster,ski);
+
+  // printf("Spellinfo: %O\n",ski);
+
+  if (!CanTrySpell(caster, ski))
+    return 1;
+
+  if (caster->QueryProp(P_SP) < (cost=GetFValueO(SI_SPELLCOST,ski,caster))) {
+    if(txt=ski[SI_SP_LOW_MSG])write(txt);
+    else write("Du hast zu wenig Zauberpunkte fuer diesen Spruch.\n");
+    return 1;
+  }
+  // printf("cost: %d\n",cost);
+
+  if (mappingp(ski[SI_X_SPELLFATIGUE])) {
+    // fuer jeden Key die Spellfatigue pruefen, wenn das Mapping hinterher
+    // nicht leer ist, ist man zu erschoepft.
+    tmp = filter(ski[SI_X_SPELLFATIGUE],
+        function int (string key, int val)
+        { return (int)caster->CheckSpellFatigue(key); } );
+    if (sizeof(tmp)) {
+        write(ski[SI_TIME_MSG] ||
+            "Du bist noch zu erschoepft von Deinem letzten Spruch.\n");
+      return 1;
+    }
+  }
+  else {
+    if (caster->CheckSpellFatigue()) {
+        write(ski[SI_TIME_MSG] ||
+            "Du bist noch zu erschoepft von Deinem letzten Spruch.\n");
+      return 1;
+    }
+  }
+
+  if (!(ski[SI_NO_ATTACK_BUSY]&NO_ATTACK_BUSY_QUERY) &&
+      caster->QueryProp(P_ATTACK_BUSY)) {
+    if (txt=ski[SI_ATTACK_BUSY_MSG]) write(txt);
+    else write("Du bist schon zu sehr beschaeftigt.\n");
+    return 1;
+  }
+
+  // Spruchvorbereitung
+  if (pointerp(ps=(mixed)caster->QueryProp(P_PREPARED_SPELL)) // Ausstehender Spruch
+      && sizeof(ps)>=3 && intp(ps[0] && stringp(ps[1]))) {
+    if (ps[1]==spell) { // Dieser Spruch wird noch vorbereitet
+      if (time()<ps[0]) {
+        if (!stringp(txt=ski[SI_PREPARE_BUSY_MSG]))
+          txt="Du bist noch mit der Spruchvorbereitung beschaeftigt.\n";
+        write(txt);
+        return 1;
+      }
+    }
+    else { // Andere Sprueche brechen die Vorbereitung ab
+      if (!mappingp(tmp=QuerySpell(ps[1])) || // richtige Meldung holen...
+          !stringp(txt=tmp[SI_PREPARE_ABORT_MSG]))
+        txt="Du brichst die Spruchvorbereitung fuer `%s' ab.\n";
+      printf(txt,ps[1]);
+      if (fat=GetValue(SI_PREPARE_TIME,ski,caster)) {
+        // Spruch braucht vorbereitungszeit
+        caster->SetProp(P_PREPARED_SPELL,({time()+fat,spell,ski[SI_SKILLARG]}));
+        prepare_spell(caster,spell,ski);
+        return 1;
+      }
+    }
+  }
+  else {
+    if (fat=GetValue(SI_PREPARE_TIME,ski,caster)) {
+      // Spruch braucht vorbereitungszeit
+      caster->SetProp(P_PREPARED_SPELL,({time()+fat,spell,ski[SI_SKILLARG]}));
+      prepare_spell(caster,spell,ski);
+      return 1;
+    }
+  }
+  if (ps)
+    caster->SetProp(P_PREPARED_SPELL,0);
+  
+  // Funktion kann anderen Namen haben als Spell
+  if (!(fname=sinfo[SI_SKILLFUNC]))
+    fname=sname;
+
+  if((ski[SI_NOMAGIC] < environment(caster)->QueryProp(P_NOMAGIC)) &&
+      random(100) < environment(caster)->QueryProp(P_NOMAGIC)) {
+    if (txt=ski[SI_NOMAGIC_MSG])
+      write(txt);
+    else
+      write("Dein Zauberspruch verpufft im Nichts.\n");
+    res=ABGEWEHRT;
+  }
+  else {
+    // Spruch ausfuehren.
+    res=(int)call_other(this_object(),fname,caster,ski);
+  }
+  // TODO: Wenn die ausgefuehrte Spellfunktion eine 0 zurueckgibt, sollen jetzt
+  // noch notify_fails zum Zuge kommen koennen. Daher in diesem Fall auch 0
+  // zurueckgeben.
+  if (!res || !caster)
+    return 1;
+
+  if (!(ski[SI_NO_ATTACK_BUSY]&NO_ATTACK_BUSY_QUERY))
+  	{
+	if (!ski[SI_ATTACK_BUSY_AMOUNT])  
+    		caster->SetProp(P_ATTACK_BUSY,1);
+	else
+		caster->SetProp(P_ATTACK_BUSY,ski[SI_ATTACK_BUSY_AMOUNT]);
+  	}
+
+  caster->restore_spell_points(-1*cost);
+
+  if (mappingp(ski[SI_X_SPELLFATIGUE])) {
+    // fuer jeden Key die Spellfatigue setzen. Keys mit Dauer 0 loesen keine
+    // Spellfatigue aus.
+    filter(ski[SI_X_SPELLFATIGUE],
+        function int (string key, int val)
+        { return (int)caster->SetSpellFatigue(val, key); } );
+  }
+  else {
+    if ((fat=GetFValueO(SI_SPELLFATIGUE,ski,caster))<0)
+      fat=1;
+    caster->SetSpellFatigue(fat);
+  }
+
+
+  if (res==ERFOLG)
+    Erfolg(caster,spell,ski);
+  else
+  if (res==MISSERFOLG)
+    Misserfolg(caster,spell,ski);
+  return 1;
+}
+
+object *
+FindGroup(object pl, int who) {
+  object *res,ob,env,team;
+  int p1,p2;
+  closure qp;
+
+  res=({});
+  if (!pl || !(env=environment(pl))) return res;
+  p1 = query_once_interactive(pl) ? 1 : -1;
+  team=(object)pl->QueryProp(P_TEAM);
+  for (ob=first_inventory(env);ob;ob=next_inventory(ob)) {
+    if (!living(ob)) continue;
+    qp=symbol_function("QueryProp",ob);
+    if (pl->IsEnemy(ob)) // Feinde sind immer Gegner
+      p2=-1*p1;
+    else if (objectp(team) && funcall(qp,P_TEAM)==team)
+      p2=p1; // Teammitglieder sind immer auf Seite des Spielers
+    else
+      p2 = (query_once_interactive(ob)||funcall(qp,P_FRIEND)) ? 1 : -1;
+    if (p2>0 && !interactive(ob) && query_once_interactive(ob))
+      continue; // keine Netztoten.
+    if (funcall(qp,P_GHOST))
+      continue;
+    if ( who<0 && (funcall(qp,P_NO_ATTACK) || funcall(qp,P_NO_GLOBAL_ATTACK)) )
+      continue;
+    if (IS_LEARNING(ob) &&
+        (funcall(qp,P_INVIS) || (who<0 && !pl->IsEnemy(ob))))
+      continue;
+    if (p1*p2*who >=0)
+      res+=({ob});
+  }
+  return res;
+}
+
+object *
+FindGroupN(object pl, int who, int n) {
+  if (!pl) return ({});
+  n=(n*(int)pl->QuerySkillAttribute(SA_EXTENSION))/100;
+  if (n<1) n=1;
+  return FindGroup(pl,who)[0..(n-1)];
+}
+
+object *
+FindGroupP(object pl, int who, int pr) {
+  object *res,*nres;
+  int i;
+
+  nres=({});
+  if (!pl) return nres;
+  pr=(pr*(int)pl->QuerySkillAttribute(SA_EXTENSION))/100;
+  if (pr<0) return nres;
+  res=FindGroup(pl,who);
+  for (i=sizeof(res)-1;i>=0;i--)
+    if (pr>=random(100))
+      nres+=({res[i]});
+  return nres;
+}
+
+// Findet feindliche und freundliche GRUPPEN im angegebenen Bereich.
+varargs mixed
+FindDistantGroups(object pl, int dist, int dy, int dx) {
+  mapping pos;
+  object ob,enteam,myteam;
+  int p1,min,max,i;
+  mixed *b_rows,x;
+  closure is_enemy, qp;
+
+  if (!objectp(pl) || !environment(pl))
+    return ({({}),({})});
+  if (!dy) dy=100;
+  if (!dx) dx=MAX_TEAM_ROWLEN*100;
+  x=(int)pl->QuerySkillAttribute(SA_EXTENSION);
+  dx=(dx*x)/100;dy=(dy*x)/100;
+  dist=(dist*(int)pl->QuerySkillAttribute(SA_RANGE))/100;
+  min=dist-dy/2;
+  max=dist+dy/2;
+
+  pos=([]);
+  p1=query_once_interactive(pl) ? 1 : -1;
+  is_enemy=symbol_function("IsEnemy",pl); // zur Beschleunigung
+  myteam=(object)pl->QueryProp(P_TEAM);
+  for (ob=first_inventory(environment(pl));ob;ob=next_inventory(ob)) {
+    if (!living(ob)) continue;
+    qp=symbol_function("QueryProp",ob); // zur Beschleunigung
+
+    // Zuerst mal die Position feststellen:
+    if (!objectp(enteam=funcall(qp,P_TEAM)))
+      pos[ob]=1;
+    else if (!pos[ob] && mappingp(x=(mapping)ob->PresentTeamPositions()))
+      pos+=x;
+    // PresentTeamPositions wird nur einmal pro Team ausgerechnet, weil
+    // anschliessend jedes anwesende Teammitglied pos[ob]!=0 hat.
+
+    pos[ob]=(2*pos[ob])-1;
+    // Reihen sollen Abstand 2 haben, auch Reihen 1 und -1 (nach Umrechnung)
+
+    // Feindliche Reihen an Y-Achse spiegeln, also gegenueber hinstellen:
+    if (funcall(is_enemy,ob))
+      pos[ob]*=-1; // Ist auf jeden Fall Feind
+    else if (objectp(myteam) && myteam==enteam)
+        ;            // Teammitglieder sind immer auf eigener Seite
+    else
+      pos[ob]*=(p1*((int)(query_once_interactive(ob)||
+                          funcall(qp,P_FRIEND))?1:-1));
+
+    // Den Spieler auf keinen Fall entfernen
+    if (ob==pl)
+      continue;
+    // Netztote, Geister  und unsichtbare Magier nicht beruecksichtigen,
+    // nicht (global) angreifbare Monster und nicht-feindliche Magier
+    // von feindlicher Seite entfernen.
+    if ((!interactive(ob) && query_once_interactive(ob)) // Netztote raus
+        || funcall(qp,P_GHOST)
+        || (IS_LEARNING(ob) && funcall(qp,P_INVIS))   // Bin nicht da :-)
+        || (pos[ob]<0 && (funcall(qp,P_NO_GLOBAL_ATTACK) // Nicht angreifen
+                          || funcall(qp,P_NO_ATTACK)
+                          || (IS_LEARNING(ob) && !funcall(is_enemy,ob)))))
+      m_delete(pos,ob);
+  }
+
+  // Reihen aufstellen
+  // Kampfreihe: |   5     4     3     2     1 ||  1     2     3     4     5|
+  // Arrays:     |0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19|
+  //             |<----------- Freunde ------->||<--------- Feinde -------->|
+  b_rows=EMPTY_TEAMARRAY+EMPTY_TEAMARRAY+EMPTY_TEAMARRAY+EMPTY_TEAMARRAY;
+  x=m_indices(pos);
+  for (i=sizeof(x)-1;i>=0;i--) {
+    p1=2*MAX_TEAMROWS-pos[ob=x[i]];
+    pos[ob]=p1;
+    if (p1>=0 && p1<4*MAX_TEAMROWS) {
+      if (random(100)<50)             // Damit gut gemischt wird...
+        b_rows[p1]=b_rows[p1]+({ob}); // zufaellig hinten anfuegen
+      else
+        b_rows[p1]=({ob})+b_rows[p1]; // oder vorne anfuegen.
+    }
+  }
+
+  // Umrechnung von Min, Max auf Reihen
+  // ... -2: -124..-75, -1:-74..-25, 0:-24..24, 1:25..74 2:75..124 ...
+  // Das muss man machen, wenn man keine vernuenftige Rundungsfunktion hat:
+  if (min<0) min=-1*((25-min)/50); else min=(25+min)/50;
+  if (max<0) max=-1*((25-max)/50); else max=(25+max)/50;
+  // Relativ zur Position des Spielers verschieben:
+  p1=pos[pl];min+=p1;max+=p1;
+  // Umrechnung von Breite auf Anzahl:
+  dx=((dx+50)/100)-1;if (dx<0) dx=0;
+
+  x=({({}),({})});
+  for (i=0;i<2*MAX_TEAMROWS;i++)
+    if (i>=min && i<=max)
+      x[1]+=b_rows[i][0..dx]; // Freunde
+  for (i=2*MAX_TEAMROWS+1;i<4*MAX_TEAMROWS;i++)
+    if (i>=min && i<=max)
+      x[0]+=b_rows[i][0..dx]; // Feinde
+
+  return x;
+}
+
+// Findet feindliche oder freundliche Gruppe (oder Summe beider) im
+// angegebenen Bereich.
+varargs object *
+FindDistantGroup(object pl, int who, int dist, int dy, int dx) {
+  mixed *x;
+
+  x=FindDistantGroups(pl,dist,dy,dx);
+  if (who<0)
+    return x[0];
+  else if (who>0)
+    return x[1];
+  return x[0]+x[1];
+}
+
+
+static varargs object
+find_victim(string wen, object pl) {
+  object victim;
+
+  if (!pl) return 0;
+  if (!sizeof(wen)) {
+    if (victim = (object)pl->SelectEnemy())
+      return victim;
+    else
+      return 0;
+  }
+  if (victim = present(wen, environment(pl)))
+    return victim;
+  else if (victim = present(wen, pl))
+    return victim;
+  else
+    return 0;
+}
+varargs object
+FindVictim(string wen, object pl, string msg) {
+  object vic;
+
+  if (!(vic=find_victim(wen,pl)) && msg)
+    write(msg);
+  return vic;
+}
+varargs object
+FindLivingVictim(string wen, object pl, string msg) {
+  object vic;
+
+  if (!(vic=FindVictim(wen,pl,msg)))
+    return 0;
+  if (!living(vic) || vic->QueryProp(P_GHOST)) {
+    printf("%s lebt doch nicht!\n", capitalize((string)vic->name()));
+    return 0;
+  }
+  return vic;
+}
+
+private varargs object
+DoFindEnemyVictim(string wen, object pl, string msg,
+                  mixed func, int min, int max) {
+  object vic;
+  mixed no_attack;
+  int row;
+
+  if (!(vic=FindLivingVictim(wen,pl,msg))) {
+    if ((stringp(wen) && wen!="") || !objectp(pl))
+      return 0;
+    if (pointerp(func)) { // Soll einer DIESER Gegner genommen werden?
+      if (!(vic=(object)pl->SelectEnemy(func))) // Dann daraus auswaehlen
+        return 0;
+    } else {
+      if (!stringp(func))
+        func="SelectEnemy";
+      if (!(vic=(object)call_other(pl,func,0,min,max)))
+        return 0;
+    }
+    func=0; // kein zweites Mal pruefen.
+  }
+  if (no_attack = (mixed)vic->QueryProp(P_NO_ATTACK)) {
+    if (stringp(no_attack))
+      write(no_attack);
+    else
+      write(vic->Name(WER,1)+" laesst sich nicht angreifen.\n");
+    return 0;
+  }
+  if (vic==pl) {
+    write("Du koenntest Dir dabei wehtun.\n");
+    return 0;
+  }
+  if (stringp(func)) {
+    switch(func) {
+    case "SelectNearEnemy":
+      if (pl->PresentPosition()>1) {
+        write("Du stehst nicht in der ersten Kampfreihe.\n");
+        return 0;
+      }
+      if (vic->PresentPosition()>1) {
+        write(vic->Name(WER,1)+" ist in einer hinteren Kampfreihe.\n");
+        return 0;
+      }
+      break;
+    case "SelectFarEnemy":
+      if (row=(int)vic->PresentPosition())
+        row--;
+      if (row>=min && row<=max)
+        break;
+      if (row<min)
+        write(vic->Name(WER,1)+" ist zu nahe.\n");
+      else if (row>max)
+        write(vic->Name(WER,1)+" ist zu weit weg.\n");
+      else
+        write(vic->Name(WER,1)+" ist unerreichbar.\n");
+      return 0;
+    default:;
+    }
+  } else if (pointerp(func)) {
+    if (member(func,vic)<0) {
+      write(vic->Name(WER,1)+" ist unerreichbar.\n");
+      return 0;
+    }
+  }
+
+  if (!pl->IsEnemy(vic)) // War es bisher kein Feind?
+    pl->Kill(vic);       // Dann ist es jetzt einer.
+  return vic;
+}
+
+varargs object
+FindEnemyVictim(string wen, object pl, string msg) {
+  return DoFindEnemyVictim(wen,pl,msg,"SelectEnemy");
+}
+
+// Wie FindEnemyVictim, aber nur im Nahkampf erreichbare Feinde werden
+// gefunden.
+varargs object
+FindNearEnemyVictim(string wen, object pl, string msg) {
+  return DoFindEnemyVictim(wen,pl,msg,"SelectNearEnemy");
+}
+
+// Wie FindEnemyVictim, aber nur Feinde im Bereich der angegebenen Reihen
+// (min,max) werden gefunden.
+varargs object
+FindFarEnemyVictim(string wen, object pl, string msg,
+                   int min, int max) {
+  return DoFindEnemyVictim(wen,pl,msg,"SelectFarEnemy",min,max);
+}
+
+// Wie FindEnemyVictim, findet aber nur Feinde in
+// FindDistantGroup(GEGNER,entfernung,abweichung)
+varargs object
+FindDistantEnemyVictim(string wen, object pl, string msg,
+                       int dist, int dy) {
+  return DoFindEnemyVictim(wen,pl,msg,
+                           FindDistantGroup(pl,-1,dist,dy,10000));
+}
+
+varargs int
+TryGlobalAttackSpell(object caster, mapping sinfo, int suc,
+                     int damage, mixed dt, mixed is_spell,
+                     int dist, int depth, int width) {
+  int i;
+  mixed x,coldam;
+  object *obs,ob;
+
+  if (!suc) suc=random(sinfo[SI_SKILLABILITY]);
+  if (!dt) dt=GetData(SI_SKILLDAMAGE_TYPE,sinfo,caster);
+  if (!is_spell) is_spell=GetData(SI_SPELL,sinfo,caster);
+  if (!dist) dist=GetRandFValueO(SI_DISTANCE,sinfo,caster);
+  if (!depth) depth=GetRandFValueO(SI_DEPTH,sinfo,caster);
+  if (!width) width=GetRandFValueO(SI_WIDTH,sinfo,caster);
+
+  if (!depth && width) depth=width;
+  if (!width && depth) width=depth;
+  if (!mappingp(is_spell)) is_spell=([]);
+  is_spell[SP_GLOBAL_ATTACK]=1;
+
+  x=FindDistantGroups(caster,dist,depth,width);
+  sinfo[SI_NUMBER_ENEMIES]=sizeof(x[0]);
+  sinfo[SI_NUMBER_FRIENDS]=sizeof(x[1]);
+
+  obs=x[0];
+  for (i=sizeof(obs)-1;i>=0;i--)
+    if (objectp(ob=obs[i]) && suc>=ob->SpellDefend(caster,sinfo))
+      TryAttackSpell(ob,(damage?random(damage):
+                         GetRandFValueO(SI_SKILLDAMAGE,sinfo,caster)),
+                     dt,is_spell,caster,sinfo);
+
+  if (!intp(coldam=sinfo[SI_COLLATERAL_DAMAGE]) || !coldam)
+    return 1;
+  obs=x[1];
+  for (i=sizeof(obs)-1;i>=0;i--)
+    if (objectp(ob=obs[i]) && suc>=ob->SpellDefend(caster,sinfo))
+      ob->reduce_hit_points(((damage?random(damage):
+                              GetRandFValueO(SI_SKILLDAMAGE,sinfo,caster))
+                             *coldam)/10);
+  // 10 statt 100 ist Absicht, weil reduce_hit_points schon um Faktor
+  // 10 staerker wirkt als Defend.
+
+  return 1;
+}
diff --git a/std/store.c b/std/store.c
new file mode 100644
index 0000000..3511105
--- /dev/null
+++ b/std/store.c
@@ -0,0 +1,166 @@
+// MorgenGrauen MUDlib
+//
+// store.c -- Das Lager eines Ladens
+//
+// $Id: store.c 7100 2009-02-01 09:24:43Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define ANZ 3
+#include <properties.h>
+#include <bank.h>
+#include <defines.h>
+
+inherit "/std/thing/properties";
+inherit "/std/room/items";
+
+private nosave object *all;
+private nosave mapping all2;
+private nosave object shop;
+private nosave int sum;
+private nosave int store_percent_left;
+
+static int _set_store_percent_left()
+{
+  return store_percent_left=ZENTRALBANK->_query_store_percent_left();
+}
+
+public void _register_shop(object ob)
+{
+  shop=ob;
+}
+
+protected void create()
+{
+  seteuid(getuid());
+  properties::create();
+  items::create();
+  _set_store_percent_left();
+  SetProp(P_MIN_STOCK, 20); // immer eine Reserve fuer unsere Anfaenger...
+}
+
+protected void create_super() {
+      set_next_reset(-1);
+}
+
+public int MayAddObject(object ob)
+{   return 1;   }
+
+protected varargs void remove_multiple(int limit, mixed fun) {}
+
+static int _query_store_consume()
+// da 0 = 20 ist, ist kein default initialisieren im create noetig!!!
+{
+  int consum;
+  consum=100-Query(P_STORE_CONSUME);
+  if (consum<0 || consum >=100) // consum = 100, wenn P_STORE_CONSUM = 0
+    return 70;
+  return consum;
+}
+
+static int _set_store_consume(int consum)     /* 1 <= consum <= 100 */
+{
+  if (consum<1 || consum>100) return -1;
+  return (100-(int)Set(P_STORE_CONSUME, consum));
+}
+
+static int _query_min_stock()
+{
+  int stock;
+  stock=Query(P_MIN_STOCK);
+  if (stock<0) return 0;
+  return stock;
+}
+
+private void update_money() {
+  if (sum) {
+    if (!shop)
+      ZENTRALBANK->PayIn(sum);
+    else
+      shop->_add_money(sum);
+  }
+  sum=0;
+}
+
+protected void RemoveObjectFromStore(object ob) {
+  // Alle Funktionen die ausserhalb aufgerufen werden, werden "gecatcht"
+  catch(sum+=ob->QueryProp(P_VALUE)*store_percent_left/100; publish);
+  catch(ob->remove(); publish);
+  if (ob) destruct(ob);  // Objekt auf jeden Fall zerstoeren
+}
+
+protected void aufraeumen() {
+  int i, size;
+  object ob;
+  string element;
+
+  if (!pointerp(all)) return;
+  if (!mappingp(all2)) all2=([]);
+  size=sizeof(all);
+  for (i=(size<=50 ? 0 : size-50); i<size; i++) {
+    if (!objectp(ob=all[i])) continue;
+    if (object_name(ob)[0..2]=="/d/" || object_name(ob)[0..8]=="/players/")
+      element=BLUE_NAME(ob);
+    else
+      catch(element=ob->short()+BLUE_NAME(ob); publish);
+    if (all2[element]++>ANZ)
+      RemoveObjectFromStore(ob);
+  }
+  if (size<=50) {
+    all=0;  // Speicher freigeben...
+    all2=0;
+    update_money();
+  } else {
+    all=all[0..size-51];
+    call_out(#'aufraeumen,2);
+  }
+}
+
+void reset() {
+  int i, to, stock;
+  mixed *itemlist;
+  
+  items::reset();
+  _set_store_percent_left();
+
+  if (!(all=all_inventory()) || !sizeof(all)) {
+    all=0; // Speicher freigeben
+    return;
+  }
+  if (sizeof(itemlist=QueryProp(P_ITEMS))) {
+    itemlist=filter(itemlist, #'[, 0);
+    all-=itemlist;
+    if (!sizeof(all)) {
+      all=0; // Speicher freigeben
+      return;
+    }
+  }
+
+  i=sizeof(all)-1;
+  to=i*QueryProp(P_STORE_CONSUME)/100;
+  if ( to < (stock=QueryProp(P_MIN_STOCK)) )
+    to=stock;
+  else
+    stock=0;
+
+  // Hinterer Teil des Inventories wird zerstoert, also alle aelteren
+  // und somit vermutlich selten gekaufte Objekte
+  for (;i>=to;i--)
+    RemoveObjectFromStore(all[i]);
+  all2=([]);
+  call_out(#'aufraeumen,random(10));
+  update_money();
+}
+
+//dieser Raum wird sich nie per clean_up() zerstoeren, daher geben wir
+//0 zurueck, damit der Driver clean_up() nicht mehr ruft.
+int clean_up(int refcount) { return 0; }
+
+static int _query_current_money()
+{
+  return sum;
+}
+
diff --git a/std/thing.c b/std/thing.c
new file mode 100644
index 0000000..908b6c1
--- /dev/null
+++ b/std/thing.c
@@ -0,0 +1,45 @@
+// MorgenGrauen MUDlib
+//
+// thing.c -- standard object
+//
+// $Id: thing.c 7804 2011-07-10 20:37:52Z Zesstra $                      
+
+#pragma strict_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+//#pragma no_clone
+
+inherit "/std/thing/properties";
+inherit "/std/thing/light";
+inherit "/std/thing/description";
+inherit "/std/thing/moving";
+inherit "/std/thing/language";
+inherit "/std/thing/commands";
+inherit "/std/thing/restrictions";
+inherit "/std/thing/envchk";
+
+protected void create()
+{
+  seteuid(getuid());
+  properties::create();
+  light::create();
+  commands::create();
+  description::create();
+  restrictions::create();
+  envchk::create();
+  AddId("Ding");
+
+  return;
+}
+
+// wird gerufen, wenn implizit per inherit geladen. In diesem Fall wird kein
+// Reset benoetigt, weil das Objekt nicht konfiguriert ist und nur das
+// Programm wichtig ist.
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// Damit man in ALLEN Standardobjekten ::reset aufrufen kann.
+void reset() {}
+
diff --git a/std/thing/commands.c b/std/thing/commands.c
new file mode 100644
index 0000000..f745b67
--- /dev/null
+++ b/std/thing/commands.c
@@ -0,0 +1,532 @@
+// MorgenGrauen MUDlib
+//
+// thing/commands.c -- thing description
+//
+// $Id: commands.c 9514 2016-02-23 20:33:09Z Gloinson $
+//
+// Aus Regenbogen MUDLib
+// aus Gueldenland/Wunderland MUDlib (Morgengrauen MUDlib)
+//
+// P_COMMANDS data-structure:
+//
+// AddCmd(verb,fun1,1);
+// AddCmd(verb+syn1a|syn1b&syn2a|syn2b|syn2c,fun2,
+//	   error1_notify|error2_notify^error2_write);
+// -->
+// ([verb:({fun1,fun2});					// funs
+//	  ({1,({error1_notify, error2_write^error2_say, 1})});  // flags
+//        ({0,({({syn1a,syn1b}),({syn2a,syn2b,syn2c})})});	// rules
+//        0])							// IDs
+//
+// Rules: ({<Regelsatz fuer fun1>, ({<1. Synonymgruppe>,
+//				     <2. Synonymgruppe, ...}), ...})
+// Flags: ({<Flag fuer fun1>, ({<Fehlermeldung 1. Synonymgruppe>, ... ,
+//				[, Index fuer write anstatt notify_fail]}),
+//	    ... })
+// IDs:   0 oder ({<ID fuer fun1>}) oder ({0, <ID fuer fun2>}) ...
+//
+// IDEA: save no 0s in rules/flags if possible (as in IDs)
+//       (adressing especially old-style-AddCmd-MUDs)
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <moving.h>
+#include <thing/language.h>
+#include <exploration.h>
+
+#define NEED_PROTOTYPES
+#include <thing/description.h>
+#include <thing/commands.h>
+#undef NEED_PROTOTYPES
+
+#ifdef DBG
+#undef DBG
+#endif
+#define DBG(x) printf("Object %O tmpstr=%s\n", explode(object_name(this_object()),"#")[1], x);
+
+private nosave mapping added_cmds;
+
+protected void create()
+{
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}     
+
+varargs void AddCmd(mixed cmd, mixed func, mixed flag, mixed cmdid) {
+ int i,j;
+ closure cl;
+ mixed *rule;
+
+ // potentielle AddCmd mit Regel?
+ if(stringp(cmd)) {
+  // eine Regel? - aufsplitten
+  if((i=member(cmd,'&'))>0) {
+   // ... in Array mit Verknuepfungselementen
+   rule=explode(cmd[(i+1)..],"&");
+   j=sizeof(rule);
+   // ... in Array mit Arrays mit Alternativelementen:
+   // "p|q&r|s" -> ({ ({"p","q"}), ({"r","s"}} })
+   while(j--)
+    rule[j]=explode(rule[j],"|");
+
+   // Regeln von Kommandoverben abschneiden
+   cmd=cmd[0..(i-1)];
+  }
+  // Kommandoverben extrahieren
+  cmd=explode(cmd,"|");
+
+  // Satz von Regeln existiert: Aufsplitten von Fehlermeldungen
+  if(rule)
+   if(stringp(flag)) {
+    mixed *fail;
+    // in einfaches Array mit jeweiligen Fehlermeldungen
+    fail=explode(flag,"|");
+    j=0;
+    i=sizeof(fail);
+    while(j<i) {
+     // write - Fehlermeldung entdeckt - Position ggf. eintragen
+     if(member(fail[j],'^')>=0 && !intp(fail[<1]))
+      fail+=({j});
+     if(member(fail[j],'@')>=0) {
+      int s;
+      flag=regexplode(fail[j], "@WE[A-SU]*[0-9]");
+      s=sizeof(flag);
+      while((s-=2)>0) {
+       int tmpint;
+       tmpint=flag[s][<1]-'1';
+       if(tmpint<0 || tmpint>j)
+        raise_error(sprintf(
+         "AddCmd: error-message %d contains out-of-bounds @WExx-rule.\n",j+1));
+      }
+     }
+     j++;
+    }
+    // "Was?|Wie das?" -> ({"Was?","Wie das?"})
+    // "Was?|Wie das?^|Womit das?|Worauf das?^@WER1 macht was." ->
+    //  ({"Was?",
+    //    "Wie das?^Womit das?",
+    //	  "Worauf das?^@WER1 macht was.",1})
+    flag=sizeof(fail);
+    if(flag && flag<sizeof(rule))
+     raise_error(
+      "AddCmd: number of error-messages does not match number of rules.\n");
+    flag=fail; // ueberschreiben mit den parsefreundlichen Format
+  } else if(flag)
+   raise_error("AddCmd: rules exist but flags are not an error-string.\n");
+ } // end if(stringp(cmd)) ... kein Regelstring vorhanden
+
+ // kein Kommandoarray gewesen noch erzeugt?
+ if(!pointerp(cmd))
+   raise_error("AddCmd: missing string/pointer-parameter for command.\n");
+
+ // Closure aus einem String erzeugen, wenn moeglich und sicher
+ // (function_exists() filtert unnoetigerweise auch reine "static" funs,
+ //  die genaue Pruefung ueber functionlist() kostet jedoch zuviel)
+ if(stringp(func) &&
+    (!extern_call() || function_exists(func,this_object())) &&
+    closurep(cl=symbol_function(func,this_object())))
+  func=cl;
+
+ // jedes einzelne Verb mit seinen Regeln und Funktionen eintragen
+ i=sizeof(cmd);
+ if(!added_cmds) added_cmds=m_allocate(i,4);
+ while(i--) {
+  string str;
+  str=cmd[i];
+  if(!func)
+   if(extern_call()) func=previous_object();
+   else func=this_object();
+  if(!member(added_cmds,str))
+   added_cmds+=([str:allocate(0);allocate(0);allocate(0);0]);
+  // existierendes Verb ergaenzen
+  added_cmds[str,0]+=({func});
+  added_cmds[str,1]+=({flag});
+  added_cmds[str,2]+=({rule});
+  // ggf. id in das ID-Mapping eintragen
+  if(cmdid) {
+   mixed *tmp;
+   j=sizeof((string*)added_cmds[str,0]);
+   tmp=added_cmds[str,3]||allocate(j);
+   if(sizeof(tmp)<j) tmp+=allocate(j-sizeof(tmp));
+   tmp[<1]=cmdid;
+   added_cmds[str,3]=tmp;
+  }
+ }
+}
+
+// Auswertung fuer ein Verb loeschen
+varargs int RemoveCmd(mixed cmd, int del_norule, mixed onlyid) {
+ int ret;
+
+ // Falls Magier das RemoveCmd falsch nutzen (z.B. analog zu AddCmd)
+ // wird das Regelsystem verwirrt. Da das del_norule nur int sein darf,
+ // gibt es hier eine gute Chance den Fehler abwaertskompatibel zu ent-
+ // decken. Damit Spieler den Fehler nicht mitbekommen, wird hier auf
+ // ein raise_error verzichtet, und statt dessen in ein Logfile ge-
+ // schrieben.
+ if (!intp(del_norule))
+ {
+   log_file("REMOVE_CMD",
+     sprintf("\n-- %s --\nIllegal RemoveCommand() in Object [%O]:\n %O\n",
+       dtime(time()), this_object(), cmd));
+   del_norule=0;
+   onlyid=0;
+ }
+ 
+ if(!added_cmds || (!cmd && !del_norule && !onlyid))
+  added_cmds=(mapping)0; 
+ else {
+  int i, j;
+  mixed *rule, *flag, *fun, *delrule, *ids;
+
+  if(stringp(cmd)) {
+   // Regeln entdeckt - Zerlegen (wie AddCmd)
+   if((i=member(cmd,'&'))>0) {
+    delrule=explode(cmd[(i+1)..],"&");
+    j=sizeof(delrule);
+    while(j--)
+     delrule[j]=explode(delrule[j],"|");
+    cmd=cmd[0..(i-1)];
+   }
+   cmd=explode(cmd,"|");
+  } else if(del_norule || onlyid) cmd=m_indices(added_cmds);
+
+  if(!pointerp(cmd))
+   raise_error("RemoveCmd: missing string/pointer-parameter.\n");
+  i=sizeof(cmd);
+
+  while(i--) {
+   // keine Regeln da und Regeln loeschen erlaubt: alles loeschen
+   if(!delrule && !del_norule && !onlyid) m_delete(added_cmds,cmd[i]);
+   else if(m_contains(&fun, &flag, &rule, &ids, added_cmds, cmd[i])) {
+    j=sizeof(fun);
+    while(j--) {
+     int k;
+     // DBG(rule[j]);
+    	// Regeln nicht löschen und Regel?
+     if(!(del_norule && pointerp(rule[j])) &&
+        // nur bestimmte ID löschen und ID passt nicht?
+        !(onlyid && (!pointerp(ids) || sizeof(ids)<=j || ids[j]!=onlyid)) &&
+        // Löschregel existiert und passt nicht auf Regel?
+        !(delrule && (k=sizeof(rule[j]))!=sizeof(delrule))) {
+      // partielles Testen einer Löschregel ...
+      if(delrule) {
+       while(k--)
+        if(!sizeof(rule[j][k]&delrule[k])) break;
+       if(k>=0) continue;
+      }
+      // alles korrekt: Löschen!
+      // (Arraybereich durch leeres Array loeschen)
+      flag[j..j]  = allocate(0);
+      fun[j..j]   = allocate(0);
+      rule[j..j]  = allocate(0);
+      if(ids) {
+       ids[j..j] = allocate(0);
+       if(!sizeof(ids-allocate(1))) ids=(mixed*)0;
+      }
+      ret++;
+     }
+    } // end while(j--) {
+   }
+   // Funktions/Regelliste update oder ggf. Kommando voellig loeschen
+   if(sizeof(rule)) {
+    added_cmds[cmd[i],0]=fun;
+    added_cmds[cmd[i],1]=flag;
+    added_cmds[cmd[i],2]=rule;
+    added_cmds[cmd[i],3]=ids;
+   } else m_delete(added_cmds,cmd[i]);
+  }
+  if(!sizeof(added_cmds)) added_cmds=(mapping)0;
+ }
+ return ret;
+}
+
+// Ausfuehren samt geparstem Inputstring und getriggerten Parserergebnissen
+static int _execute(mixed fun, string str, mixed *parsed) {
+ if(closurep(fun))
+  return ((int)funcall(fun,str,&parsed));
+ if(stringp(fun))
+  return ((int)call_other(this_object(),fun,str,&parsed));
+ return 0;
+}
+
+#define CHECK_PRESENT     1
+#define CHECK_ID          2
+#define CHECK_PUTGETNONE  4
+#define CHECK_PUTGETDROP  8
+#define CHECK_PUTGETTAKE  16
+#define CHECK_PUTGET      (CHECK_PUTGETNONE|CHECK_PUTGETDROP|CHECK_PUTGETTAKE)
+
+// Wert fuer Fehlschlag, Fallback-Wert der benutzten while-- - Schleifen
+#define NOMATCHFOUND      -1
+
+// Regeln fuer ein (nun unwichtiges) Verb triggern
+static int _process_command(string str, string *noparsestr,
+			     mixed fun, mixed flag, mixed rule) {
+ mixed *parsed, *objmatches;
+
+ // eine Regel ... auswerten ...
+ if(pointerp(rule)) {
+  int nrul;
+  parsed=objmatches=allocate(0);
+  int lastmatchpos=NOMATCHFOUND;
+
+  // Abgleichen der gesplitteten Eingabe mit Regeln:
+  // vorwaerts durch die Synonymgruppen
+  int rs=sizeof(rule);
+  while(nrul<rs) {
+   int matchpos;
+   string *synonym;
+   mixed matchstr;
+
+   matchpos=NOMATCHFOUND;
+   matchstr=0;
+
+   // Synonyme extrahieren
+   int nrsynonyms=sizeof(synonym=rule[nrul]);
+
+   // egal wie durch Synonyme bis Match - Abgleich mit Eingabe
+   while(nrsynonyms--) {
+    int tmppos = member(noparsestr,synonym[nrsynonyms]);
+    // ist Synonym im Eingabestring und kommt spaeter als vorheriges Synonym?
+    if(tmppos>=0 && tmppos>lastmatchpos) {
+     // Erfolg: merken der Position im Eingabestring und den matchenden String
+     matchpos=tmppos;
+     matchstr=noparsestr[tmppos];
+     break;
+    }
+   }
+
+   // kein Match durch Synonyme? Pruefe die @-Spezialvariablen.
+   if(matchpos == NOMATCHFOUND) {
+    int check_present;
+    // ist Abpruefen von ID/PRESENT in der Synonymgruppe verlangt
+    // bei present()/find_obs gleich Voraussetzung gueltiger TP mitprufen
+    if(member(synonym,"@ID")>=0) check_present=CHECK_ID;
+    if(this_player()) {
+     if(member(synonym,"@PRESENT")>=0) check_present|=CHECK_PRESENT;
+     else if(member(synonym,"@PUT_GET_NONE")>=0)
+		check_present|=CHECK_PUTGETNONE;
+     else if(member(synonym,"@PUT_GET_TAKE")>=0)
+		check_present|=CHECK_PUTGETDROP;
+     else if(member(synonym,"@PUT_GET_DROP")>=0)
+		check_present|=CHECK_PUTGETTAKE;
+    }
+
+    if(check_present) {
+     // wir fangen hinter dem letzten Match an
+     int q_start=lastmatchpos+1;
+     int r_end=sizeof(noparsestr)-1;
+
+     int range;
+     while((range=r_end-q_start)>=0) {
+      mixed tmpobj;
+
+      // wie weit wollen wir zurückgehen?
+      if(range)
+        if(!(check_present&CHECK_PUTGET))
+          range=range>2?2:range; // 3 Fragmente fuer @ID/@PRESENT (Adverb/Nr)
+        else if(range>4)
+          range=4;               // 5 Fragmente fuer @PUT_XXX 
+
+      // und jetzt die Substrings pruefen
+      while(range>=0 && !matchstr) {
+       string tmpstr;
+
+       // zu pruefenden String aus den Teilen zusammensetzen
+       if(range) tmpstr=implode(noparsestr[q_start..(q_start+range)]," ");
+       else tmpstr=noparsestr[q_start];
+
+       //DBG(tmpstr);
+       if(check_present&CHECK_PRESENT &&			// PRESENT ?
+          ((tmpobj=present(tmpstr,this_player())) ||
+           (tmpobj=present(tmpstr,environment(this_player())))))
+        matchstr=tmpobj;
+       else if(check_present&CHECK_ID && id(tmpstr))	// ID ?
+        matchstr=this_object();
+       else if((check_present&CHECK_PUTGET) &&	// PUT_GET_??? ?
+               (tmpobj=(object*)
+                  this_player()->find_obs(tmpstr,
+                      ([CHECK_PUTGETNONE:PUT_GET_NONE,
+                        CHECK_PUTGETDROP:PUT_GET_TAKE,
+                        CHECK_PUTGETTAKE:PUT_GET_DROP])
+                                        [CHECK_PUTGET&check_present])) &&
+               sizeof(tmpobj)) {
+        if(sizeof(tmpobj)==1) matchstr=tmpobj[0];
+        else {	// Arrays werden zwischengespeichert ...
+         objmatches+=({sizeof(parsed),tmpobj});
+         matchstr=tmpstr;
+        }
+       } else { // dieser Substring hat nicht gematcht
+        // ab weniger als 3 Teilen ist das Nichtmatching eines Substrings mit
+        // beendendem Numeral Kriterium fuer Abbruch ("objekt 2" soll matchen)
+        string numeralcheck;
+        if(range && range<=2 &&
+           sizeof(numeralcheck=noparsestr[q_start+range]) &&
+           to_string(to_int(numeralcheck))==numeralcheck)
+          break;
+
+        // Substringlaenge verkuerzen und weiter
+        range--;
+       }
+      }
+
+      // Match gefunden!
+      if(matchstr) {
+       matchpos=range+q_start;
+       // DBG(matchpos);
+       break;
+      }
+      q_start++;
+     } // end while
+    }
+   }
+
+   // Fehlermeldung fuer diese fehlgeschlagene Synonymgruppe setzen
+   if(matchpos == NOMATCHFOUND) {
+    // Fehlermeldungen und ein Eintrag an der Fehlerstelle?
+    if(pointerp(flag) && sizeof(flag)>nrul) {
+
+     matchstr=flag[nrul];
+
+     if(stringp(matchstr) && sizeof(matchstr)) {
+      if(member(matchstr,'@')>=0) {
+       matchstr=replace_personal(&matchstr,({this_player()})+parsed,1);
+       string stamm=((query_verb()[<1]^'e')?query_verb():query_verb()[0..<2]);
+       matchstr=regreplace(matchstr,"@VERB",capitalize(stamm),1);
+       matchstr=regreplace(matchstr,"@verb",stamm,1);
+      }
+
+      // ist Fehlermeldung ein WRITE?
+      // dann return 1 !
+      if(intp(flag[<1]) && flag[<1]<=nrul) {
+       if(member(matchstr,'^')>=0) {
+        matchstr=explode(matchstr,"^");
+        write(capitalize(break_string(matchstr[0],78,0,1)));
+        if(sizeof(matchstr[1]))
+         say(capitalize(break_string(matchstr[1],78,0,1)),({this_player()}) );
+       } else write(capitalize(break_string(matchstr,78,0,1)));
+       return 1;
+      } else notify_fail(capitalize(break_string(matchstr,78,0,1)));
+     }
+    }
+    return 0;
+   }
+
+   // Updaten der Hilfsvariablen
+   parsed+=({matchstr});
+   lastmatchpos=matchpos;
+   nrul++;
+  } // end while(nrul<rs) ... Erfolg ... ab zum naechsten Regelteil
+ }
+
+ // Arrays der @-Objectmatches in Parameter fuer Methode resubstituieren
+ int objsize;
+ if((objsize=sizeof(objmatches)))
+  while((objsize-=2)>=0)
+   parsed[objmatches[objsize]]=objmatches[objsize+1];
+
+ // erfolgreich Methode gefunden/eine Regel bis zum Ende durchgeparst:
+ return(_execute(&fun,&str,&parsed));
+}
+
+// Auswertung - add_action-Fun 
+public int _cl(string str) {
+ int nindex;
+ string *keys;
+ mixed *flag;
+ // Verb existiert, Kommandos auch, Eintrag fuer Verb gefunden
+ if(mappingp(added_cmds) && query_verb()) {
+  mixed *fun, *rule, *ids;
+
+  // ist das Verb ein Key im Kommandomapping?
+  if(m_contains(&fun, &flag, &rule, &ids, added_cmds, query_verb())) {
+   string *noparsestr;
+   nindex=sizeof(fun);
+   noparsestr=explode((str||"")," ")-({""});
+   // dann matche ungeparsten Input gegen etwaige Regeln
+   // -- nicht aendern: neue Kommandos sollen alte "ueberschreiben" koennen
+   while(nindex--)
+    if(_process_command(&str, noparsestr,
+		       fun[nindex], flag[nindex], rule[nindex]))
+    {
+      GiveEP(EP_CMD, query_verb());
+      return 1;
+    }
+  }
+
+  // keine Regel passte, unscharfe Auswertung auf alle
+  // AddCmdverben ausdehnen
+  keys=m_indices(added_cmds);
+  nindex=sizeof(keys);
+  while(nindex--)
+   if(!strstr(query_verb(),keys[nindex]) &&
+      member((flag=added_cmds[keys[nindex],1]),1)>=0) {
+    int i,ret;
+    i=sizeof(flag);
+    // Reihenfolge nicht aendern !
+    while(i--)
+      if(flag[i]==1) {
+        mixed *exec = added_cmds[keys[nindex],0];
+        if(!pointerp(exec) || sizeof(exec)<=i)
+          catch(raise_error(
+            "AddCmd-Auswertung: CatchAll-Kommando \""+keys[nindex]+"\""
+            " wurde waehrend der Ausfuehrung veraendert oder geloescht. "
+            "Klartext: Das ist schlecht. Sucht ein RemoveCmd? ");
+            publish);
+        else if(_execute(exec[i],str,0)) {
+          GiveEP(EP_CMD, query_verb());
+          return 1;
+        }
+      }
+   }
+ }
+ return 0;
+}
+
+void init() {
+ add_action("_cl","",1);
+}
+
+
+static void _check_copy_commands(string ind, mixed *fun,
+				  mixed *flag, mixed *rule) {
+ if(pointerp(fun)) added_cmds[ind,0]=fun+allocate(0);
+ else added_cmds[ind,0]=({fun});
+ if(pointerp(flag)) added_cmds[ind,1]=flag+allocate(0);
+ else if(flag) added_cmds[ind,1]=({flag});
+ else added_cmds[ind,1]=allocate(sizeof(added_cmds[ind]));
+ if(pointerp(rule)) added_cmds[ind,2]=rule+allocate(0);
+ else added_cmds[ind,2]=allocate(sizeof(added_cmds[ind]));
+
+ if(sizeof(added_cmds[ind])!=sizeof(added_cmds[ind,1]) ||
+    sizeof(added_cmds[ind])!=sizeof(added_cmds[ind,2])) {
+  added_cmds=(mapping)0;
+  raise_error("SetProp(P_COMMANDS): corrupt commands-mapping.\n");
+ }
+}
+
+static mapping _set_commands(mapping commands) {
+ if(!commands) added_cmds=(mapping)0;
+ else if(mappingp(commands)) {
+  added_cmds=m_allocate(sizeof(commands),4);
+  walk_mapping(commands,#'_check_copy_commands);
+ }
+ if (mappingp(added_cmds))
+   return(deep_copy(added_cmds));
+ else
+   return (mapping)0;
+}
+
+static mapping _query_commands() {
+ if (mappingp(added_cmds))
+   return(deep_copy(added_cmds));
+ else
+   return (mapping)0;
+}
+
diff --git a/std/thing/description.c b/std/thing/description.c
new file mode 100644
index 0000000..47bfb0e
--- /dev/null
+++ b/std/thing/description.c
@@ -0,0 +1,851 @@
+// MorgenGrauen MUDlib
+//
+// thing/description.c -- description handling for standard objects
+//
+// $Id: description.c 9561 2016-05-25 19:33:22Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <tls.h>
+#include <thing/description.h>
+#include <thing/material.h>
+#include <thing/lighttypes.h>
+#include <exploration.h>                  // wegen EPMASTER
+#include <class.h>
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <thing/language.h>
+
+#undef NEED_PROTOTYPES
+#include <properties.h>
+
+// Variable um den FP abzuspeichern 
+private nosave mixed *explore;
+
+// Prototypen
+public string short();
+public varargs string long(int mode);
+
+//                       #####################
+//######################## System-Funktionen ############################
+//                       #####################
+
+// Objekt erzeugen
+protected void create()
+{
+  string poid, tpid;
+  object tp;
+
+  SetProp( P_NAME, "Ding" );
+  SetProp( P_SHORT, "Nichts besonderes" );
+  SetProp( P_LONG, 0 );
+
+  Set( P_ADJECTIVES, ({}) );
+  Set( P_NAME_ADJ, ({}) );
+  Set( P_IDS, ({}) );
+  Set( P_CLASS, ({}) );
+
+  Set( P_READ_DETAILS, ([]), F_VALUE);
+  Set( P_READ_DETAILS, SECURED|NOSETMETHOD, F_MODE_AS);
+
+  Set( P_DETAILS, ([]), F_VALUE);
+  Set( P_DETAILS, SECURED|NOSETMETHOD, F_MODE_AS );
+
+  Set( P_SMELLS, ([]), F_VALUE);
+  Set( P_SMELLS, SECURED|NOSETMETHOD, F_MODE_AS );
+
+  Set( P_SOUNDS, ([]), F_VALUE);
+  Set( P_SOUNDS, SECURED|NOSETMETHOD, F_MODE_AS );
+
+  Set( P_TOUCH_DETAILS, ([]), F_VALUE);
+  Set( P_TOUCH_DETAILS, SECURED|NOSETMETHOD, F_MODE_AS );
+
+  // Aenderungen an dieser Prop sind tabu.
+  Set( P_CLONE_TIME, NOSETMETHOD|SECURED, F_MODE_AS );
+
+  // Id des Cloners und des Besitzers kommen nach P_CLONER
+  if (objectp( tp=this_interactive()||this_player() ))
+  {
+    tpid=geteuid(tp);
+    if (!(tpid=geteuid(tp))) tpid=getuid(tp);
+  }
+  else
+    tpid="UNKNOWN";
+
+  if (previous_object())
+  {
+    if (!(poid = geteuid(previous_object())))
+      poid = getuid(previous_object());
+  }
+  else
+    poid="UNKNOWN";
+
+  Set( P_CLONER, (poid != tpid ? poid+":"+tpid: tpid) );
+  Set( P_CLONER, NOSETMETHOD|SECURED, F_MODE_AS );
+
+  // Gibt es FPs ?
+  explore = (mixed *)EPMASTER->QueryExplore();
+
+  return;
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+//                        ##################
+//######################### Forscherpunkte ##############################
+//                        ##################
+
+// FP vergeben
+static void GiveEP( int type, string key )
+{
+  //Abbruch, wenn vergebendes Objekt schon zerstoert ist. ACHTUNG: Auch
+  //diese Abfrage wuerde kein FP vergeben werden, wenn sich das Objekt im
+  //vergebenden Kommando zerstoert, da der Driver call_other() von zerstoerten
+  //Objekten ignoriert!
+  if (!objectp(this_object())) return;
+  if (this_player()) this_player()->countCmds( type, key );
+  
+  if (explore&&!extern_call()&&
+      (explore[0] == type) && (member(explore[1], key) >= 0) )
+    EPMASTER->GiveExplorationPoint(key);
+  return;
+}
+
+// Manche Objekte koennen mit rename_object einen neuen Filenamen bekommen.
+// Danach sollte der EPMASTER neu nach den Details befragt werden.
+void __reload_explore()
+{
+  explore = (mixed *)EPMASTER->QueryExplore();
+  return;
+}
+
+//                         #################
+//########################## ID-Management ##############################
+//                         #################
+
+// Gibt eine ID zurueck, die den Ladenamen, die aktuelle Kurzbeschreibung und
+// die aktuelle Langbeschreibung beruecksichtigt. Diese ID ist ein Hashwert.
+public string description_id() {
+  return hash(TLS_HASH_MD5, load_name() + short() + long());
+}
+
+/* Ids muessen uebergeben werden, da unit machmal mit plural-Ids, */
+/* also anderen als den normalen arbeiten muss. */
+int match_item( string str, string *ids )
+{
+  string *obj,*ads;
+  int len, i;
+  
+  // Parameter falsch? Passt nicht ...
+  if(!str)           return 0;
+  if(!pointerp(ids)) return 0;
+  if(!sizeof(ids))   return 0;
+  
+  // Ist schon so dabei? Na Klasse :-)
+  if(member(ids,str)>-1) return 1;
+  
+  // Keine Adjektive vorhanden ... passt nicht.
+  if (!(ads=QueryProp(P_ADJECTIVES))) return 0;
+  if (!sizeof(ads))                   return 0;
+  
+  // Nur ein Wort? Dann passt es nicht
+  obj=explode(str," ");
+  if (!(len=sizeof(obj)-1)) return 0;
+  
+  // Adjektive stehen am Anfang. Sobald es nicht mehr passt,
+  // muss es das Objektiv sein.
+  while(i<len&&member(ads,obj[i])>-1) i++;
+
+  return (member(ids,implode(obj[i..len]," "))>-1);
+}
+
+// Wird vom Gamedriver aufgerufen (present)
+// Hat dieser Gegenstand die ID str?
+// lvl wird ignoriert
+varargs int id( string str, int lvl ) 
+{ 
+  string str2, tmp;
+  int count;	
+  string|string* ids;
+
+  // Kein Argument? Dann passt es nicht ...
+  if (!stringp(str)) return 0;
+
+  // Keine IDs? Auch nicht gut ...
+  if (!pointerp(ids=QueryProp(P_IDS))) return 0;
+  if (!sizeof(ids)) return 0;
+
+  ids += ({ ("\n" + object_name()),
+            ("\n" + explode(object_name(),"#")[0]) });
+
+  // Id passt? Alles klar :-)
+  if (match_item( str, ids )) return 1;
+
+  // Die id hat eine Zahl drin. Wenn Zahl die Rohid passt,
+  // dann gucken, ob man selber das nte Element ist.
+  if (sscanf( str, "%s %d%s", str2, count, tmp)<2) return 0;
+  if (count<1) return 0;
+  if (sizeof(tmp)) return 0;
+  if (!match_item( str2, ids )) return 0;
+  if (!environment()) return 0;
+  return present(str2,count,environment())==this_object();
+}
+
+// Gleich eine ganze Liste von ids testen
+int match_ids(string *list)
+{
+  string *ids;
+
+  // Ungueltige Parameter? Weg hier ...  
+  if (!pointerp(list)) return 0;
+  if (!pointerp(ids=QueryProp(P_IDS))) return 0;
+
+  ids += ({ ("\n" + object_name()),
+            ("\n" + explode(object_name(),"#")[0]) });
+
+  return sizeof( list & ids );
+}
+
+// ID hinzufuegen
+void AddId( string|string* str )
+{
+  if (stringp(str)) str = ({ str });
+  if (pointerp(str))
+    // Doppelte eliminieren
+    Set( P_IDS, Query(P_IDS, F_VALUE)-str+str, F_VALUE);
+  return;
+}
+
+// ID entfernen
+void RemoveId(string|string* str)
+{
+  if (stringp(str)) str = ({ str });
+  if (pointerp(str))
+    Set(P_IDS,Query(P_IDS, F_VALUE)-str, F_VALUE);
+  return;
+}
+
+// Alle Ids auf einmal setzen
+static string* _set_ids( string* ids )
+{
+    Set( P_IDS,({}));
+    AddId(ids);
+    return Query(P_IDS, F_VALUE);
+}
+
+// Adjektiv hinzufuegen
+void AddAdjective(string|string* str)
+{
+  if (stringp(str)) str = ({ str });
+  if (pointerp(str))
+    // Doppelte eliminieren
+      Set( P_ADJECTIVES, Query(P_ADJECTIVES, F_VALUE)-str+str, F_VALUE );
+  return;
+}
+
+// Adjektiv entfernen
+void RemoveAdjective(string|string* str)
+{
+  if (stringp(str)) str = ({ str });
+  if (pointerp(str))
+    Set( P_ADJECTIVES, Query(P_ADJECTIVES, F_VALUE) - str, F_VALUE );
+  return;
+}
+
+// Alle Adjektive auf einmal setzen
+static string* _set_adjectives(string* adjectives)
+{
+  Set( P_ADJECTIVES,({}), F_VALUE);
+  AddAdjective(adjectives);
+  return Query(P_ADJECTIVES, F_VALUE);
+}
+
+
+//                         ################
+//########################## Namensgebung ###############################
+//                         ################
+
+// Im Fall von mehreren Adjektiven muessen diese mit komma
+// zusamengebaut werden, dazu muss ich das leerzeichen aber erstmal
+// abschneiden und so weiter ...
+private string depointer_adj( string* adj, int casus, int demon ) {
+  string msg;
+  int start;
+  string res,a;
+	adj = map( adj, #'DeclAdj, casus, demon );
+  start = 1;
+  res = "";
+  foreach( a: adj ) {
+    res += (start ? "" : ", ") + a[0..<2];
+    start = 0;
+  }
+  return res + " ";
+}
+
+// Wie lautet der Name des Objekts?
+varargs string name(int casus,int demon)
+{
+  mixed sh, adj;
+  int art, plural;
+
+  art = QueryProp(P_ARTICLE);
+
+  // RAW: direkt zurueckgeben ohne Verarbeitung
+  if (casus == RAW )
+  {
+    if(pointerp(QueryProp(P_NAME)))
+      return QueryProp(P_NAME)[WER];
+    return QueryProp(P_NAME);
+  }
+
+  // Unsichtbar: Etwas
+  if (QueryProp(P_INVIS))
+    return ({ "etwas", "von etwas", "etwas", "etwas" })[casus];
+
+  // Kein Name? Schade ...
+  if (!(sh=QueryProp(P_NAME)) ||
+      (stringp(sh) && !sizeof(sh))) return 0;
+
+  // P_NAME pruefen.
+  if (pointerp(sh) && sizeof(sh) != 4)
+      raise_error(sprintf("Ungueltige Arraygroesse in P_NAME: %d\n",
+            sizeof(sh)));
+
+  // Plural .. Namen verwursten
+  if (plural = QueryProp(P_PLURAL))
+  {
+    // Selber Artikel suchen ist nicht ...
+    if (demon==2||!art) demon = 0;
+
+    // falls P_NAME ein Array mit Faellen enthaelt, den richtigen
+    // extrahieren...
+    if (pointerp(sh)) {
+        sh = sh[casus];
+    }
+    else {
+        // sonst versuchen, zu deklinieren.
+        int last = sh[<1];
+        if (casus == WEM&&last!='s'&&last!='n') sh = sh + "n";
+    }
+
+    // Sind Adjektive vorhanden?
+    if ( pointerp(adj = QueryProp(P_NAME_ADJ)) && sizeof(adj))
+        adj = depointer_adj(adj,casus,demon);
+    if (!stringp(adj)) adj = "";
+
+    return sprintf("%s%s%s%s",QueryArticle(casus,demon,0),adj,
+                   (plural < 2 ? "":(plural < 8 ?
+                    ({ "zwei ", "drei ", "vier ", "fuenf ", "sechs ",
+                       "sieben " })[plural-2] : to_string(plural)+" ")),sh);
+  }
+
+  // Name ist Pointer: Einfach den richtigen auswaehlen
+  if (pointerp(sh))
+    sh = sh[casus];
+
+  // Ansonsten doch wieder verwursten ...
+  else if (stringp(sh))
+  {
+    int last = sh[<1];
+
+    switch(casus)
+    {
+      case WEM:
+      case WEN:
+        if ( art && last=='e'&&QueryProp(P_GENDER) == MALE)
+          sh = (string)sh + "n";
+        break;
+
+      case WESSEN:
+        if( !art )
+        {
+          switch(last)
+          {
+            case 'x':
+            case 's':
+            case 'z':
+              sh = (string)sh + "'";
+              break;
+
+          default:
+            sh = (string)sh + "s";
+          }
+        } 
+        else
+        {
+          switch(last)
+          {
+            default:
+              if (QueryProp(P_GENDER)!=FEMALE)
+                sh=(string)sh+"s";
+              break;
+            case 'e':
+              if (QueryProp(P_GENDER)==MALE)
+                sh=(string)sh+"n";
+            case 'x':
+            case 's':
+            case 'z':
+              break;
+          } /* switch (last) */
+        } /* if( !art ) else */
+    } /* switch( casus ) */
+  } /* pointerp(sh) */
+
+  // RAW? Dann mal zurueck
+  if (demon == RAW) return (string)sh;
+
+  // Selber Artikel suchen ...
+  if (demon==2)
+  {
+    if (art)
+      demon = SuggestArticle();
+    else
+      demon=0; // Kein Artikel: egal (SuggestArticle ist zeitaufwendig)
+  }
+
+  if (pointerp(adj = QueryProp(P_NAME_ADJ)) && sizeof(adj))
+    adj = depointer_adj(adj,casus,demon);
+
+  if (!stringp(adj))  adj = "";
+
+  return QueryArticle( casus, demon )+adj+sh;
+}
+
+// Grossgeschriebenen Namen zurueckgeben
+varargs string Name( int casus, int demon )
+{
+    return capitalize(name( casus, demon )||"");
+}
+
+// Langbeschreibung anzeigen
+public varargs string long(int mode)
+{
+    return process_string( QueryProp(P_LONG) );
+}
+
+// Kurzbeschreibung anzeigen, falls nicht unsichtbar
+public string short()
+{
+  string sh;
+
+  // Unsichtbar? Dann gibts nichts zu sehen ...
+  if (QueryProp(P_INVIS)||!(sh=QueryProp(P_SHORT)))
+    return 0;
+  
+  return process_string(sh)+".\n";
+}
+
+// Namens-Adjektive setzen
+static string* _set_name_adj(string|string* adjectives)
+{
+  if (!adjectives)
+      adjectives=({});
+  // In Array umwandeln
+  else if ( !pointerp(adjectives)) 
+      adjectives = ({ to_string(adjectives) });
+  return Set( P_NAME_ADJ, adjectives );
+}
+
+//                   ############################
+//#################### Details, Gerueche, Laerm #########################
+//                   ############################
+
+// Low-level Funktion zum Ergaenzen von Details, wird von den div. Add...()
+// gerufen, die das richtige Mapping uebergeben.
+// Aendert das Mapping <details> direkt.
+private void _add_details(string|string* keys,
+                          string|string*|mapping|closure descr,
+                          mapping details )
+{
+  if (stringp(keys))
+    details[keys]=descr;
+  else if (pointerp(keys))
+  {
+    foreach(string key : keys)
+      details[lower_case(key)]=descr;
+  }
+  else
+    raise_error("Wrong type to argument 1, expected string|string*.\n");
+}
+
+// Low-level Funktion zum Entfernen von Details, wird von den div. Remove...()
+// gerufen, die das richtige Mapping uebergeben.
+// Aendert das Mapping <details> direkt.
+private void _remove_details(string|string* keys, mapping details )
+{
+  if (stringp(keys))
+    details -= ([keys]);
+  else if (pointerp(keys))
+    details -= mkmapping(keys);
+  else
+    raise_error("Wrong type to argument 1, expected string|string*.\n");
+}
+
+// Detail(s) hinzufuegen
+void AddDetail(string|string* keys, string|string*|mapping|closure descr)
+{
+    int i;
+    mapping details;
+
+    details = Query(P_DETAILS, F_VALUE);
+
+    // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
+    return _add_details(keys, descr, details);
+}
+
+// Detail(s) entfernen
+varargs void RemoveDetail(string|string* keys )
+{
+  // Alle loeschen geht direkt ...
+  if (!keys )
+    Set(P_DETAILS, ([]), F_VALUE);
+  else
+    // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
+    _remove_details(keys, Query(P_DETAILS, F_VALUE));
+}
+
+// SpecialDetail hinzufuegen
+void AddSpecialDetail(string|string* keys, string functionname )
+{
+    closure cl;
+
+    // Absichern! Sonst koennte jeder interne Funktionen aufrufen
+    if (extern_call() &&
+        (geteuid(previous_object()) != geteuid() || process_call()) &&
+        !(object_name(previous_object()) == "/obj/doormaster" &&
+          functionname == "special_detail_doors") )
+      raise_error( "Illegal use of AddSpecialDetail!\n" );
+
+    // Closure generieren
+    if ( !stringp(functionname)||
+         !(cl = symbol_function( functionname, this_object())) )
+      return;
+
+    // Detail hinzufuegen
+    AddDetail( keys, cl );
+    return;
+}
+
+// SpecialDetail(s) entfernen
+void RemoveSpecialDetail(string|string* keys )
+{
+  // RemoveSpecialDetail(0) wuerde sonst ALLE Details (auch die
+  // 'normalen') loeschen
+  if (pointerp(keys)||stringp(keys))
+    RemoveDetail(keys);
+  return;
+}
+
+// Lesbares Detail einfuegen
+void AddReadDetail(string|string* keys,
+                   string|string*|mapping|closure descr )
+{
+  // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
+  return _add_details(keys, descr, Query(P_READ_DETAILS, F_VALUE));
+}
+
+// Lesbare(s) Detail(s) entfernen
+varargs void RemoveReadDetail(string|string* keys )
+{
+  // Alle loeschen geht direkt ...
+  if (!keys )
+    Set(P_READ_DETAILS, ([]), F_VALUE);
+  else
+    // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
+    _remove_details(keys, Query(P_READ_DETAILS, F_VALUE));
+}
+
+// Geraeusch(e) dazufuegen
+void AddSounds(string|string* keys,
+               string|string*|mapping|closure descr )
+{
+  // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
+  return _add_details(keys, descr, Query(P_SOUNDS, F_VALUE));
+}
+
+// Geraeusch(e) entfernen
+varargs void RemoveSounds(string|string* keys )
+{
+  // Alle loeschen geht direkt ...
+  if (!keys )
+    Set(P_SOUNDS, ([]), F_VALUE);
+  else
+    // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
+    _remove_details(keys, Query(P_SOUNDS, F_VALUE));
+}
+
+// Geru(e)ch(e) hinzufuegen
+void AddSmells(string|string* keys,
+               string|string*|mapping|closure descr )
+{
+  // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
+  return _add_details(keys, descr, Query(P_SMELLS, F_VALUE));
+}
+
+// Geru(e)ch(e) entfernen
+varargs void RemoveSmells(string|string* keys )
+{
+  // Alle loeschen geht direkt ...
+  if (!keys )
+    Set(P_SMELLS, ([]), F_VALUE);
+  else
+    // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
+    _remove_details(keys, Query(P_SMELLS, F_VALUE));
+}
+
+// Tastbare(s) Detail(s) hinzufuegen
+void AddTouchDetail(string|string* keys,
+                    string|string*|mapping|closure descr )
+{
+  // _add_details() aendern das Mapping direkt, Set etc. nicht noetig.
+  return _add_details(keys, descr, Query(P_TOUCH_DETAILS, F_VALUE));
+}
+
+// Tastbare(s) Detail(s) entfernen
+varargs void RemoveTouchDetails(string|string* keys )
+{
+  // Alle loeschen geht direkt ...
+  if (!keys )
+    Set(P_TOUCH_DETAILS, ([]), F_VALUE);
+  else
+    // _remove_details() aendern das Mapping direkt, Set etc. nicht noetig.
+    _remove_details(keys, Query(P_TOUCH_DETAILS, F_VALUE));
+}
+
+// Detailinfos fuer Detail key, Spieler hat die Rasse race
+// und benutzt seinen Sinn sense
+varargs string GetDetail(string key, string race, int sense)
+{
+  string|string*|mapping|closure detail;
+  
+  if (stringp(race)) race = lower_case(race);
+  
+  switch(sense)
+  {
+    case SENSE_SMELL: detail=Query(P_SMELLS, F_VALUE)[key];
+                      sense=EP_SMELL; break;
+    case SENSE_SOUND: detail=Query(P_SOUNDS, F_VALUE)[key];
+                      sense=EP_SOUND; break;
+    case SENSE_TOUCH: detail=Query(P_TOUCH_DETAILS, F_VALUE)[key];
+                      sense=EP_TOUCH; break;
+    case SENSE_READ:  detail=Query(P_READ_DETAILS, F_VALUE)[key];
+                      sense=EP_RDET;
+                      break;
+
+    default:          detail=Query(P_DETAILS, F_VALUE)[key];
+                      sense=EP_DETAIL; break;
+  }
+
+  if (!stringp(detail))
+  {
+    if (closurep(detail))
+      detail = (string)funcall(detail,key);
+    else if (mappingp(detail))
+      detail = (string)(detail[race]||detail[0]);
+    else if (pointerp(detail))
+      detail = (string)(detail[random(sizeof(detail))]);
+  }
+
+  // FP vergeben (so vorhanden ;-) )
+  if (detail) GiveEP(sense,key);
+
+  return detail;
+}
+
+// TODO: OBSOLET (Libgrep notwendig)
+void read( string str ) {
+  raise_error("Diese Funktion existiert nicht mehr.\n");
+}
+
+
+//                      ######################
+//####################### Zugriffsfunktionen ############################
+//                      ######################
+
+// Dienen dazu, die direkte Manipulation der Props von aussen zu erschweren.
+
+// Filter, um Specialdetails zu eliminieren
+// erstellt ausserdem ne Kopie vom Mapping. (Wichtig!)
+// Wird vor allem benoetigt, um P_DETAILS in P_DETAILS und 
+// P_SPECIAL_DETAILS zu treffen.
+private int _closures(string x, mapping details, int yes ) 
+{ 
+    return yes ? closurep(details[x]) : !closurep(details[x]); 
+}
+
+static mapping _query_details()
+{
+  return filter_indices(Query(P_DETAILS, F_VALUE), #'_closures,
+      Query(P_DETAILS, F_VALUE),0);
+}
+
+static mapping _query_special_details()
+{
+  return filter_indices(Query(P_DETAILS, F_VALUE),#'_closures,
+      Query(P_DETAILS, F_VALUE),1);
+}
+
+static mapping _query_read_details() {
+  return deep_copy(Query(P_READ_DETAILS, F_VALUE));
+}
+
+static mapping _query_sound_details() {
+  return deep_copy(Query(P_SOUNDS, F_VALUE));
+}
+
+static mapping _query_smell_details() {
+  return deep_copy(Query(P_SMELLS, F_VALUE));
+}
+
+
+//                    ##########################
+//##################### Klassen-Mitgliedschaft ##########################
+//                    ##########################
+
+// Klasse hinzufuegen
+public void AddClass(string|string* str)
+{
+  if (stringp(str)) 
+      str = ({ str });
+  // Aliase aufloesen und implizite Klassen addieren.
+  str = (string*)CLASSDB->AddImplicitClasses(str);
+  // Summe mit alten Klassen bilden und Doppelte eliminieren
+  str = str + Query(P_CLASS, F_VALUE);
+  Set( P_CLASS, m_indices(mkmapping(str)), F_VALUE);
+
+  return;
+}
+
+// Klasse entfernen
+void RemoveClass(string|string* str)
+{
+ if (stringp(str)) 
+      str = ({ str });
+
+  // Aliase aufloesen und implizite Klassen addieren.
+  str = (string*)CLASSDB->AddImplicitClasses(str);
+
+  // Und alle - inklusive impliziter Klassen - entfernen
+  // TODO: Pruefen, ob dies die richtige Entscheidung ist.
+  Set( P_CLASS, Query(P_CLASS, F_VALUE)-str, F_VALUE);
+
+  return;
+}
+
+// Ist das Objekt Mitglied der Klasse str?
+int is_class_member(string|string* str)
+{
+  // Keine Klasse, keine Mitgliedschaft ...
+  if (!str || str=="") 
+      return 0;
+
+  // Es sollte schon ein Array sein
+  if (stringp(str)) 
+      str = ({ str });
+
+  // Klassen und Ids ins Array
+  string *classes=QueryProp(P_CLASS);
+  if (!pointerp(classes))
+    return 0;
+
+  // .. und testen
+  foreach(string class : str)
+    if (member(classes,class) > -1 ) return 1;
+
+  return 0;
+}
+
+// Klasse direkt setzen abfangen
+static string* _set_class(string* classes )
+{
+  Set( P_CLASS, ({}), F_VALUE );
+  AddClass(classes);
+  return QueryProp(P_CLASS);
+}
+
+//                       #####################
+//######################## Material-Handling ############################
+//                       #####################
+
+// Material setzen
+static mapping _set_material(mapping|string|string* mat )
+{
+  mapping mats = ([]);
+  
+  if (mappingp(mat)) 
+  {
+    if( !sizeof(mat) || !widthof(mat) )
+      raise_error(sprintf("P_MATERIAL: expected mapping with at least one "
+        "key and one value, got %.50O\n",mat));
+    else 
+      mats = mat;
+  }
+  else if (stringp(mat))
+    mats[mat]=100;
+  else
+  {
+    int sz = sizeof(mat);
+    // Kommt dann vor, wenn <mat> 0 oder ({}) ist.
+    if ( !sz )
+      raise_error(sprintf("P_MATERIAL: expected string or non-empty "
+        "mapping|string*, got %.50O.\n", mat));
+    mats = mkmapping(mat, allocate(sz, 100/sz));
+  } 
+  return Set( P_MATERIAL, mats, F_VALUE );
+}
+
+// Woraus besteht das Objekt?
+static mapping _query_material()
+{
+  mixed res;
+  
+  if ( !mappingp(res = Query(P_MATERIAL, F_VALUE)) )
+    return ([MAT_MISC:100]);
+  
+  return res;
+}
+
+// Anteil von mat am Objekt?
+int QueryMaterial( string mat )
+{
+  mapping mats;
+  
+  if ( !mappingp(mats = QueryProp(P_MATERIAL)) )
+    return 0;
+  
+  return mats[mat];
+}
+
+// Anteil der Gruppe am Objekt
+int QueryMaterialGroup( string matgroup )
+{
+  return (int)call_other( MATERIALDB, "MaterialGroup",
+                          QueryProp(P_MATERIAL), matgroup );
+}
+
+
+string MaterialList( int casus, mixed idinf )
+{
+  return (string)call_other( MATERIALDB, "ConvMaterialList",
+                             QueryProp(P_MATERIAL), casus, idinf );
+}
+
+static int _set_size(int sz) {
+//Groesse muss > 0 sein, alles andere ist unsinnig! (0 und neg. Groessen
+//haben keine phys. Relevanz und machen u.U. Probleme mit Objekten, die
+//Schaden in Abhaengigkeit der Groesse machen)
+  if (sz>0)
+    Set(P_SIZE,sz,F_VALUE);
+  return(Query(P_SIZE,F_VALUE));
+}
+
+// P_CLONE_TIME
+static int _query_clone_time() { return object_time(); }
+
diff --git a/std/thing/envchk.c b/std/thing/envchk.c
new file mode 100644
index 0000000..f6ce13b
--- /dev/null
+++ b/std/thing/envchk.c
@@ -0,0 +1,36 @@
+// MorgenGrauen MUDlib
+//
+// envchk.c -- verhindern, dass objekte ohne env herumfliegen
+//
+// $Id: thing.c 6283 2007-05-09 21:30:33Z Zesstra $                      
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <moving.h>
+#define NEED_PROTOTYPES
+#include <thing/moving.h>
+
+protected void check_for_environment(string cloner)
+{
+  // Clones, die innerhalb von 10 Sekunden kein Environment haben,
+  // sollen auf -debug scrollen.
+  if ( clonep() && !environment() ) {
+    // mal in den Muellraum bewegen, damit diese Objekte zwar nicht zerstoert
+    // werden, aber zumindest hinterher noch einfach auffindbar sind. (Und
+    // entweder per hand oder automatisch aufgeraeumt werden koennen.)
+    move("/room/muellraum",M_NOCHECK|M_SILENT);
+    if ( !stringp(cloner) || !sizeof(cloner) )
+      cloner = "<Unbekannt>";
+    raise_error("Objekt hat kein Environment. Cloner: ["+cloner+"] ");
+  }
+}
+
+void create()
+{
+	if( clonep() ) 
+    call_out(#'check_for_environment, 3, object_name(previous_object()));
+}
diff --git a/std/thing/language.c b/std/thing/language.c
new file mode 100644
index 0000000..4a3d6f9
--- /dev/null
+++ b/std/thing/language.c
@@ -0,0 +1,199 @@
+// MorgenGrauen MUDlib
+//
+// thing/language.c -- language handling object
+//
+// $Id: language.c 8359 2013-02-09 11:43:39Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <thing/language.h>
+#include <thing/description.h>
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+
+// Kein create()
+void create()
+{
+  return;
+}
+
+//Inversion, damit Defaultwert von 0 einen Artikel ausgibt
+static int _query_article() { return !Query(P_ARTICLE); }
+
+static int _set_article(int art) { return !Set(P_ARTICLE,!art); }
+
+// Bestimmten Artikel bestimmen
+private string query_c_article(int casus)
+{
+  if (QueryProp(P_PLURAL))
+    return ({ "die ", "der ", "den ", "die "})[casus];
+
+  return ({ ({ "das ", "des ", "dem ", "das " }),
+            ({ "der ", "des ", "dem ", "den " }),
+            ({ "die ", "der ", "der ", "die " }) })
+    [(int)QueryProp(P_GENDER)][casus];
+}
+
+// Ende fuer Artikel und Adjektive
+private varargs string query_g_suffix(int gen, int casus, int anzahl)
+{
+  return ({ ({ ({ "","e"}), ({"es","er"}), ({"em","en"}), ({  "","e"}) }),
+	    ({ ({ "","e"}), ({"es","er"}), ({"em","en"}), ({"en","e"}) }),
+	    ({ ({"e","e"}), ({"er","er"}), ({"er","en"}), ({ "e","e"}) }) })
+    [gen][casus][anzahl];
+}
+
+// Artikel vorschlagen: gibt es noch mehr Objekte im inv?
+varargs int SuggestArticle(string id)
+{
+  object ob,*obs;
+
+  // Raum oder Master: Bestimmt.
+  if (!environment()) return 1;
+
+  // Keine id? Dann raus
+  if (!id) return 1;
+
+  // Objekt mit gleichem Namen im env? Dann unbestimmt
+  for ( ob=first_inventory(environment()) ; ob ; ob=next_inventory(ob) )
+    if ( ob!=this_object()&& id==(string)ob->QueryProp(P_NAME) )
+      return 0;
+
+  // sonst bestimmt
+  return 1;
+}
+
+// Artikel bestimmen
+varargs string QueryArticle(int casus, int dem, int force)
+{
+  // Kein Artikel
+  if (!force &&!(QueryProp(P_ARTICLE))) return "";
+
+  // Artikelart aussuchen
+  if ( dem==2 ) dem = SuggestArticle(QueryProp(P_NAME));
+
+  // Bestimmter Artikel
+  if (dem) return query_c_article(casus);
+
+  // Unbestimmter Artikel
+  if (QueryProp(P_PLURAL)) return "";
+  
+  return sprintf("ein%s ",query_g_suffix((int)QueryProp(P_GENDER),casus));
+}
+
+// Besitzanzeiger fuer Objekt bestimmen
+varargs string QueryOwn(int casus)
+{
+  return sprintf("Dein%s",query_g_suffix(QueryProp(P_GENDER),casus));
+}
+
+// Possessivpronomen bestimmen
+varargs string QueryPossPronoun(mixed what, int casus, int number)
+{
+  int gen2;
+
+  // Geschlecht ermitteln
+  gen2 = (objectp(what)?(int)what->QueryProp(P_GENDER):(int)what);
+
+  // Plural ist schoen einfach
+  if (QueryProp(P_PLURAL)) return "ihr"+query_g_suffix(gen2, casus, number);
+
+  return (((((int)QueryProp( P_GENDER ))==FEMALE )? "ihr":"sein")+
+          query_g_suffix(gen2%3, casus, number));
+}
+
+// Pronomen bestimmen nach KNG
+varargs string QueryPronoun(int casus)
+{
+  int gender;
+
+  // Plural ist immer einfach ...
+  if (QueryProp(P_PLURAL)) 
+  {
+     if (casus==WEM) return "ihnen";
+     return "sie";
+  }
+
+  switch(QueryProp(P_GENDER))
+  {
+    case FEMALE:
+      if (casus==WESSEN) return "ihrer";
+      if (casus==WEM) return "ihr";
+      return "sie";
+
+    case MALE:
+      if (casus==WER) return "er";
+      if (casus==WESSEN) return "seiner";
+      if (casus==WEM) return "ihm";
+      return "ihn";
+  }    
+  if (casus==WESSEN) return "seiner";
+  if (casus==WEM) return "ihm";
+  return "es"; //fall-through
+}
+
+// Anrede fuer Spieler bestimmen
+varargs string QueryDu(int casus,int gender,int zahl)
+{
+  return
+    ({ ({({    "du",   "ihr"}),({    "du",   "ihr"}),({    "du",   "ihr"})}),
+       ({({"deiner",  "euer"}),({"deiner",  "euer"}),({"deiner",  "euer"})}),
+       ({({   "dir",  "euch"}),({   "dir",  "euch"}),({   "dir",  "euch"})}),
+       ({({  "dich",  "euch"}),({  "dich",  "euch"}),({  "dich",  "euch"})})
+     })[casus][gender][zahl];
+}
+
+// Welches Geschlecht hat das Objekt als String?
+string QueryGenderString()
+{
+  switch( (int)QueryProp( P_GENDER ))
+  {
+    case MALE:   return "maennlich";
+    case FEMALE: return "weiblich";
+  }
+  return("saechlich"); //fall-through
+}
+
+// Artikel durchdeklinieren nach Kasus, Numerus, Genus und Art
+// des Artikels (demon==bestimmt)
+varargs string DeclAdj(mixed adj, int casus, int demon)
+{
+	// Unregelmaessige Adjektive
+	if( pointerp(adj) ) return adj[casus]+" ";
+
+  // Falscher Typ? Und Tschuess ...
+  if (!stringp(adj)) return "";
+
+  // Plural ist einfach
+  if (QueryProp(P_PLURAL))
+  {
+    // Bestimmt
+    if (demon) return adj+"en ";
+
+    // Unbestimmt
+    return adj + ({ "e ", "er ", "en ", "e " })[casus];
+  }
+  
+  if ( demon )
+    return adj + ({ ({ "e " , "en ", "en ", "e "  }),
+		    ({ "e " , "en ", "en ", "en " }),
+		    ({ "e " , "en ", "en ", "e "  }) })
+      [(int)QueryProp( P_GENDER )][casus];
+  else
+    return adj + ({ ({ "es ", "en ", "en ", "es " }),
+		    ({ "er ", "en ", "en ", "en " }),
+		    ({ "e " , "en ", "en ", "e "  }) })
+      [(int)QueryProp( P_GENDER )][casus];
+}
+
+// P_GENDER setzen. Es sind nur 0,1 und 2 (MALE,FEMALE,NEUTER) erlaubt
+static int _set_gender(int i)
+{
+  if (i>-1&&i<3) return Set(P_GENDER,i);
+  return Query(P_GENDER);
+}
diff --git a/std/thing/light.c b/std/thing/light.c
new file mode 100644
index 0000000..36411b4
--- /dev/null
+++ b/std/thing/light.c
@@ -0,0 +1,62 @@
+// MorgenGrauen MUDlib
+//
+// thing/description.c -- Lichtsystemkomponenten fuer Standardobjekte
+//
+// $Id: description.c 7635 2010-08-19 21:21:30Z Zesstra $
+
+#pragma strict_types
+#pragma save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <thing/language.h>
+#include <hook.h>
+#undef NEED_PROTOTYPES
+
+#include <thing/lighttypes.h>
+#include <properties.h>
+
+//                       #####################
+//######################## System-Funktionen ############################
+//                       #####################
+
+// Objekt erzeugen
+protected void create()
+{
+  Set( P_LIGHT, 0 );
+  Set( P_LIGHT_TYPE, LT_MISC);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}     
+
+// Lichtsy ... sys ...
+static int _query_total_light() { return QueryProp(P_LIGHT); }
+
+static int _set_light( int light )
+{
+    object env = this_object();
+
+    // TODO: Temporaer Lichtlevel in geeignete Wertefenster zwingen.
+    if (light > 100)
+      light = 100;
+    else if (light < -100)
+      light = -100;
+   
+    while ( objectp(env = environment(env)) )
+        // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
+        // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
+        // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
+        // da um jedes bisschen Rechenzeit geht.
+        // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
+        //
+        // Tiamak
+        env->_set_last_content_change();
+    
+    return Set( P_LIGHT, light, F_VALUE);
+}
+
diff --git a/std/thing/moving.c b/std/thing/moving.c
new file mode 100644
index 0000000..780ac41
--- /dev/null
+++ b/std/thing/moving.c
@@ -0,0 +1,201 @@
+// MorgenGrauen MUDlib
+//
+// thing/moving.c -- object moving
+//
+// $Id: moving.c 8892 2014-08-04 19:48:28Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <defines.h>
+#include <moving.h>
+#include <properties.h>
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+
+// Das Objekt bewegen.
+// Rueckgabe 1 ist Erfolg, <=0 ist Fehler
+
+// a) P_NODROP/P_NOGET-Behandlung.
+// b) zum Ueberschreiben
+protected int PreventMove(object dest, object oldenv, int method) {
+  int tmp;
+  
+  // M_NOCHECK? -> Bewegung eh erlaubt (und Rueckgabewert wuerde ignoriert)
+  if ((method&M_NOCHECK)) {
+    // Bei M_NOCHECK zwar Prevent* aufrufen, aber das Resultat ignorieren
+    if (oldenv) oldenv->PreventLeave(this_object(),dest);
+    dest->PreventInsert(this_object());
+
+    return 0; // das wars, rest ist egal.
+  }
+
+  // P_NODROP verhindert weggeben
+  if ((method & (M_PUT|M_GIVE)) && QueryProp(P_NODROP))
+      return ME_CANT_BE_DROPPED;
+
+  // P_NOGET verhindert nehmen
+  if ((method & (M_GET|M_GIVE)) && QueryProp(P_NOGET))
+      return ME_CANT_BE_TAKEN;
+
+  // Gewicht ermitteln
+  if ( !(tmp = (int)QueryProp(P_TOTAL_WEIGHT)) )
+      tmp = (int)QueryProp(P_WEIGHT);
+        
+  // Ist das Objekt nicht zu schwer?
+  if ( (tmp = (int)dest->MayAddWeight(tmp)) < 0) {
+      if ( tmp == -2 ) return ME_TOO_HEAVY_FOR_ENV;
+      return ME_TOO_HEAVY;
+  }
+
+  // Ist das Zielobjekt schon voll?
+  if ( !dest->MayAddObject(this_object()) )
+      return TOO_MANY_OBJECTS;
+
+  // Darf hinausbewegt werden?
+  if ( oldenv && oldenv->PreventLeave(this_object(), dest) )
+      return ME_CANT_LEAVE_ENV;
+
+  // Darf hineinbewegt werden?
+  if ( dest->PreventInsert(this_object()) )
+      return ME_CANT_BE_INSERTED;
+
+  return(0);
+}
+
+// zum Ueberschreiben...
+protected void NotifyMove(object dest, object oldenv, int method) {
+}
+
+varargs int move( object|string dest, int method )
+{
+  object oldenv;
+  int tmp;
+  string fn,vc;
+  mixed sens;
+
+  if (!objectp(dest) && !stringp(dest))
+      raise_error(sprintf("Wrong argument 1 to move(). 'dest' must be a "
+            "string or object! Argument was: %.100O\n",
+            dest));
+
+  // Jetzige Umgebung merken
+  oldenv = environment();
+
+  // Bewegung in Para-Welt-Raeume?
+  // tmp ist die Ziel-Parallelweltnummer
+  if (!environment()||!living(environment())||
+      !intp(tmp =(int)environment()->Query(P_PARA)))
+    tmp=0;
+    
+  // Wenn das Objekt von einem in der Parawelt befindlichen Spieler
+  // oder NPC bewegt wird, sollte es auch wieder in der Parawelt landen.
+  // Um Rechenzeit zu sparen, wird angenommen, dass bei Bewegungen in
+  // das Inv oder Env des Spielers 'dest' als Objekt uebergeben wird,
+  // wohingegen bei Bewegungen in Nachbarraeume (die eigentlich nur
+  // interessant sind) 'dest' als Filename angegeben wird.
+  if (tmp&&!objectp(dest)&&!environment(dest)) {
+      // Falls der Zielraum nicht schon explizit in der Parallelwelt ist,
+      // neuen Zielraum suchen. Aber nur, wenn das Ziel kein Clone ist. Sonst
+      // buggt, wenn man versucht, nach raum#42^para zu bewegen.
+      if (!IS_PARA(dest) && strrstr(dest,"#")==-1) {
+        fn=dest+"^"+tmp;
+
+        // Der Parawelt-Raum wird nur zum Ziel, wenn er a) existiert
+        // und b) auch von Spielern betreten werden darf. Letzteres
+        // Kriterium kann nur mit im Objekt gesetzter Property
+        // P_TESTPLAYER umgangen werden.
+          if ( (find_object(fn) || ((file_size(fn+".c")>0||
+                  (file_size(vc=implode(explode(fn,"/")[0..<2],"/")+
+                  "/virtual_compiler.c")>0 &&
+                  !catch(tmp=(int)call_other(vc,"QueryValidObject",fn);
+                    publish) && tmp>0)) &&
+                  !catch(load_object( fn );publish))) &&
+              (!fn->QueryProp(P_NO_PLAYERS) || QueryProp(P_TESTPLAYER)) )
+           dest = fn;
+      }
+  }
+    
+  // dest auf Objekt normieren.
+  if (stringp(dest))
+      dest = load_object(dest);
+    
+  // testen, ob das Objekt bewegt werden will
+  if (tmp=PreventMove(dest, oldenv, method)) {
+      // auf gueltigen Fehler pruefen, wer weiss, was Magier da evtl.
+      // versehentliche zurueckgeben.
+      if (VALID_MOVE_ERROR(tmp))
+        return(tmp);
+      else
+        return(ME_DONT_WANT_TO_BE_MOVED);
+  }
+
+  // Sensitive Objekte muessen entfernt werden
+  sens = QueryProp(P_SENSITIVE);
+
+  if (sens && environment())
+  {
+    environment()->RemoveSensitiveObject( this_object() );
+    if (!objectp(ME))
+      return ME_WAS_DESTRUCTED;
+  }
+  // Bewegen
+  move_object(ME, dest);
+
+  //falls (sich) das objekt im init() zerstoert (wurde). (Die u. stehenden
+  //Funktionsaufrufe werden dann vom Driver eh groesstenteils ignoriert.)
+  if (!objectp(this_object())) return(ME_WAS_DESTRUCTED);
+
+  // Objekt informieren. ;-)
+  NotifyMove(environment(), oldenv, method);
+
+  // Alte Umgebung informieren
+  if (oldenv) oldenv->NotifyLeave(this_object(), dest);
+
+  // Wenn das Objekt eine Umgebung hat, selbige informieren
+  if (environment()) {
+    if (sens)
+    {
+      environment()->InsertSensitiveObject(this_object(),sens);
+      if (!objectp(ME)) return ME_WAS_DESTRUCTED;
+    }
+    environment()->NotifyInsert(this_object(), oldenv);
+  }
+  //wurde das Objekt vielleicht noch zerstoert?
+  if (!objectp(ME)) return(ME_WAS_DESTRUCTED);
+  
+  //scheint wohl alles ok zu sein.
+  return MOVE_OK;
+}
+
+// Das Objekt zerstoeren
+varargs int remove(int silent)
+{ 
+    if (environment() ) {
+        if(QueryProp(P_SENSITIVE))
+                environment()->RemoveSensitiveObject(this_object());
+        environment()->NotifyRemove(this_object());
+    }
+    if (objectp(this_object()))
+        destruct(this_object());
+    return 1;
+}
+
+public string NotifyDestruct(object caller) {
+  // Lichtsystem mit der aenderung versorgen. :-/
+  foreach(object env : all_environment() || ({})) {
+      // Ja. Man ruft die _set_xxx()-Funktionen eigentlich nicht direkt auf.
+      // Aber das Lichtsystem ist schon *so* rechenintensiv und gerade der
+      // P_LAST_CONTENT_CHANGE-Cache wird *so* oft benoetigt, dass es mir
+      // da um jedes bisschen Rechenzeit geht.
+      // Der Zweck heiligt ja bekanntlich die Mittel. ;-)
+      //
+      // Tiamak
+      env->_set_last_content_change();
+  }
+  return 0;
+}
+
diff --git a/std/thing/properties.c b/std/thing/properties.c
new file mode 100644
index 0000000..06592bf
--- /dev/null
+++ b/std/thing/properties.c
@@ -0,0 +1,326 @@
+// MorgenGrauen MUDlib
+//
+// thing/properties.c -- most general class (property handling)
+//
+// $Id: properties.c 6951 2008-08-09 23:08:31Z Zesstra $
+
+// Properties.c -- Propertyverwaltung
+// (c) 1993 Hate@MorgenGrauen, Mateese@NightFall
+//          Idea and Code      Flames and Destructions
+// -> *grin* thats the point actually :) 
+//
+// Ueberarbeitet von Jof       am 12.06.1994
+// Ueberarbeitet von Mandragon am 11.05.2003
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+
+#include "/sys/thing/properties.h"
+#include "/secure/wizlevels.h"
+
+
+// the mapping where the actual properties are stored. Direct initialization.
+private nosave mapping *prop = ({ ([]), ([]), ([]), ([]) });
+
+// the mapping that is used for saving
+private mapping properties;
+
+// security-flag
+private nosave int closure_call;
+
+// Initialisierung der Props. Kann leider momentan nicht private sein, weil
+// Padreic son komisches Objekt hat, was die Funktion hier ruft.
+// TODO: irgendwann mal private machen.
+// TODO: Da props jetzt einfach bei der Deklaration initlisiert wird,
+// eruebrigt sich diese Funktion eigentlich. Bis auf Padreics Objekt...
+protected void InitializeProperties() {
+  prop = ({ ([]), ([]), ([]), ([]) });
+  return;
+}
+
+// Props nur dann initialisieren, wenn sie es noch nicht sind
+protected void create() {
+  // Blueprints in /std benoetigenkeinen Reset ....
+  if (object_name()=="/std/thing/properties")
+    set_next_reset(-1);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+// Welche externen Objekte duerfen zugreifen?
+nomask private int allowed()
+{
+    if ( (previous_object() && IS_ARCH(getuid(previous_object())) &&
+          this_interactive() && IS_ARCH(this_interactive())) ||
+         (previous_object() && getuid(previous_object()) == ROOTID &&
+          geteuid(previous_object()) == ROOTID) )
+        return 1;
+    return 0;
+}
+
+
+// Set() -- provides direct access to a property, no filters
+public varargs mixed Set( string name, mixed Value, int Type, int extern )
+{
+
+  if (!objectp(this_object()))
+    return 0;
+
+  // Properties, die SECURED oder PROTECTED sind, duerfen nur vom Objekt
+  // selber, EM+ oder ROOT veraendert werden
+  if ((prop[F_MODE][name]&(PROTECTED|SECURED))&&
+      (closure_call||extern||extern_call()) &&
+       previous_object() != this_object() && !allowed())
+    return -1;
+  
+  // Das SECURED-Flag darf bei Properties nicht mehr geloescht werden
+  if ((prop[F_MODE][name]&SECURED)&&
+      (Type==F_MODE||Type==F_MODE_AD)&&(Value & SECURED))
+    return -2;
+  
+  // Setzen duerfen das SECURED-Flag nur das Objekt selber, EM+ oder ROOT
+  if ((Type==F_MODE||Type==F_MODE_AS)&&(Value&SECURED)&&
+      (closure_call ||extern || extern_call()) &&
+       previous_object() != this_object() && !allowed() )
+    return -3;
+
+  switch(Type)
+  {
+    // Je nach Modus Flags veraendern
+    case F_MODE_AS:  prop[F_MODE][name]|= Value;
+                     return prop[F_MODE][name];
+    case F_MODE_AD:  prop[F_MODE][name]&= ~Value;
+                     if (!prop[F_MODE][name]) prop[F_MODE]-=([name]);
+                     return prop[F_MODE][name];
+    case F_MODE:     prop[F_MODE][name]^= Value;
+                     if (!prop[F_MODE][name]) prop[F_MODE]-=([name]);
+                     return prop[F_MODE][name];
+    case F_SET_METHOD:
+      // -1 als Setz-Methode: Nosetmethod setzen
+      if (Value == -1)
+      {
+        prop[F_SET_METHOD]-=([name]);
+        prop[F_MODE][name] |= NOSETMETHOD;
+        return 0;
+      }
+      // Kein break!
+    case F_QUERY_METHOD:
+      // Ungebundene Lambda_Closure? Binden!
+      if (closurep(Value)&&!query_closure_object(Value))
+      {
+        if (extern_call() &&
+             (getuid(previous_object()) != getuid()||
+              geteuid(previous_object()) != geteuid()))
+          return prop[Type][name];
+        
+        Value = bind_lambda( Value,this_object());
+      }
+      // Kein break!
+    default:
+      if (!Value) prop[Type]-=([name]);
+      else prop[Type][name] = Value;
+  }
+
+  // Gesamtwert zurueckgeben
+  return prop[Type][name];
+}
+
+
+// Direktes Auslesen der Property ohne Filter ...
+public varargs mixed Query( string name, int Type )
+{
+    if (pointerp(prop)) return prop[Type][name];
+    return 0;
+}
+
+// Property setzen unter Verwendung evtl. vorhandener Zugriffsfunktionen
+public mixed SetProp( string name, mixed Value )
+{
+  closure func;
+  mixed result;
+   
+  // nur fuer heute
+  if (!objectp(this_object()))
+    return 0;
+
+  // NOSETMETHOD: Darf nicht gesetzt werden
+  if (prop[F_MODE][name] & NOSETMETHOD ) return -1;
+
+  // Set-Method abfragen, so vorhanden
+  if (func=prop[F_SET_METHOD][name])
+  {
+    int flag;
+
+    // Wert als Set-Method? gleich zurueckgeben
+    if (!closurep(func)) return func;
+
+    // An dieser Stelle muss func eine Closure sein. Da Set() ungebundene
+    // Lambdas bindet, kann es auch nur eine gebundene Closure sein und das
+    // Objekt existiert auch noch (sonst waere func == 0).
+
+    // closure_call setzen, falls noch nicht gesetzt
+    if ((flag=closure_call<time()))
+      closure_call = time()+59;
+
+    // Dann mal die Closure aufrufen. Bei Fehler selbige loeschen
+    if (catch(result=funcall(func, Value, name);publish))
+    {
+      prop[F_SET_METHOD]-=([name]);
+    }
+      
+    // Wenn closure_call gesetzt wurde, wieder loeschen
+    if (flag) closure_call = 0;
+
+    // Und zurueckgeben
+    return result; 
+  }
+
+  // _set_*-Methode vorhanden? falls ja, aufrufen.i
+  // TODO: Closurecache einfuehren und Funktionaufruf nur noch machen, wenn es
+  // die _set_* auch gibt?
+  if (call_resolved(&result,this_object(),"_set_"+name,Value ))
+        return result;
+
+  // Letzte Moeglichkeit: Muss eine 'normale' Property sein
+  return Set( name, Value, F_VALUE, extern_call() );
+}
+
+
+// Property auslesen unter Verwendung evtl. vorhandener Zugriffsfunktionen
+public mixed QueryProp( string name )
+{
+  closure func;
+  mixed result;
+ 
+  // nur fuer heute
+  if (!objectp(this_object()))
+    return;
+
+  // Query-Methode vorhanden?
+  if ( func = prop[F_QUERY_METHOD][name] )
+  {
+    int flag;
+
+    // Wert als Query-Method? Gleich zurueckgeben ...
+    if (!closurep(func)) return func;
+ 
+    // An dieser Stelle muss func eine Closure sein. Da Set() ungebundene
+    // Lambdas bindet, kann es auch nur eine gebundene Closure sein und das
+    // Objekt existiert auch noch (sonst waere func == 0).
+   
+    // closure_call setzen, falls noch nicht gesetzt
+    if ((flag=closure_call<time()))
+      closure_call = time()+59;
+    
+    // Dann Mal die Closure aufrufen. Bei Fehler selbige loeschen
+    if (catch(result=funcall(func);publish))
+    {
+      prop[F_QUERY_METHOD]-=([name]);
+    }
+    // Wenn closure_call gesetzt wurde, wieder loeschen    
+    if (flag) closure_call = 0;
+    
+    // Und zurueckgeben
+    return result;
+  }
+  
+  // _query_*-Methode vorhanden? falls ja, aufrufen.
+  // TODO: Closurecache und nur rufen, wenn es _query_* auch gibt?
+  if (call_resolved(&result,this_object(),"_query_"+name))
+    return result;
+  
+  // Hilft alles nichts. Es ist eine 'normale' Property ...
+  return prop[F_VALUE][name];
+}
+
+
+// Das gesamte Property-Mapping auf einen Schlag setzen
+public void SetProperties( mapping props )
+{
+  string *names;
+  int i, j, same_object;
+ 
+  // Kein Mapping? Schlecht ...
+  if(!mappingp(props)) return;
+
+  // Setzen wir selber?
+  same_object = (!closure_call &&
+                 (!extern_call()||previous_object()==this_object()||
+                  allowed()));
+  names = m_indices(props);
+  
+  // Das SECURED-Flag darf nur durch das Objekt selber gesetzt werden:
+  // Alle SECURED-Flags aus props loeschen
+  if (!same_object)
+  {
+    j=sizeof(names);
+    while(j--) props[names[j], F_MODE] &= ~SECURED;
+  }
+
+  j=sizeof(names);
+  while(j--)
+  {
+    // Properties, die schon SECURED oder PROTECTED sind, duerfen
+    // nur vom Objekt selber manipuliert werden
+    if (same_object||!(prop[F_MODE][names[j]] & (PROTECTED|SECURED)) )
+    {
+      i=4;
+      while(i--)
+      {
+        if(props[names[j],i])
+          prop[i][names[j]] = props[names[j], i];
+        else
+          prop[i]-=([names[j]]);
+      }
+    }
+  }
+  return;
+}
+
+
+// Ein Mapping mit allen Properties zurueckgeben
+public mapping QueryProperties()
+{
+  mapping props;
+  int i, j;
+  string *names;
+
+  props = m_allocate( 0, 4 );
+  
+  if (pointerp(prop))
+  {
+    i=4;
+    while(i--)
+    {
+      names = m_indices(prop[i]);
+      j=sizeof(names);
+      while(j--) props[names[j], i] = prop[i][names[j]];
+    }
+  }
+  return props;
+}
+
+// Die Properties als urspruengliches Array zurueckgeben
+public mixed *__query_properties()
+{
+  if ( pointerp(prop) )
+    return(deep_copy(prop));
+  else
+    return ({ ([]),([]),([]),([]) });
+}
+
+
+// mapping Properties setzen zum Speichern (per save_object())
+// Aufruf nur aus simul_efun heraus
+public void  _set_save_data(mixed data) { properties = data; }
+
+// mapping Properties zum Restoren zurueckgeben
+public mixed _get_save_data()           { return properties; }
+
diff --git a/std/thing/restrictions.c b/std/thing/restrictions.c
new file mode 100644
index 0000000..e8d0f83
--- /dev/null
+++ b/std/thing/restrictions.c
@@ -0,0 +1,142 @@
+// MorgenGrauen MUDlib
+//
+// thing/restrictions.c -- Gewicht u.ae. eines Objekts
+//
+// $Id: restrictions.c 6809 2008-03-29 21:54:04Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <defines.h>
+#include <properties.h>
+
+#define NEED_PROTOTYPES 
+#include <thing/properties.h>
+
+// Gewicht ist 1kg, wenn nicht anders bestimmt
+protected void create() 
+{  
+  SetProp(P_WEIGHT, 1000);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}     
+
+// P_TOTAL_WEIGHT bei Nicht-Containern auf P_WEIGHT umleiten
+static int _set_weight(int weight) {
+  return SetProp(P_TOTAL_WEIGHT,
+	  Set(P_WEIGHT, weight, F_VALUE) );
+}
+
+// P_X_ATTR_MOD aendern (Attributaenderungen durch Ausruestung)
+static mapping _set_extern_attributes_modifier(mapping xmod) 
+{
+  mapping res;
+
+  // wenn Muell oder ein leeres Mapping uebergeben wurde, wird die prop
+  // geloescht und ggf. das Objekt im Living abgemeldet.
+  if (!mappingp(xmod)) xmod=([]);
+  
+  if (!sizeof(xmod)) {
+ 
+    res=Set(P_X_ATTR_MOD, xmod);
+  
+    // wenn Prop geloescht wird, kann man das Item auch aus der Liste der
+    // Statmodifizierer des Lebewesens austragen.
+    if (living(environment())) {  
+      environment()->deregister_modifier(ME);
+      environment()->UpdateAttributes();
+    }
+  }
+  else {
+    // ok, Prop wird auf nen interessanten Wert gesetzt. ;-)
+
+    // Damit Insert/RemoveSensitiveObject beim Move aufgerufen wird,
+    // muss P_SENSITIVE gesetzt sein
+    if (!QueryProp(P_SENSITIVE)) SetProp(P_SENSITIVE,({}));
+  
+    if (living(environment()) && 
+        (!mappingp(res=QueryProp(P_X_ATTR_MOD)) || !sizeof(res))) {
+      // Wenn dieses Objekt in einem Living ist und bisher die prop nicht
+      // gesetzt war, muss sich das Objekt noch im Living als Statmodifizierer
+      // registrieren.
+      res=Set(P_X_ATTR_MOD, xmod);
+      environment()->register_modifier(ME);
+      environment()->UpdateAttributes(ME);
+    }
+    else if (living(environment())){
+      // sonst reicht ein einfaches UpdateAttributes() um das Lebewesen zu
+      // informieren.
+      res=Set(P_X_ATTR_MOD, xmod);
+      environment()->UpdateAttributes();
+    }
+    else {
+      // wenn kein lebendes Env, reicht einfaches Setzen.
+      res=Set(P_X_ATTR_MOD,xmod);
+    }
+  }
+
+  return res;
+}
+
+// P_X_HEALTH_MOD aendern (LP/KP-Aenderung durch Ausruestung)
+static mapping _set_extern_health_modifier(mapping xmod) {
+  mapping res;
+
+  // wenn Muell oder ein leeres Mapping uebergeben wurde, wird die prop
+  // geloescht und ggf. das Objekt im Living abgemeldet.
+  if (!mappingp(xmod)) xmod=([]);
+  
+  if (!sizeof(xmod)) {
+ 
+    res=Set(P_X_HEALTH_MOD, xmod);
+  
+    // wenn Prop geloescht wird, kann man das Item auch aus der Liste der
+    // Statmodifizierer des Lebewesens austragen.
+    if (living(environment())) {  
+      environment()->deregister_modifier(ME);
+      environment()->UpdateAttributes();
+    }
+  }
+  else {
+    // ok, Prop wird auf nen interessanten Wert gesetzt. ;-)
+
+    // Damit Insert/RemoveSensitiveObject beim Move aufgerufen wird,
+    // muss P_SENSITIVE gesetzt sein
+    if (!QueryProp(P_SENSITIVE)) SetProp(P_SENSITIVE,({}));
+  
+    if (living(environment()) && 
+        (!mappingp(res=QueryProp(P_X_HEALTH_MOD)) || !sizeof(res))) {
+      // Wenn dieses Objekt in einem Living ist und bisher die prop nicht
+      // gesetzt war, muss sich das Objekt noch im Living als Statmodifizierer
+      // registrieren.
+      res=Set(P_X_HEALTH_MOD, xmod);
+      environment()->register_modifier(ME);
+      environment()->UpdateAttributes(ME);
+    }
+    else if (living(environment())){
+      // sonst reicht ein einfaches UpdateAttributes() um das Lebewesen zu
+      // informieren.
+      res=Set(P_X_HEALTH_MOD, xmod);
+      environment()->UpdateAttributes();
+    }
+    else {
+      // wenn kein lebendes Env, reicht einfaches Setzen.
+      res=Set(P_X_HEALTH_MOD,xmod);
+    }
+  }
+
+  return res;
+
+}
+
+// P_X/M_ATTR_MOD werden nicht beruecksichtigt, da CUMULATIVE_ATTR_LIMIT
+// ueberschritten ist
+void NotifyXMAttrModLimitViolation()
+{
+}
+  
diff --git a/std/thing/sockets.c b/std/thing/sockets.c
new file mode 100644
index 0000000..64d9899
--- /dev/null
+++ b/std/thing/sockets.c
@@ -0,0 +1,254 @@
+// MorgenGrauen MUDlib
+/** @file
+* Implementation fuer Sockelitems.
+* Langbeschreibung... TODO
+* @author Zesstra + Arathorn
+* @date xx.05.2008
+* @version $Id$
+*/
+
+/* Changelog:
+*/
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic
+#pragma range_check
+
+#define NEED_PROTOTYPES
+#include <thing/sockets.h>
+#undef NEED_PROTOTYPES
+#include <defines.h>
+#include <lpctypes.h>
+
+#ifndef DEBUG
+/** Outputs debug message to Maintainer, if Maintainer is logged in. 
+*/
+#define DEBUG(x) __debug_msg__(x)
+#endif
+
+
+private void __debug_msg__(string x) {
+  if (find_player("zesstra"))
+      tell_object(find_player("zesstra"),"SDBG: "+x+"\n");
+}
+
+/** Setzt \a item in einen passenden Sockel ein.
+  Wird vom Handwerker gerufen. Sofern die Pruefung mittels TestSocketItem()
+  erfolgreich ist, werden die Props dieses Objekt durch die im Item
+  gespeicherten in P_SOCKET_PROPS ergaenzt, ggf. P_RESTRICTION geaendert und
+  Name der Blueprint, Name und P_SOCKET_PROPS in P_SOCKETS gespeichert, um
+  spaeter nachvollziehen zu koennen, wie dieses Objekt bereits modifiziert
+  wurde.
+  @attention Am Ende der Funktion wird \a item zerstoert und darf nicht mehr
+  benutzt werden!
+  @param[in] item Objekt, welches in einen passenden Socket eingesetzt werden
+  soll.
+  @return 1, falls Einsetzen erfolgreich, <0 wenn nicht.
+  @sa TestSocketItem(object)
+ */
+public int MountSocketItem(object item) {
+  if (!objectp(item)) return SOCKET_NO_OBJECT;
+  if ((int res=TestSocketItem(item)) != SOCKET_OK)
+    return res;
+  mapping idata=(mapping)item->QueryProp(P_SOCKET_PROPS);
+  // TODO: Spezialbehandlung fuer Props, bei denen das Objekt erhalten bleiben
+  // muss. (z.B. P_DEFEND_FUNC).
+
+  // zu modifizierende Props addieren
+  foreach(string propname, mixed pval: idata) {
+    SetProp(propname, AddToEntity(QueryProp(propname), pval) );
+  }
+  // Restrictions hinzufuegen.
+  SetProp(P_RESTRICTIONS, 
+      AddToEntity(QueryProp(P_RESTRICTIONS), 
+	item->QueryProp(P_SOCKET_RESTR_USE)) );
+
+  // Daten ueber dieses Socketitem abspeichern.
+  mixed sockets=QueryProp(P_SOCKETS)[item->QueryProp(P_SOCKET_TYPE)];
+  // freier Sockel muss existieren (->TestSocketItem())
+  int index=member(sockets, SOCKET_FREE);
+  sockets[index] = idata + (["BLUE_NAME": load_name(item),
+                           "DESCRIPTION": item->name(WER) ]);
+
+  // ggf. Beschreibung aktualisieren?
+  // ggf. Sonderbehandlung fuer Props, bei denen das Objekt noch gebraucht
+  // wird (z.B. P_DEFEND_INFO)
+
+  // Prop schuetzen, sobald Daten drin stehen.
+  Set(P_SOCKETS, PROTECTED|NOSETMETHOD, F_MODE_AS);
+
+  // item entsorgen
+  if (!item->remove(1))
+    raise_error(sprintf("MountSocketItem() in %O: %O hat Ausfuehrung von "
+	  "remove() verweigert.\n", ME, item));
+  return SOCKET_OK;
+}
+
+/** Prueft, ob \a item in einen Sockel eingesetzt werden kann.
+  Prueft, ob es fuer das Item einen passenden Sockel gibt und ob 
+  es die formalen Anforderungen erfuellt, dort eingesetzt zu werden. 
+  Kann vom Handwerker vor dem echten Einsetzen gerufen werden.
+  Wird auch von MountSocketItem(object) gerufen.
+  @param[in] item Objekt, welches auf Eignung zum Einsetzen in einen Sockel
+  getestet werden soll.
+  @return 1, falls ein passender und freier Sockel existiert, 0 sonst.
+  @sa MountSocket(object)
+*/
+public int TestSocketItem(object item) {
+
+  if (!objectp(item)) return SOCKET_NO_OBJECT;
+  
+  // ist es ein Sockelitem und hat es einen gueltigen Typ?
+  mapping idata = (mapping)item->QueryProp(P_SOCKET_PROPS);
+  if (!mappingp(idata) || !sizeof(idata))
+    return SOCKET_NO_DATA;
+  string styp=(string)item->QueryProp(P_SOCKET_TYPE);
+  if (!stringp(styp)
+      || member(VALID_SOCKET_TYPES, styp) == -1)
+    return SOCKET_INVALID_TYPE;
+
+  // Hat dieses Item ueberhaupt Sockel? Und wenn ja, haben wir nen freien
+  // Socke fuer den betreffenden Typ?
+  mapping mysockets = QueryProp(P_SOCKETS);
+  if (!mappingp(mysockets) || !sizeof(mysockets)) 
+    return SOCKET_NO_SOCKETS;
+  if (!member(mysockets, styp)
+      || !member(mysockets[styp], SOCKET_FREE) == -1 )
+    return SOCKET_NONE_AVAILABLE;
+
+  // Handwerker pruefen.
+  // TODO: Soll die Fehlermeldung komplett vom Aufrufer erledigt werden oder
+  // soll es einen Heinweis geben, warum der Handwerker nicht geeignet ist?
+  object craftsman = previous_object();
+  mapping restr = (mapping)item->QueryProp(P_SOCKET_RESTR_MOUNT);
+  if (!objectp(craftsman)
+      || (mappingp(restr)
+	&& "/std/restriction_checker"->check_restrictions(craftsman,
+	  restr)))
+    return SOCKET_NO_EXPERTISE;
+
+  // da P_RESTRICTION nur beim Anziehen/Zuecken geprueft wird, darf man ein
+  // Item nicht in getragenem Zustand modifizieren.
+  if (objectp((object)item->QueryProp(P_WIELDED))
+      || objectp((object)item->QueryProp(P_WORN)))
+    return SOCKET_ITEM_INUSE;
+
+  return SOCKET_OK;
+}
+
+/** Liefert Infos ueber die Sockets (Typ, was drin ist, etc.\ ).
+  Primaer fuer Gilden gedacht.
+*/
+public mixed GetSocketInfo() {
+}
+
+/* **************************** private ************************* */
+
+/** Addiert zwei Variablen, sofern sie kompatibel sind.
+  Lassen sich die beiden Datentypen nicht sinnvoll addieren, erfolgt keine
+  Addition und \a old wird zurueck geliefert (z.B. bei Objekten, Closures).
+  Hierbei erfolgt z.B. bei Mappings eine wertweise Addition, keine blosse
+  Ersetzung der Keys wie bei der normalen Mapping-Addition.
+  @param[in,out] old Datum, zu dem addiert werden soll. Wird bei Mappings
+  geaendert.
+  @param[in] new Datum, welches addierten soll.
+  @return Summe von old und new, falls die Datentypen kompatibel sind. Falls
+  nicht, kann old zurueckgegeben werden. Der Datentyp von 
+  @attention \b Rekursiv! Kann teuer werden.\n
+    Kann (modifizierte) \a old oder \a new oder ganz neues Datum 
+    zurueckliefern, d.h. der zurueckgelieferte Datentyp muss nicht dem
+    Datentyp von old entsprechen. Ebenso erzeugt z.B. die Addition zweier
+    Arrays ein neues Array.
+    Wenn die korrespondierenden Werte nicht den gleichen Datentyp haben,
+    findet u.U. keine Addition statt oder es wird eine Datentyp-Umwandlung
+    durchgefuehrt, z.B. Int + Float == Float.\n
+  */
+private mixed AddToEntity(mixed old, mixed new) {
+
+  // einfachste Faelle:
+  if (!old)
+    return new;
+  else if (!new)
+    return old;
+
+  // Typ bestimmen
+  int oldtype = typeof(old);
+  int newtype = typeof(new);
+  // Variablen gleichen Typs sind einfach.
+  if (oldtype == newtype) {
+    switch (oldtype) {
+      // einige Typen werden stumpf addiert.
+      case T_NUMBER:
+      case T_STRING:
+      case T_FLOAT:
+      case T_POINTER: // TODO: anderes Verhalten?
+	return old+new;
+      // Mappings werden wertweise addiert.
+      case T_MAPPING:
+	// nur wenn die Breite des 2. Summanden <= der des 1. Summanden ist,
+	// lassen sich Mappings hiermit addieren.
+	if (widthof(new) > widthof(old))
+	  return old;
+	// new auf old addieren. Keys werden nicht ersetzt, sondern nach
+	// Moeglichkeit die werte unter den keys jeweils addiert.
+	// map() nur zum Uebergeben aller Keys+Value. AddToMapping aendert
+	// direkt das uebergebene Mapping old.
+	map(new, #'AddToMapping, old); 
+	// alles hier nicht aufgefuehrte kann nicht addiert werden.
+      default: return old;
+    }
+  }
+  // Ints und Floats sind auch noch gut addierbar.
+  else if ((oldtype == T_FLOAT && newtype == T_NUMBER) ||
+           (oldtype == T_NUMBER && newtype == T_FLOAT) )
+    return old + new;
+  // Arrays lassen sich auch gut verwursten (new kann kein Array sein).
+  // Umgekehrter Fall waere auch meoglich, aber der Datentyp von old waere
+  // sehr deutlich unterschiedlich vom urspruenglichen.
+  else if (oldtype == T_POINTER)
+    return old+({new});
+  // Strings und Zeichenliterale (Ints) sind Ansichtssache.
+  else if (oldtype == T_STRING && newtype == T_NUMBER)
+    return sprintf("%s%c",old,new);
+  else if (oldtype == T_NUMBER && newtype == T_STRING)
+    return sprintf("%c%s",old,new);
+
+  // Fall-through
+  return old;
+}
+
+/** Addiert einen Schluessel und seine Werte zu einem Mapping, ggf.\ auf die
+ * bestehenden Werte des Mappings \a old.
+ * Der Key und seine Werte ersetzen bestehende Werte in \a old nicht, wie es
+ * die normale Mapping-Addition des Driver macht. Stattdessen wird versucht,
+ * die neuen Werte auf die entsprechenden alten Werte zu addieren. Falls die
+ * Datentypen zweier Werte inkompatibel sind, erfolgt keine Addition und der
+ * alte Werte hat Bestand.
+  @param[in] key (mixed) Wert, der in das Mapping addiert wird.
+  @param[in] values (mixed)
+  @param[in,out] old Mapping, in das addiert wird.
+  @attention Die Breites des 2. Summanden darf \b nicht groesser sein als die
+  Breite des 1, Summanden! \n
+    Die korrespondieren Werte sollten den gleichen Datentyp haben, sonst
+    findet u.U. keine Addition statt oder es wird eine Datentyp-Umwandlung
+    durchgefuehrt, z.B. Int + Float == Float.\n
+  */
+private void AddToMapping(mixed key, mixed values, mapping old) {
+  if (!mappingp(old)) return;
+  if (!pointerp(values)) values = ({values});
+
+  // wenn der Key noch nicht existiert, ists einfach.
+  if (!member(old, key))
+    m_add(old, key, values...); // flatten operator ... cool. ;-)
+  else {
+    // sonst muessen wir teure handarbeit machen. Insb. weil die Values einen
+    // beliebigen Datentyp haben koennen, u.a. Mappings, weswegen wir wieder
+    // rekursiv AddToEntity() rufen muessen.
+    for(int i=sizeof(values) ; i-- ; ) {
+      old[key,i] = AddToEntity(old[key,i], values[i]);
+    }
+  }
+}
+
diff --git a/std/thing/util.c b/std/thing/util.c
new file mode 100644
index 0000000..d434642
--- /dev/null
+++ b/std/thing/util.c
@@ -0,0 +1,111 @@
+// MorgenGrauen MUDlib
+//
+// thing/util.c -- Utilities
+//
+// $Id: util.c 6366 2007-07-15 21:06:24Z Zesstra $
+
+#define NEED_PROTOTYPES
+#include "/sys/thing/util.h"
+#include "/sys/thing/properties.h"
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+public void ShowPropList(string *props) 
+{
+  int i,j;
+
+  j=sizeof(props);
+
+  for ( i=0; i<j ; i++) 
+  {
+    write("*"+props[i]+": ");
+    PrettyDump(QueryProp(props[i]));
+    write("\n");
+  }
+}
+
+static void PrettyDump(mixed x) 
+{
+  if (pointerp(x)) 
+  {
+    DumpArray(x);
+  }
+  else if (mappingp(x))
+  {
+    DumpMapping(x);
+  }
+  else if (objectp(x)) 
+  {
+    write ("OBJ("+object_name(x)+")");
+  }
+  else if (stringp(x))
+  {
+    write("\""+x+"\"");
+  }
+  else
+  {
+    write (x);
+  }
+}
+
+static void DumpArray(mixed *x) 
+{
+  int i,j;
+
+  write ("({ ");
+  if ( (j=sizeof(x))>0 )
+  {
+    for ( i=0 ; i<(j-1) ; i++) 
+    {
+      PrettyDump(x[i]);
+      write(", ");
+    }
+    PrettyDump(x[i]);
+    write(" ");
+  }
+  write ("})");
+}
+
+static void DumpMapping(mapping x)
+{
+  int   i, c, s;
+  mixed *ind;
+
+  write("([ ");
+
+  if ( (c=sizeof(ind=m_indices(x)))<1 )
+  {
+    write(" ])");
+    return;
+  }
+
+  s=get_type_info(x,1);
+
+  DumpKeyValPair(x, ind[0], s);
+  for ( i=1 ; i<c ; i++ )
+  {
+    write(", ");
+    DumpKeyValPair(x, ind[i], s);
+  }
+  write(" ])");
+}
+
+// Lacht nicht ueber den Namen!!! -Boing
+// Nein, ueber den Namen lache ich nicht ... -Paracelsus
+static void DumpKeyValPair(mapping x, mixed key, int size)
+{ int j, vc;
+
+  PrettyDump(key);
+  write(" : ");
+  PrettyDump(x[key,0]);
+
+  for ( j=1; j<size; j++)
+  {
+    write("; ");
+    PrettyDump(x[key, j]);
+  }
+}
diff --git a/std/trading_price.c b/std/trading_price.c
new file mode 100644
index 0000000..4bfc96c
--- /dev/null
+++ b/std/trading_price.c
@@ -0,0 +1,174 @@
+// MorgenGrauen MUDlib
+//
+// trading_price.c -- Preisverhalten von Laeden und Haendlern
+//
+// $Id: trading_price.c,v 3.3 2002/06/09 19:51:40 Tilly Exp $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <bank.h>
+
+#undef NEED_PROTOTYPES
+#include <properties.h>
+
+// TODO: langfristig zu private aendern?
+nosave int buyfact, shop_percent_left, mymoney, wantto, last;
+private nosave int wantthresh, maxnum;
+
+varargs int SetTradingData(int money, int factor, int maxobs)
+{
+  if (extern_call() && previous_object() != this_object())
+    log_file("ZENTRALBANK",sprintf("INITIALIZE: %O got (%d,%d,%d) from %O (%O)\n",
+				   this_object(), money,factor,maxobs,
+				   previous_object(),this_player()));
+
+  if (money < 1000)
+    money = 1000;
+
+  last=wantto=mymoney=money;
+  wantthresh=wantto/2;
+  if (wantthresh < 5000)
+    wantthresh = 5000;
+
+  if (factor < 100)
+    buyfact = 300;
+  else
+    buyfact = factor;
+  
+  if (maxobs < 1)
+    maxnum=3;
+  else
+    maxnum=maxobs;
+  
+  return mymoney;
+}
+
+void SetBuyFact(int i)
+{
+  buyfact=i*100;
+}
+
+varargs int QueryBuyFact(object client)
+{
+  return buyfact;
+}
+
+int InflateSellValue(int value, int cnt)
+{
+  cnt-=maxnum;
+  if (cnt<0) cnt=0;
+  return value-(value*cnt)/(cnt+25);
+}
+
+int TruncateSellValue(int value)
+{
+  if (value > 500) {
+    value-=500;
+    value = ((1000*value)/(value+1000))+500;
+  }
+  return value;
+}
+
+int ObjectCount(object ob)
+{
+  return objectp(ob); // 0 oder 1
+}
+
+varargs int QueryValue(object ob, int value, object client)
+{
+  value=TruncateSellValue(InflateSellValue(value,ObjectCount(ob)));
+  if (3*value>mymoney)
+  {
+    log_file("LADEN",sprintf("Out of money: %O for %O (%s)\n",this_object(),
+			     this_player(),dtime(time())[5..]));
+    value=mymoney/3;
+  }
+  if (value<0) value=0;
+  
+  /*   Preisverfall von beschaedigtenObjekten gleich hier festlegen  */
+
+  if(value && ob->QueryProp(P_DAMAGED))  
+  {                                      
+    if(ob->QueryProp(P_WC) || ob->QueryProp(P_AC))
+    {
+      value = (6 * (value / 10));
+    }
+    else
+    {
+      value = (4 * (value / 10));
+    }
+  }
+                                          
+  return value;
+}
+
+varargs int QuerySellValue(object ob, object client)
+{
+  return QueryValue(ob,ob->QueryProp(P_VALUE),client);
+}
+
+varargs int QueryBuyValue(mixed ob, object client)
+{
+  // Mit Runden.
+  return (ob->QueryProp(P_VALUE)*QueryBuyFact(client) + 50)/100;
+}
+
+protected void create()
+{
+  SetProp(P_SHOP_PERCENT_LEFT, 0);
+  SetTradingData(50000,300,3);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+void reset()
+{
+  SetProp(P_SHOP_PERCENT_LEFT, 0);
+
+  if (last/3>mymoney)
+    wantto=wantto*110/100;
+  else
+    if (last/2<mymoney)
+      wantto=wantto*85/100;
+  if (wantto<wantthresh) wantto=wantthresh+500;
+  if (wantto>1500000) wantto=1500000;
+  if (wantto<mymoney)
+  {
+    ZENTRALBANK->PayIn(mymoney-wantto);
+    mymoney=wantto;
+  } else
+    mymoney+=ZENTRALBANK->WithDraw(wantto-mymoney);
+  last=mymoney;
+}
+
+void _add_money(int i) {
+  if (extern_call() && previous_object() != this_object())
+    log_file("ZENTRALBANK",sprintf("%s: TRANSFER: %O got %d from %O\n",
+	strftime("%H:%M"), this_object(), i, previous_object()));
+  mymoney+=i;
+}
+
+int _set_shop_percent_left(int dummy) {
+  return shop_percent_left=ZENTRALBANK->_query_shop_percent_left();
+}
+
+int _query_shop_percent_left() {
+  return shop_percent_left;
+}
+
+int _query_current_money() {
+  return mymoney;
+}
+
+int _query_wantto() {
+  return wantto;
+}
+
+
diff --git a/std/transport.c b/std/transport.c
new file mode 100644
index 0000000..fffe1e0
--- /dev/null
+++ b/std/transport.c
@@ -0,0 +1,521 @@
+// MorgenGrauen MUDlib
+//
+// transport.c -- Basisklasse fuer Schiffe und aehnliche Transporter
+//
+// $Id: transport.c 9400 2015-12-11 21:56:14Z Zesstra $
+#pragma strong_types
+//#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/thing/moving";
+inherit "/std/room";
+
+#include <properties.h>
+#include <moving.h>
+#include <defines.h>
+#include <language.h>
+#include <transport.h>
+#include <regexp.h>
+
+/* transport.c
+ * 
+ * Ueberarbeitete und 
+ * erweiterte Version : Tilly@MorgenGrauen, 10.01.02
+ * Basierend auf      : transport.c@SilberLand (Woody@SilberLand), 05.12.99
+ * Basierend auf      : Hates und Rumatas generisches Transport Objekt
+ *                      MorgenGrauen 15.02.93
+ */
+     
+/*
+ ********************* Variablen *********************
+ */
+
+// TODO: langfristig waer ja private schoen...
+//
+// Datenstruktur von 'route' (bei HP_ROOM)
+// 0 ({string ID,      : HP_ROOM
+// 1   string room,    : Dateiname Zielraum
+// 2   int stay,       : Dauer Haltezeit
+// 3   int next,       : Dauer naechste Fahrtzeit
+// 4   string code,    : Haltestellenname fuer QueryArrived
+// 5   mixed dest,     : Haltestellen-IDs fuer HasRoute (reise nach)
+// 6   mixed deststr }): unbenutzt.
+//
+// Datenstruktur von 'route' (bei HP_MSG, HP_FUN)
+// 0 ({string ID,      : HP_MSG
+// 1   string message, : Meldung    oder    string fun : Funktionsname
+// 2   int next})      : Dauer bis zum naechsten Ereignis
+nosave mixed *route;    /* Liste der Haltepunkte. */
+nosave int rpos;        /* Momentane Position in obiger Liste. */
+nosave string roomCode; /* Code des aktuellen Raumes (oder 0). */
+
+/*
+ ********** Management der builtin-properties **********
+ */
+
+string _query_short()
+{ 
+  if (roomCode) return Query(P_SHORT); 
+  return 0; 
+}
+
+mixed _query_transparent()
+{ 
+  if (roomCode) return Query(P_TRANSPARENT);
+  return 0; 
+}
+
+mixed *_set_route(mixed *r) { return route = r; }
+mixed *_query_route()       { return route; }
+
+/*
+ **************** Zugriffsfunktionen ***************
+ */
+
+void Halt()
+{
+  while (remove_call_out( "changeHp" )>-1);
+  while (remove_call_out( "disconnect" )>-1);
+}
+
+// Aktualisiert/Setzt die Route im TravelD, wenn erlaubt (d.h. kein
+// P_NO_TRAVELING)
+private void ReportRoute()
+{
+  if(!QueryProp(P_NO_TRAVELING))
+  {
+    mixed tmp = filter(route, function int (mixed arr)
+        {
+          return arr[0] == HP_ROOM;
+        } );
+    string *route = map(tmp, function string (mixed arr)
+        { return arr[1]; }
+        );
+    TRAVELD->AddRoute(object_name(this_object()),route);
+  }
+}
+
+varargs void Start(int pos)
+{
+  Halt();
+  rpos = (pos >= sizeof(route))?-1:pos-1;
+  call_out("changeHp",0);
+  // Tell TRAVELD our current route
+  ReportRoute();
+}
+
+void SetTravelCmds()
+{
+  if (pointerp(QueryProp(P_LEAVECMDS)))
+     AddCmd(QueryProp(P_LEAVECMDS),"GoOutside");
+  if (pointerp(QueryProp(P_ENTERCMDS)))
+    AddCmd(QueryProp(P_ENTERCMDS),"GoInside");
+  if (pointerp(QueryProp(P_TRAVEL_CMDS)))
+    AddCmd(QueryProp(P_TRAVEL_CMDS),"GoInAndOutside");
+  return;
+}
+
+mixed HasRoute(mixed dest)
+{
+  int i,s,z;
+  string str;
+  object ob;
+  mixed harb;
+  
+  s = sizeof(route);
+
+  for (i = rpos;i <= rpos+s-1;i++)
+  {
+    if (route[i%s][0] == HP_ROOM)
+    {
+      if (member(route[i%s][5],dest) != -1 &&
+          objectp(ob=load_object(route[i%s][1])) &&
+          pointerp(harb=ob->QueryProp(P_HARBOUR)) &&
+          sizeof(harb))
+      {
+        return ({ route[i%s][1], harb[0] });
+      }
+    }
+  }
+  return 0;
+}
+
+public varargs void AddRoute(string room, int stay, int next, 
+    string harbour_desc, string|string* dest_ids, string deststr)
+{
+  // Daten aus dem Zielanleger abfragen.
+  <string|string*>* harbour = room->QueryProp(P_HARBOUR)||({});
+  string* harbour_ids = ({});
+
+  // IDs des Zielanlegers fuer Syntaxpruefung 
+  if ( sizeof(harbour)==2 )
+  {
+    if ( pointerp(harbour[1]) )
+      harbour_ids = harbour[1];
+    else
+      harbour_ids = ({harbour[1]});
+  }
+  
+  // <dest_ids> in ein Array umwandeln, ist dann ggf. leer
+  if ( !dest_ids )
+  {
+    dest_ids = ({});
+  }
+  if ( stringp(dest_ids) )
+  {
+    dest_ids = ({dest_ids});
+  }
+
+  // explizit angegebene IDs stehen jetzt in <dest_ids>, die IDs des 
+  // Zielhafens aus P_HARBOUR werden addiert.
+  dest_ids += harbour_ids;
+
+  // Ist <dest> immer noch leer, versuchen wir, aus <harbour_desc> ein paar
+  // Stichwoerter zu erzeugen, die man als Zielangabe in der Syntax 
+  // "reise nach <ziel>" verwenden kann.
+  if ( !sizeof(dest_ids) )
+  {
+    // Grossgeschriebene Begriffe in <harbour_desc> in <dest> eintragen. Dazu:
+    // 1) <code> erstmal so zerschneiden, dass alle ueblichen Satzzeichen
+    // rausfliegen (es gibt Transporter, die sowas in <harbour_desc> 
+    // uebergeben).
+    dest_ids = regexplode(harbour_desc, "[(),.;:&\+_ ]", 
+                          RE_OMIT_DELIM|RE_GLOBAL);
+    // 2a) So filtern, dass nur grossgeschriebene Woerter uebrig bleiben,
+    //     von 1) uebriggebliebene Leerstrings gleich mit wegwerfen.
+    // 2b) Ergebnis kleinschreiben, damit die Syntaxpruefung damit arbeiten
+    //     kann.
+    dest_ids = map( filter(dest_ids, function int (string key) { 
+                  return (key!="" && key[0]>='A' && key[0]<='Z');
+               }), #'lower_case);
+  }
+  // Sollte <dest> jetzt immer noch leer sein, wurde an allen drei Stellen
+  // nichts oder nur Muell uebergeben.
+  if ( !sizeof(dest_ids) )
+  {
+    raise_error("Transporterfehlfunktion in AddRoute(): Identifikations"
+      "matrix unzureichend definiert. Transporter unbenutzbar fuer "
+      "Spieler. Bitte mindestens eine Ziel-ID via P_HARBOUR oder als "
+      "Argument to AddRoute().");
+  }
+  route += ({ ({ HP_ROOM, room, stay, next, harbour_desc, dest_ids, 
+                 deststr }) });
+}
+
+varargs void AddMsg(string msg, int next) 
+{
+  route += ({ ({ HP_MSG, msg, next }) }); 
+}
+
+void AddFun(string fun, int next) { route += ({ ({ HP_FUN, fun, next }) }); }
+
+string QueryArrived() { return roomCode; }
+
+mixed* QueryPosition()
+{
+  return ({ route[rpos][1],route[(rpos+1)<sizeof(route)?(rpos+1):0][1] });
+}
+
+object* QueryPassengers()
+{
+  return filter(all_inventory(),#'query_once_interactive); 
+}
+
+varargs string *QueryHarbours(int textflag)
+{
+  string *ret = ({});
+
+  foreach( mixed* entry : route )
+  {
+    if ( entry[0] == HP_ROOM )
+    {
+      if ( textflag )
+      {
+        string *hp_ids = entry[1]->QueryProp(P_HARBOUR)[1];
+        if (pointerp(hp_ids) && sizeof(hp_ids))
+        {
+          string *h = map( explode(hp_ids[0]," "), #'capitalize);
+          ret += ({ implode(h, " ") });
+        }
+      }
+      else
+      {
+        ret += ({ entry[1] });
+      }
+    }
+  }
+  return ret;
+}
+
+// beim zerstoeren sollte auch die route und der Transporter aus dem traveld
+// abgemeldet werden.
+public varargs int remove(int silent)
+{
+  TRAVELD->RemoveTransporter(this_object());
+  return ::remove(silent);
+}
+
+void RemoveRoute()
+{
+  Halt();
+  route = ({ });
+  rpos  =   0;
+  TRAVELD->RemoveTransporter(this_object());
+}
+
+varargs int Enter(object who)
+{
+  string *emsg;
+  mixed efail;
+
+  if (!objectp(who)) who = this_player();
+  if (environment(who) == this_object())
+  {
+    tell_object(who,"Da bist Du doch bereits, schon vergessen?\n");
+    return 1;
+  }
+  if (!QueryArrived()) return 0;
+  if (QueryProp(P_MAX_PASSENGERS) && 
+     (sizeof(QueryPassengers()) >= QueryProp(P_MAX_PASSENGERS)))
+  {
+    if (pointerp(efail=QueryProp(P_ENTERFAIL)))
+    {
+      if (sizeof(efail) == 2)
+        tell_room(this_object(),who->Name(WER,2)+" "+process_string(efail[1])+
+                                                   ".\n",({who}));      
+      tell_object(who,process_string(efail[0])+".\n");
+    }
+    else if (stringp(efail))
+       tell_object(who,process_string(efail)+".\n");
+    else if (closurep(efail)) funcall(efail);
+    return 1;
+  }
+  
+  tell_object(who,"Du betrittst "+name(WEN,1)+".\n");
+  if (pointerp(emsg=QueryProp(P_ENTERMSG)) && sizeof(emsg) == 2)
+     return who->move(this_object(),M_GO,"",process_string(emsg[0]),
+                                             process_string(emsg[1]));
+  return who->move(this_object(),M_GO,
+        name(WEN,1),"betritt","kommt herein");
+}
+
+varargs int Leave(object who)
+{
+  string *lmsg;
+  mixed lfail;
+
+  if (!objectp(who)) who = this_player();
+  if (environment(who) != this_object())
+  {
+    if (QueryArrived())
+    {
+      tell_object(who,"Dafuer muesstest Du erstmal dort sein.\n");
+      return 1;
+    }
+    return 0;
+  }
+  if (!QueryArrived())
+  { 
+    if (lfail=QueryProp(P_LEAVEFAIL))
+    {
+      if (pointerp(lfail) && sizeof(lfail))
+      {
+        if (sizeof(lfail) == 2)
+           tell_room(this_object(),who->Name(WER,2)+" "+process_string(
+               lfail[1])+".\n",({who}));
+        tell_object(who,process_string(lfail[0])+".\n");
+      }
+      else if (stringp(lfail))
+        tell_object(who,process_string(lfail)+".\n");
+      else if (closurep(lfail)) funcall(lfail);
+      return 1;
+    }
+    tell_object(who,"Fehler beim Verlassen des Transporters.\n"
+                    "Bitte zustaendigen Magier verstaendigen.\n");
+    return 1;
+  }
+
+  if (who->QueryProp(P_TRAVEL_INFO)) who->SetProp(P_TRAVEL_INFO,0);
+  tell_object(who,"Du verlaesst "+name(WEN,1)+".\n");
+  if (pointerp(lmsg=QueryProp(P_LEAVEMSG)) && sizeof(lmsg) == 2)
+      return who->move(environment(),M_GO,"",process_string(lmsg[0]),
+                                             process_string(lmsg[1]));
+  return who->move(environment(),M_GO,
+           name(WEN,1),"verlaesst","kommt herein");
+}
+
+/*
+ ****************** Internal Functions ******************
+ */
+
+static int GoInside(string str)
+{
+  _notify_fail("Was moechtest Du denn genau?\n");
+  if (stringp(str) && id(str)) {
+      Enter();
+      return 1;
+  }
+  return 0;
+}
+
+static int GoOutside(string str)
+{
+  _notify_fail("Was moechtest Du denn genau?\n");
+  if (stringp(str) && id(str)) {
+      Leave();
+      return 1;
+  }
+  return 0;
+}
+
+static int GoInAndOutside(string str)
+{
+  string to;
+
+  _notify_fail("Was moechtest Du denn genau?\n");
+  if (!sizeof(str)) return 0;  
+  if ((sscanf(str,"auf %s",to) == 1 || sscanf(str,"in %s",to) == 1) && id(to))
+    return Enter(),1;
+  if ((sscanf(str,"von %s",to) == 1 || sscanf(str,"aus %s",to) == 1) && id(to))
+    return Leave(),1;
+  return 0;
+}
+
+protected void create()
+{
+  ::create();
+
+  route = ({});
+
+  SetProp(P_LEAVEFAIL,"Das ist momentan viel zu gefaehrlich");
+  SetProp(P_ENTERFAIL,"Dort ist kein Platz mehr fuer Dich");
+  SetProp(P_TRANSPARENT,1);
+
+  AddId("Transporter");
+  
+  call_out("SetTravelCmds",1);
+}
+
+static varargs void disconnect(int change, int change_time)
+{
+  object room;
+  mixed *departmsg;
+
+  departmsg = QueryProp(P_DEPARTMSG);
+
+  if ((room = environment()) && pointerp(departmsg))
+  {
+    tell_room(this_object(),process_string(departmsg[0]));
+    tell_room(room,process_string(departmsg[1]));
+  }
+
+  roomCode = 0;
+
+  if (change) call_out("changeHp",change_time);
+}
+
+static varargs void connect(string room, string code)
+{
+  mixed *arrivemsg, *t;
+  object *trav, ob;
+  string *trs, *msgs;
+  int i;
+
+  if (roomCode) disconnect();
+  
+  roomCode = code?code:"";
+
+  if (catch(move(room,M_SILENT|M_NOCHECK);publish))
+  {
+    roomCode = 0;
+    return;
+  }
+
+  arrivemsg = QueryProp(P_ARRIVEMSG);
+
+  if (pointerp(arrivemsg))
+  {
+    tell_room(this_object(),process_string(arrivemsg[0]));
+    tell_room(room,process_string(arrivemsg[1]));
+  }
+
+  trav = filter(all_inventory(this_object()),#'living);
+
+  i = sizeof(trav);
+  while(i--)
+  {
+    if (pointerp(t = trav[i]->QueryProp(P_TRAVEL_INFO))&&
+        t[0]==this_object()&&t[2]==room)
+    { 
+      if (trav[i]->InFight())
+        tell_object(trav[i],break_string("Du solltest Deinen Kampf "
+                "schnell beenden,denn eigentlich wolltest Du hier "
+                "aussteigen.",78));
+      else
+        Leave(trav[i]);
+      if (environment(trav[i])!=this_object())
+        trav[i]->SetProp(P_TRAVEL_INFO,0);
+    }
+  }
+  trav = filter(all_inventory(find_object(room))-trav,#'living);
+  i=sizeof(trav);
+  while(i--)
+  {
+    if (objectp(trav[i]) && pointerp(t = trav[i]->QueryProp(P_TRAVEL_INFO))&&
+        t[0] == environment(trav[i]) && t[1] == this_object())
+    {
+      if ( trav[i]->InFight() )
+        tell_object(trav[i],
+           break_string("Du solltest Deinen Kampf schnell beenden, denn "
+                        "eigentlich wolltest Du mit "+name(WEM,1)+
+                        " reisen.",78));
+      else
+        Enter(trav[i]);
+      if (environment(trav[i]) == this_object()) 
+      {
+        t[0] = this_object();
+        trav[i]->SetProp(P_TRAVEL_INFO,t);
+      }
+    }
+  }
+}
+
+// this object never performs any clean-up, the driver should not call it
+// again.
+int clean_up(int arg) { return 0; }
+
+void changeHp()
+{
+  if (++rpos == sizeof(route))
+  {
+    rpos = 0;
+    //TRAVELD die aktuelle Route uebermitteln
+    ReportRoute();
+  }
+  if (route[rpos][0] == HP_MSG)
+  {
+    call_out("changeHp",route[rpos][2]);
+    tell_room(this_object(),route[rpos][1]);
+  }
+  else if (route[rpos][0] == HP_FUN)
+  {
+    call_out("changeHp",route[rpos][2]);
+    call_other(this_object(),route[rpos][1]);
+  }
+  else 
+  {
+    call_out("disconnect",route[rpos][2],1,route[rpos][3]);
+    connect(route[rpos][1],route[rpos][4]);
+  }
+}
+
+void __restart(string funname)
+{
+  if (!funname || funname == "" || (funname != "changeHp" && 
+                                    funname != "disconnect"))
+    return;
+  while(remove_call_out(funname) != -1);
+  call_out(funname,funname == "changeHp"?15:5);
+}
diff --git a/std/tray.c b/std/tray.c
new file mode 100644
index 0000000..df47955
--- /dev/null
+++ b/std/tray.c
@@ -0,0 +1,60 @@
+// MorgenGrauen MUDlib               
+//
+// tray.c -- container standard object
+//
+// $Id: tray.c 7804 2011-07-10 20:37:52Z Zesstra $
+
+// The most general object class. It defines the really basic functions.
+//
+// This object should understand the properties short, long, info, read_msg,
+// value, weight.
+//
+// weight is given in grams, 1 kilogram (kg) = 1000 grams (g) = 1 old weight
+// unit
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/properties";
+inherit "/std/thing/moving";
+inherit "/std/thing/commands";
+inherit "/std/thing/language";
+inherit "/std/container/light";
+inherit "/std/container/restrictions";
+inherit "/std/tray/description";
+inherit "/std/thing/envchk";
+
+#include <properties.h>
+#include <wizlevels.h>
+#include <defines.h>
+
+protected void create()
+{
+  properties::create();
+  commands::create();
+  light::create();
+  description::create();
+  restrictions::create();
+  envchk::create();
+  SetProp(P_TRAY,1);
+  SetProp(P_PREPOSITION, "auf");
+  SetProp(P_SOURCE_PREPOSITION, "von");
+  SetProp(P_DEST_PREPOSITION, "auf");
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+/*
+void init() 
+{
+  commands::init();
+  description::init();
+}
+*/
+
+void reset()
+{}
diff --git a/std/tray/description.c b/std/tray/description.c
new file mode 100644
index 0000000..9079712
--- /dev/null
+++ b/std/tray/description.c
@@ -0,0 +1,36 @@
+// MorgenGrauen MUDlib
+//
+// tray/description.c -- standard description for containers
+//
+// $Id: description.c 6371 2007-07-17 22:46:50Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "std/container/description";
+
+//#define NEED_PROTOTYPES
+
+#include <container.h>
+#include <properties.h>
+#include <defines.h>
+#include <wizlevels.h>
+#include <language.h>
+
+void create()
+{ 
+  ::create();
+  AddId("tray");
+}
+
+varargs string long(int mode) {
+  string descr, inv_descr;
+
+  descr = process_string(QueryProp(P_LONG));
+  inv_descr = make_invlist(PL, all_inventory(ME), mode );
+  if ( inv_descr != "" )
+    descr += "Auf " + QueryPronoun(WEM) + " liegt:\n" + inv_descr;
+  return descr;
+}
diff --git a/std/unit.c b/std/unit.c
new file mode 100644
index 0000000..ae715f2
--- /dev/null
+++ b/std/unit.c
@@ -0,0 +1,771 @@
+// MorgenGrauen MUDlib
+//
+// unit.c -- Basisklasse fuer Mengenobjekte
+//           (neue Version von Padreic)
+//
+// $Id: unit.c 9077 2015-01-16 23:26:00Z Zesstra $
+#pragma strong_types,save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/thing";
+
+#define NEED_PROTOTYPES
+#include <unit.h>
+#undef NEED_PROTOTYPES
+
+#include <properties.h>
+#include <thing/properties.h>
+#include <defines.h>
+#include <language.h>
+#include <moving.h>
+#include <wizlevels.h>
+#include <debug_info.h>
+
+// zum debuggen und extra public
+public string debugmode;
+public string __set_debug(string recv) {return debugmode=recv;}
+#include <living/comm.h>
+#define ZDEBUG(x) if (stringp(debugmode) && find_player(debugmode)) \
+  find_player(debugmode)->ReceiveMsg(x,MT_DEBUG,0,object_name()+": ",ME)
+//#define ZDEBUG(x)
+
+private void _call_DoDecay(object *klone);
+
+private nosave string lastverb;
+private nosave int lastevalnumber;
+
+protected void create()
+{
+  ::create();
+  SetProp(U_IDS,({({}),({})}));
+  SetProp(P_GENDER, 1);
+  SetProp(P_AMOUNT, 1);
+  // Props sollten nicht direkt manipuliert werden, nur ueber Set-Methoden
+  Set(P_UNIT_DECAY_INTERVAL, PROTECTED, F_MODE_AS);
+  Set(P_UNIT_DECAY_QUOTA, PROTECTED, F_MODE_AS);
+} 
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+//// Query und Set Funktionen fuer die Propertys
+
+static void check_leave()
+{  if (Query(P_AMOUNT,F_VALUE)<1) remove(1);  }
+
+static int _query_invis()
+{
+  if (QueryProp(P_AMOUNT) < 1)
+    return 1;
+  return Query(P_INVIS, F_VALUE);
+}
+
+static int _set_amount(int am)
+{
+  if (am<1)
+    call_out("check_leave",1);
+  SetProp(U_REQ, am);
+  return Set(P_AMOUNT,am, F_VALUE);
+}
+
+static int _set_u_req(int ureq)
+{
+  lastverb = query_verb();
+  lastevalnumber = debug_info(DINFO_EVAL_NUMBER);
+  return Set(U_REQ, ureq, F_VALUE);
+}
+
+static int _query_u_req()
+{
+  // Zwei Dinge benutzten, um zu entscheiden, ob U_REQ noch gueltig ist oder
+  // besser auf P_AMOUNT zurueckgesetzt werden sollte: die DINFO_EVAL_NUMBER
+  // und das Kommandoverb. Wenn eines von beiden sich geaendert hat, wird
+  // U_REQ als ungueltig betrachtet. (Und dies bleibt ein uebler Hack.)
+  if (lastevalnumber != debug_info(DINFO_EVAL_NUMBER)
+      || lastverb != query_verb())
+  {
+    return SetProp(U_REQ, Query(P_AMOUNT, F_VALUE));
+  }
+  return Query(U_REQ, F_VALUE);
+}
+
+// Gesamtwert der gerade mit U_REQ ausgewaehlten unitobjekte
+static int _query_value()
+{
+  mixed cpu;
+  cpu=Query(U_CPU);
+  if (intp(cpu)) return QueryProp(U_REQ) * cpu;
+  if (!pointerp(cpu)) return 0;
+  return (QueryProp(U_REQ) * cpu[0])/cpu[1];
+}
+
+static int _set_value(int num) {
+// Setzt den Wert fuer die derzeitige Anzahl
+  SetCoinsPerUnits(num, QueryProp(U_REQ));
+  return QueryProp(P_VALUE);
+}
+
+static int _query_weight()
+{
+  mixed gpu, req;
+  if ((req=QueryProp(U_REQ))<1) return 0;
+  gpu=Query(U_GPU);
+  if (intp(gpu)) return req * gpu;
+  if (!pointerp(gpu)) return 0;
+  return MAX(1,(req*gpu[0])/gpu[1]);
+}
+
+static int _set_weight(int num) {
+// Setzt das Gewicht fuer die derzeitige Anzahl
+    SetGramsPerUnits(num, QueryProp(U_REQ));
+    return QueryProp(P_WEIGHT);
+}
+
+static int _query_total_weight()
+{
+  mixed gpu, amount;
+  if ((amount=Query(P_AMOUNT))<1) return 0;
+  gpu=Query(U_GPU);
+  if (intp(gpu))
+    return amount*(int)gpu;
+  if (!pointerp(gpu)) return 0;
+  return MAX(1,(amount*gpu[0])/gpu[1]);
+}
+
+static int _query_plural()
+{
+  int i;
+  i=QueryProp(U_REQ); // wichtig _nicht_ QueryProp
+  return (i<=1 ? 0 : i);
+}
+
+// gibt string | string* zurueck.
+static string|string* _query_name()
+{
+  if (QueryProp(U_REQ)==1)
+    return "%s%s"+((string *)Query(P_NAME))[0];
+  return "%d %s"+((string *)Query(P_NAME))[1];
+}
+
+// gibt string | string* zurueck.
+static string|string*  _set_name(mixed names)
+{
+  if(!names)
+    return Set(P_NAME,({"",""}));
+  if(stringp(names))
+    return Set(P_NAME,({names,names}));
+  if(pointerp(names))
+    switch(sizeof(names)) {
+      case 0:  return Set(P_NAME,({"",""}));
+      case 1:  if(!stringp(names[0]))
+                 names[0]="";
+               return Set(P_NAME,({names[0],names[0]}));
+      default: if(!stringp(names[0]))
+                 names[0]="";
+               if(!stringp(names[1]))
+                 names[1]="";
+               return Set(P_NAME,names[0..1]);
+    }
+  return QueryProp(P_NAME);
+}
+
+// die Funktionen
+
+void AddSingularId(mixed str)
+{
+  string *ids;
+  if (!pointerp(str))
+    str=({str});
+  ids=Query(U_IDS);
+  Set(U_IDS,({ids[0]+str,ids[1]}));
+}
+
+void RemoveSingularId(mixed str)
+{ 
+  if(stringp(str))
+    str=({str});
+  if(pointerp(str))
+    Set(U_IDS,({Query(U_IDS)[0]-str,Query(U_IDS)[1]}));
+}
+ 
+void AddPluralId(mixed str)
+{
+  string *ids;
+  
+  if (!pointerp(str))
+    str=({str});
+  ids=Query(U_IDS);
+  Set(U_IDS,({ids[0],ids[1]+str}));
+}
+
+void RemovePluralId(mixed str)
+{ 
+  if(stringp(str))
+    str=({str});
+  if(pointerp(str))
+    Set(U_IDS,({Query(U_IDS)[0],Query(U_IDS)[1]-str}));
+}
+
+void SetCoinsPerUnits(int coins,int units)
+{
+  if (coins<0||units<=0) return;
+  if (units==1)
+    Set(U_CPU, coins);
+  else Set(U_CPU,({coins,units}));
+}
+
+void SetGramsPerUnits(int grams, int units)
+{
+  if (grams<0||units<=0) return;
+  if (units==1)
+    Set(U_GPU, grams);
+  else Set(U_GPU,({grams,units}));
+}
+
+int *QueryCoinsPerUnits()
+{
+  mixed cpu;
+  cpu=Query(U_CPU);
+  if (intp(cpu)) return ({ cpu, 1 });
+  return cpu;
+}
+
+int *QueryGramsPerUnits()
+{
+  mixed gpu;
+  gpu=Query(U_GPU);
+  if (intp(gpu)) return ({ gpu, 1 });
+  return gpu;
+}
+
+void AddAmount(int am) {
+  SetProp(P_AMOUNT, am+Query(P_AMOUNT));
+}
+
+int IsUnit() {
+  return 1;
+}
+
+int IsEqual(object ob)
+// haben ob und dieses Objekte die gleiche Blueprint?
+{
+  if (ob==this_object()) return 0; // das ist ZU gleich :)
+  return (BLUE_NAME(ME)==BLUE_NAME(ob));
+}
+
+// ueberschreiben von Standardmethoden
+
+varargs string name(int fall, int demo)
+{
+  int req;
+  mixed n_adj;
+  string adj;
+
+  if ((req=QueryProp(U_REQ))<1) return 0;
+  if (fall!=RAW && 
+      pointerp(n_adj=QueryProp(P_NAME_ADJ)) && sizeof(n_adj) )
+    adj = implode(map(n_adj, #'DeclAdj, fall, demo && req==1), "");
+  else
+    adj = "";
+
+  if (req==1)
+    return sprintf(QueryProp(P_NAME),
+                   (fall==RAW || !QueryProp(P_ARTICLE) 
+                    ? "" : QueryArticle(fall,demo,1)), adj) /* +korrektur */;
+
+  if (fall!=WEM)
+    return sprintf(QueryProp(P_NAME), req, adj);
+  else {
+     int last;
+     last=Query(P_NAME)[1][<1];
+     return sprintf(QueryProp(P_NAME), req, adj)
+                  +(last!='s' && last!='n' ? "n" : "");
+  }
+}
+
+varargs string QueryPronoun(int casus)
+{
+  if (QueryProp(U_REQ)==1)
+    return ::QueryPronoun(casus);
+  switch(casus) {
+    case WESSEN: return "ihrer";
+    case WEM:    return "ihnen";
+    //default:     return "sie";
+  }
+  return("sie"); //fall-through
+}
+
+string short()
+{
+  if (QueryProp(U_REQ)<1 || QueryProp(P_INVIS)) return 0;
+  return capitalize(name(WER,0))+".\n";
+  /*
+  if (req>1) return sprintf(QueryProp(P_NAME), req)+".\n";
+  return capitalize(sprintf(QueryProp(P_NAME), QueryArticle(WER,0,1)))+".\n";
+  */
+}
+
+varargs string long()
+{
+  return (QueryProp(P_LONG) ? process_string(QueryProp(P_LONG)) : "")+
+    sprintf("Du siehst %s.\n",name(WEN));
+}
+
+varargs int id(string str,int lvl)
+{
+
+  string s1,s2,*ids;
+  int i;
+
+  if (!str) return 0;
+  
+  // Wenn kein pos. Amount, hat dieses Objekt keine IDs mehr, damit es nicht
+  // mehr ansprechbar ist.
+  int amount=QueryProp(P_AMOUNT);
+  if (amount < 1)
+    return 0;
+
+  if (::id(str)) {
+    SetProp(U_REQ, Query(P_AMOUNT,F_VALUE));
+    return 1;
+  }
+
+ ids=Query(U_IDS);
+  if (!ids)
+    return 0;
+ 
+  //die casts auf 'mixed' sind absicht. Sonst geht der Driver offenbar davon
+  //aus, dass ids[1] ein String ist. Es ist aber aber ein Array von Strings
+  //und genau das will auch match_item() haben. ;-) Zesstra
+  if (match_item(str,(mixed)ids[1] )) {
+    SetProp(U_REQ, amount);
+    return 1;
+  }
+  if (match_item(str,(mixed)ids[0] )) {
+    SetProp(U_REQ,1);
+    return 1;
+  }
+  if (sscanf(str,"%s %s",s1,s2) && s1[0..3]=="alle" && 
+    match_item(s2,(mixed)ids[1] )) {
+    SetProp(U_REQ, amount);
+    return 1;
+  }
+  if (sscanf(str,"%d %s",i,s1)==2 && i==1 && match_item(s1,(mixed)ids[0] )) {
+    SetProp(U_REQ,1);
+    return 1;
+  }
+  if (sscanf(str,"%d %s",i,s1)==2 && match_item(s1,(mixed)ids[1] ) && 
+      i<=amount && i>0) {
+    SetProp(U_REQ,i);
+    return 1;
+  }
+  return 0;
+}
+
+// Status fuer move... *seufz*
+int _orig_amount, _move_requested;
+
+varargs int move(object|string dest,int method)
+{
+  _orig_amount = QueryProp(P_AMOUNT);
+  // Wenn M_MOVE_ALL gesetzt ist, wird IMMER ALLES bewegt.
+  if (method & M_MOVE_ALL)
+    _move_requested = _orig_amount;
+  else
+    _move_requested = QueryProp(U_REQ);
+
+  ZDEBUG(sprintf("move from %O to %O: amount %d, req %d\n",environment(),dest,_orig_amount,_move_requested));
+  // Wenn nicht alles bewegt werden soll, wird die Menge dieses Objektes
+  // erstmal reduziert und bewegt. Erst nach der Bewegung wird der Rest dann
+  // im alten Environment neu erzeugt.
+  if ( _orig_amount > _move_requested )
+  {
+     // wenn in einem alten Paket das Gewicht noch nicht neu berechnet
+     // wurde muss dies geschehn solange die Muenzen im Paket noch das
+     // volle Gewicht haben...
+     if ( environment() )
+         environment()->query_weight_contents();
+     // ab jetzt nur auf <_move_requested> Einheiten arbeiten
+     Set( P_AMOUNT, _move_requested, F_VALUE);
+     ZDEBUG(sprintf("move: amount set to %d\n",_move_requested));
+  }
+
+  int res = ::move( dest, method );
+
+  if ( res != MOVE_OK )
+  {
+      // Fehlgeschlagene Bewegung ist doof.
+      // ggf. verfruehte Aenderung (P_AMOUNT oben auf <U_REQ> Einheiten
+      // reduziert) rueckgaengig machen
+      Set( P_AMOUNT, _orig_amount, F_VALUE );
+      ZDEBUG(sprintf("move: not OK, amount restored to %d\n",_orig_amount));
+      return res;
+  }
+
+  // TODO: eigentlich sollte es nicht passieren, dass die Menge jetzt negagtiv
+  // ist. Aber bei Geld kann es durch vereinigen mit Geldboerse/Geldkarten
+  // passieren und das ist kein Fehler. Koennte man evtl. mal differenziert
+  // pruefen.
+  int finalamount= QueryProp(P_AMOUNT);
+  /*if ( finalamount < 1 )
+  {
+    ZDEBUG(sprintf("move: zerstoerung, amount %d\n",finalamount));
+    remove(1);
+    return ME_WAS_DESTRUCTED;
+  }
+  */
+  ZDEBUG(sprintf("move: OK, amount %d\n",finalamount));
+  return res;
+}
+
+
+protected void NotifyMove(object dest, object oldenv, int method)
+{
+  // Erst einen evtl. nicht zu bewegenden Rest im alten Environment wieder erzeugen.
+  if (oldenv && _orig_amount > _move_requested)
+  {
+      if ( oldenv == dest )
+      {
+          // Objekt wurde nur an den Anfang des inventory bewegt, sonst nichts.
+          // ggf. verfruehte Aenderung (oben auf <req> Einheiten reduziert)
+          // rueckgaengig machen
+          ZDEBUG(sprintf("move: same env, amount restored to %d\n",_orig_amount));
+          Set( P_AMOUNT, _orig_amount, F_VALUE );
+      }
+      else
+      {
+        // verbleibende Einheiten in einem neuen Objekte wieder zurueck ins
+        // alte Environment zurueck bewgen
+          object temp;
+          temp = clone_object(BLUE_NAME(ME));
+          temp->SetProp( P_AMOUNT, _orig_amount-_move_requested );
+          temp->move( oldenv, M_NOCHECK );
+          ZDEBUG(sprintf("move: Restmenge im alten Env erzeugt, amount %d\n",_orig_amount-_move_requested));
+      }
+  }
+
+  // Und jetzt ggf. mit den anderen Units gleichen Typs im neuen Environment
+  // vereinigen.
+  foreach(object item: all_inventory(environment()))
+  {
+      if ( IS_EQUAL(item) )
+      {
+        // Es ist ein Feature, dass auch mit Objekten mit negativen Amount
+        // vereinigt wird.
+        ZDEBUG(sprintf("move: vereinige mit %O (%d)\n",
+               item,item->QueryProp(P_AMOUNT)));
+        int mergedamount = QueryProp(P_AMOUNT) + item->QueryProp(P_AMOUNT);
+        item->Set( P_AMOUNT, 0, F_VALUE);
+        item->remove(1);
+
+        SetProp( P_AMOUNT, mergedamount);
+        // U_REQ ist jetzt auch zurueckgesetzt.
+
+        ZDEBUG(sprintf("move: nach vereinigung, amount %d\n",mergedamount));
+      }
+  }
+
+  // wenn man in frisch geclonten Units die noch kein environment haben
+  // per Hand U_REQ auf einen Wert != P_AMOUNT setzt, so gehen die
+  // restlichen Einheiten  verloren (es gibt kein Environment, in das man die
+  // Resteinheiten zurueck bewegen koennte).
+
+  // Und jetzt mal Decaykrams machen...
+
+  // wenn NO_DECAY_UNTIL_MOVE an ist und dieses ein Move ist, was von einem
+  // Env in ein anderes Env geht, loeschen. Nicht geloescht wird
+  // hingegen, wenn das move() in das allererste Env erfolgt (nach dem Clonen)
+  if ( (QueryProp(P_UNIT_DECAY_FLAGS) & NO_DECAY_UNTIL_MOVE)
+      && objectp(dest) && objectp(oldenv) && dest != oldenv)
+    SetProp(P_UNIT_DECAY_FLAGS, 
+        QueryProp(P_UNIT_DECAY_FLAGS) & ~NO_DECAY_UNTIL_MOVE );
+  ::NotifyMove(dest, oldenv, method);
+}
+
+
+void reset() {
+  if (!clonep(ME)) {
+    // Blueprint ist Master fuer zerfallende Unitobjekte
+    // das Decay sollte besser nicht durch manuellen Aufruf in der BP
+    // ausgeloest werden, daher Pruefung hier (PO == ME, falls der Reset vom
+    // Driver kommt).
+    if (previous_object() && previous_object() != ME)
+      return; 
+    int zeit = QueryProp(P_UNIT_DECAY_INTERVAL);
+    if (zeit > 0)
+    {
+      set_next_reset(zeit);
+      // Das Callout muss auf jeden Fall gemacht werden, wenn ein Decay
+      // Intervall gesetzt ist, damit die Blueprint auch den naechsten Reset
+      // kriegt, auch wenn es jetzt keine Clone gibt. Wenn es kein Quota gibt,
+      // kann der Callout wegfallen, beim Setzen eines Quota wird ja eine
+      // Funktion an diesem Objekt gerufen.
+      if (QueryProp(P_UNIT_DECAY_QUOTA) > 0)
+        call_out(#'_call_DoDecay, 1, 0);
+    }
+  }
+  else {
+    // Clones
+    if (Query(P_AMOUNT,F_VALUE)<1) remove(1);
+    else ::reset();
+  }
+}
+
+varargs int remove(int silent)
+{
+  int amount = QueryProp(P_AMOUNT);
+  int req = QueryProp(U_REQ);
+  if (amount > req)
+  {
+    ZDEBUG(sprintf("remove: reduziere amount %d um %d -> %d\n",
+           amount, req, amount-req ));
+    SetProp(P_AMOUNT, amount - req);
+    return 1;
+  }
+  ZDEBUG(sprintf("remove: restlos weg.\n"));
+  return ::remove(silent);
+}
+
+// sollte nicht von aussen gerufen werden.
+private void _call_DoDecay(object *klone) {
+  ZDEBUG(sprintf("call_DoDecay() in %O\n",ME));
+  // Clone oder kein Decay konfiguriert?
+  if (clonep()
+      || QueryProp(P_UNIT_DECAY_FLAGS) & NO_DECAY)
+    return;
+
+  if (!pointerp(klone))
+    klone = clones(2);
+  // Klone da?
+  int i=sizeof(klone);
+  if (!i)
+    return;
+
+  // in allen Klonen DoDecay rufen
+  while(get_eval_cost() > __MAX_EVAL_COST__/2 && i--) {
+    if (objectp(klone[i]))
+      catch(call_other(klone[i],"DoDecay", 0);publish);
+  }
+  // das i ist schon abgearbeitet.
+  i--;
+  // noch was uebrig?
+  if (i >= 0)
+    call_out(#'_call_DoDecay, 4, klone[0..i]);
+}
+
+// zerstoert quota Prozent der vorhandenen Units, liefert die Restmenge
+// zurueck. Ruft ggf. DoDecayMessage()
+public int DoDecay(int silent) {
+  // nur fuer Clones
+  if (!clonep())
+    return 0;
+  ZDEBUG(sprintf("DoDecay() in %O\n",ME));
+
+  // wenn das Objekt NO_DECAY oder NO_DECAY_UNTIL_MOVE in den Flags
+  // zerfaellt nix.
+  int flags = QueryProp(P_UNIT_DECAY_FLAGS);
+  if ((flags & NO_DECAY) || (flags & NO_DECAY_UNTIL_MOVE) )
+    return QueryProp(P_AMOUNT);
+
+  int quota = QueryProp(P_UNIT_DECAY_QUOTA);
+
+  // Zugewinn ist nicht. Ausserdem kann nicht mehr zerfallen, als da ist...
+  // Also ohne gueltiges Quota raus. Nur bei abs. Zerfall sind Zahlen > 10000
+  // erlaubt, bei relativen Zerfall waere das sinnlos.
+  if (quota <= 0 || 
+      (quota > 10000 && !(flags & ABSOLUTE_DECAY) ) ) 
+    return QueryProp(P_AMOUNT);
+  
+  int amount = QueryProp(P_AMOUNT);
+  int minimum = QueryProp(P_UNIT_DECAY_MIN);
+  // Zerfall nur, wenn man mehr als das Minimum hat.
+  if (amount > minimum) {
+    int zerfall;
+    if (flags & ABSOLUTE_DECAY) {
+      // Decay Quota soll einfach als abs. Zahl aufgefasst werden:
+      zerfall = quota;
+    }
+    else if (flags & INACCURATE_DECAY) {
+      // Rundungsfehler ignorieren und dafuer immer min. eine Einheit
+      // zerfallen lassein
+      zerfall = (amount * quota) / 10000;
+      if (zerfall <= 0) zerfall = 1;
+    }
+    else {
+      // Standardfall:
+      // nicht-ganzzahligen Rest als Wahrscheinlichkeit fuer den Zerfall einer
+      // Einheit auffassen
+      float tmp = amount * quota / 10000.0;
+      zerfall = to_int(tmp);
+      tmp -= zerfall; // Rest in tmp
+      // tmp*n ist eine Zahl zwischen 0 und n, wenn tmp*n > random(n)+1,
+      // dann zerfaellt eine Einheit extra. Da das Random fuer grosse Zahlen
+      // besser verteilt ist, nehm ich n = __INT_MAX__
+      if (ceil(tmp * __INT_MAX__) > random(__INT_MAX__) + 1)
+        zerfall++;
+    }
+
+    // nicht unter Minimum zerfallen
+    if (amount - zerfall < minimum)
+      zerfall = amount - minimum;
+
+    // erst ggf. Meldung ausgeben.
+    if (!silent && zerfall > 0)
+      DoDecayMessage(amount, zerfall);
+    // dann runtersetzen.
+    SetProp(P_AMOUNT, amount - zerfall);
+    ZDEBUG(sprintf("%O decayed by %d\n",ME, zerfall));
+    return amount-zerfall;
+  }
+  else if (amount < 0)
+    return 0; // neg. Anzahl gilt als "0 uebrig".
+  
+  // sonst ists wohl zwischen 0 und minimum, nix aendert sich.
+  return amount;
+}
+
+// gibt die Zerfallsmeldung aus. Wenn man mit der Standardmeldung nicht zufrieden
+// ist (duerfte der Normalfall sein), sollte man diese Funktion
+// ueberschreiben. Bekommt bisherige und jetzt gerade zerfallene Menge
+// uebergeben. Wichtig: zu diesem Zeitpunkt hat sich an der Unit noch nix (!)
+// geaendert!
+protected void DoDecayMessage(int oldamount, int zerfall) {
+  string msg;
+  if (oldamount == zerfall) {
+    if (living(environment())) {
+      tell_object(environment(), break_string(sprintf(
+          "Auf einmal %s%s zu Staub!",
+          (oldamount>1?"zerfallen Deine ":"zerfaellt D"), name(WER)),78));
+    }
+    // das tell_room() muss auf jeden fall sein, weil es "lebende" Raeume
+    // gibt. Liegt der KRam in einem solchen wuerden die Livings in diesem Raum
+    // sonst keine meldung kriegen.
+    tell_room(environment(), break_string(sprintf(
+          "Auf einmal %s %s zu Staub!",
+          (oldamount>1?"zerfallen":"zerfaellt"), name(WER)),78),
+        ({environment()}));
+  }
+  else {
+     if (living(environment())) {
+      tell_object(environment(), break_string(sprintf(
+          "Auf einmal %s %d Deiner %s zu Staub!",
+          (zerfall>1?"zerfallen":"zerfaellt"),
+          zerfall, Query(P_NAME)[1]),78));
+    }
+    // das tell_room() muss auf jeden fall sein, weil es "lebende" Raeume
+    // gibt. Liegt der KRam in einem solchen wuerden die Livings in diesem Raum
+    // sonst keine meldung kriegen.
+    tell_room(environment(), break_string(sprintf(
+          "Auf einmal %s %d %s zu Staub!",
+          (zerfall>1?"zerfallen":"zerfaellt"),
+          zerfall, Query(P_NAME)[1]),78), ({environment()}) ); 
+  }
+}
+
+static int _query_unit_decay_interval() {
+  if (clonep()) {
+    // fuer clones den Wert aus der BP liefern, der Wert in Clones ist eh
+    // uninteressant.
+    if (objectp(blueprint()))
+      return blueprint()->QueryProp(P_UNIT_DECAY_INTERVAL);
+    else
+      return load_object(load_name())->QueryProp(P_UNIT_DECAY_INTERVAL);
+  }
+  return Query(P_UNIT_DECAY_INTERVAL);
+}
+
+static int _set_unit_decay_interval(int zeit) {
+  // fuer Clones ist die Prop sinnlos und kann nicht gesetzt werden.
+  if (clonep()) {
+    if (objectp(blueprint()))
+      return blueprint()->QueryProp(P_UNIT_DECAY_INTERVAL);
+    else
+      return load_object(load_name())->QueryProp(P_UNIT_DECAY_INTERVAL);
+  }
+  // ueber diese Prop liesse sich der Reset relativ beliebig veraendern,
+  // verhindern, etc. Daher darf sie nur von diesem Objekt gesetzt werden, von
+  // Weisen+ oder eines Objektes, das die gleiche UID hat wie dieses hier oder
+  // des Programmieres dieses Objekts oder eines Magiers, der berechtigt fuer
+  // die jeweilige UID ist. Letztere beiden werden ueber den Ruf von
+  // QueryUIDsForWizard() ermittelt.
+  // erstes fremdes Objekt finden und pruefen.
+  foreach(object po: caller_stack(1)) {
+    if (po != ME) {
+      // fremden Aufrufer gefunden
+      if (getuid(po) != getuid(ME) 
+          && member(master()->QueryUIDsForWizard(getuid(po)),getuid(ME)) == -1
+          && (!this_interactive() || !IS_ELDER(this_interactive()) ) )
+        // unberechtigt, Abbruch
+        return QueryProp(P_UNIT_DECAY_INTERVAL);
+      else
+        // nur das erste ext. Objekt in der Callkette wird geprueft, raus.
+        break;
+    }
+  }
+  set_next_reset(zeit);
+  // falls dies aus dem create() gesetzt wird, muss zeitverzoegert eine
+  // Funktion von aussen am Objekt gerufen werden, da nach einem create() das
+  // Objekt als resettet gilt und sonst keinen Reset kriegt.
+  call_out("_query_unit_decay_interval",1);
+  return Set(P_UNIT_DECAY_INTERVAL, zeit, F_VALUE);
+}
+
+static int _query_unit_decay_quota() {
+  if (clonep()) {
+    // hat dieses Objekt einen eigenen wert gespeichert?
+    int quota=Query(P_UNIT_DECAY_QUOTA, F_VALUE);
+    if (quota)
+      return quota;
+    // sonst den Wert aus der Blueprint
+    if (objectp(blueprint()))
+      return blueprint()->QueryProp(P_UNIT_DECAY_QUOTA);
+    else
+      return load_object(load_name())->QueryProp(P_UNIT_DECAY_QUOTA);
+  }
+  return Query(P_UNIT_DECAY_QUOTA, F_VALUE);
+}
+
+static int _set_unit_decay_quota(int quota) { 
+  // momentan nur quota zwischen 0 und 10000
+  if (quota >= 0 && quota <=10000)
+    return Set(P_UNIT_DECAY_QUOTA, quota, F_VALUE);
+  else
+    return Query(P_UNIT_DECAY_QUOTA, F_VALUE);
+}
+
+static int _query_unit_decay_min() {
+  if (clonep()) {
+    // hat dieses Objekt einen eigenen wert gespeichert?
+    int minimum=Query(P_UNIT_DECAY_MIN, F_VALUE);
+    if (minimum)
+      return minimum;
+    // sonst den Wert aus der Blueprint
+    if (objectp(blueprint()))
+      return blueprint()->QueryProp(P_UNIT_DECAY_MIN);
+    else
+      return load_object(load_name())->QueryProp(P_UNIT_DECAY_MIN);
+  }
+  return Query(P_UNIT_DECAY_MIN, F_VALUE);
+}
+
+static int _query_unit_decay_flags() {
+  if (clonep()) {
+    // hat dieses Objekt einen eigenen wert gespeichert?
+    int flags=Query(P_UNIT_DECAY_FLAGS, F_VALUE);
+    if (flags)
+      return flags;
+    // sonst den Wert aus der Blueprint
+    if (objectp(blueprint()))
+      return blueprint()->QueryProp(P_UNIT_DECAY_FLAGS);
+    else
+      return load_object(load_name())->QueryProp(P_UNIT_DECAY_FLAGS);
+  }
+  // Die BP liefert den Wert auf jeden Fall ohne NO_DECAY_UNTIL_MOVE zurueck,
+  // weil dieses Flag im Falle der BP nicht zurueckgesetzt wird und bei Clients 
+  // ohne eigene Flags der Zerfall stoppen wuerde.
+  return Query(P_UNIT_DECAY_FLAGS, F_VALUE) & ~NO_DECAY_UNTIL_MOVE;
+}
+
diff --git a/std/user_filter.c b/std/user_filter.c
new file mode 100644
index 0000000..e4190c8
--- /dev/null
+++ b/std/user_filter.c
@@ -0,0 +1,435 @@
+// MorgenGrauen MUDlib
+//
+// user_filter.c -- Hilfsmodul fuer die wer-Liste
+//
+// $Id: user_filter.c 8755 2014-04-26 13:13:40Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+#include <properties.h>
+#include <wizlevels.h>
+#include <language.h>
+
+#define ME this_object()
+
+static int is_in ( object ob, mixed x )
+{
+  return (strstr( lower_case(country(ob)), x ) != -1 ||
+          strstr( lower_case(ob->Query(P_LOCATION) || ""), x ) != -1);
+}
+
+static int is_at( object ob, mixed x )
+{
+  return environment(ob) == x;
+}
+
+static int is_wiz_level_lt( object ob, mixed x )
+{
+  return query_wiz_level(ob) < x;
+}
+
+static int is_wiz_level_ge( object ob, mixed x )
+{
+  return query_wiz_level(ob) >= x;
+}
+
+static int is_prop_set( object ob, mixed x )
+{
+  return (mixed)ob->QueryProp(x) != 0;
+}
+
+static int is_second( object ob, mixed x )
+{
+  return ob->QueryProp(P_SECOND) && ob->QueryProp(P_SECOND_MARK) > x;
+}
+
+static int is_mage_second( object ob )
+{
+  return ob->QueryProp(P_SECOND) && IS_LEARNER(ob->QueryProp(P_SECOND));
+}
+
+static int is_in_gilde( object ob, mixed x )
+{
+  string str;
+
+  // nur Magier koennen eine andere Gilde per P_VISIBLE_GUILD in werlisten
+  // etc. vortaeuschen.
+  if (IS_LEARNER(ob)) {
+    // Querymethode von P_VISIBLE_GUILD fragt ggf. auch P_GUILD ab.
+    if ( !stringp(str = (string)ob->QueryProp(P_VISIBLE_GUILD)) )
+      return 0;
+  }
+  else {
+      str = (string)ob->QueryProp(P_GUILD);
+  }
+  if (!stringp(str)) return 0; // login hat z.B. keine Gilde.
+  return strstr(lower_case(str),x) == 0;
+}
+
+static int is_in_team( object ob, mixed x )
+{
+  object team;
+  
+  if ( !objectp(team = (object)ob->QueryProp(P_TEAM)) )
+    return x == "";
+  
+  return lower_case((string)team->Name()) == x;
+}
+
+static int is_name_in( object ob, mixed x )
+{
+  return member( x, capitalize(geteuid(ob)) ) >= 0;
+}
+
+static int is_in_region( object ob, mixed x )
+{
+  return environment(ob) &&
+    (object_name(environment(ob))[0..(sizeof(x)-1)] == x);
+}
+
+static int is_region_member( object ob, mixed x )
+{
+  return (int)master()->domain_member( geteuid(ob), x );
+}
+
+static int is_region_master( object ob, mixed x )
+{
+  return (int)master()->domain_master( geteuid(ob), x );
+}
+
+static int is_guild_master(object ob, mixed x)
+{
+  return (int)master()->guild_master(geteuid(ob), x);
+}
+
+static int is_gender( object ob, mixed x )
+{
+  return ob->QueryProp(P_GENDER) == x;
+}
+
+static int is_race( object ob, mixed x )
+{
+  return ob->QueryProp(P_RACE) == x;
+}
+
+static int is_unmarried( object ob )
+{
+  return ob->QueryProp(P_MARRIED) == 0;
+}
+
+static int is_idle( object ob, mixed x )
+{
+  return query_idle(ob) >= x;
+}
+
+static int is_hc( object ob)
+{
+  return ob->query_hc_play()>0;
+}
+
+static int is_ghost( object ob)
+{
+  return (int)ob->QueryProp(P_GHOST);
+}
+
+static int uses_ssl(object ob)
+{
+  if(!this_interactive() || !IS_LEARNER(this_interactive()))
+    return 0;
+#if __EFUN_DEFINED__(tls_query_connection_info)
+  return tls_query_connection_info(ob) != 0;
+#else
+  return 0;
+#endif
+}
+
+protected int is_active_guide(object ob) {
+    int cic=(int)ob->QueryProp(P_NEWBIE_GUIDE);
+    if (!intp(cic) || cic <= 0)
+      return 0;
+    else if (cic < 60)
+      return 1; // kleiner 60s in der Prop -> immer aktiv.
+    else if (cic <= 86400) {
+      // wenn laenger idle als cic -> inaktiv
+      if (query_idle(ob) > cic)
+	return 0;
+      return 1;
+    }
+    // ungueltiger Wertebereich -> inaktiv.
+    return 0;
+}
+
+object *filter_users( string str )
+{
+  object *res, *orig, *zwi;
+  string *words;
+  int i, sz, f, l,t;
+  mixed x;
+  
+  orig = users();
+  
+  if (!str)
+    return orig;
+  
+  res = ({});
+  words = old_explode( lower_case(str), " " );
+  sz = sizeof(words);
+  
+  for ( i = 0; i < sz; i++ ){
+    zwi = ({});
+    
+    switch (words[i]) {
+    case "ausser":
+    case "ohne":
+    case "nicht":
+      f=-1;
+      if (!i) res=orig;
+      continue;
+      
+    case "oder":
+      f=0;
+      continue;
+      
+    case "und":
+      f=1;
+      continue;
+      
+    case "alle":
+      zwi = orig;
+      break;
+
+    case "in":
+    case "aus":
+      if ( ++i >= sz )
+        break;
+      zwi = filter( orig, "is_in", ME, words[i] );
+      break;
+      
+    case "region":
+      if ( ++i >= sz )
+        break;
+      zwi = filter( orig, "is_in_region", ME, "/d/" + words[i] );
+      break;
+      
+    case "mitarbeiter":
+    case "regionsmitarbeiter":
+      if ( ++i >= sz )
+        break;
+      zwi = filter( orig, "is_region_member", ME, words[i] );
+      break;
+      
+    case "regionsmagier":
+      if ( ++i >= sz )
+        break;
+      zwi = filter( orig, "is_region_master", ME, words[i] );
+      break;
+      
+    case "gildenmagier":
+      if ( ++i >= sz )
+	break;
+      zwi = filter (orig, "is_guild_master", ME, words[i] );
+      break;
+      
+    case "gilde":
+      if ( ++i >= sz )
+        break;
+      zwi = filter( orig, "is_in_gilde", ME, words[i] );
+      break;
+      
+    case "team":
+      if ( ++i >= sz )
+        break;
+      zwi = filter( orig, "is_in_team", ME,
+                          lower_case("team " + words[i]) );
+      break;
+      
+    case "bei":
+      if ( ++i >= sz )
+        break;
+      if ( !objectp(x = find_player(words[i]) ) &&
+           !objectp(x = find_living(words[i])) )
+        break;
+      if ( x->QueryProp(P_INVIS) )
+        break;
+      zwi = filter( orig, "is_at", ME, environment(x) );
+      break;
+
+    case "goetter":
+      zwi = filter( orig, "is_wiz_level_ge", ME, GOD_LVL );
+      break;
+      
+    case "erzmagier":
+    case "erzi":
+    case "erzis":
+      zwi = filter( orig, "is_wiz_level_ge", ME, ARCH_LVL );
+      break;
+      
+    case "magier":
+      zwi = filter( orig, "is_wiz_level_ge", ME, LEARNER_LVL );
+      break;
+      
+    case "spieler":
+      zwi = filter( orig, "is_wiz_level_lt", ME, SEER_LVL );
+      break;
+      
+    case "seher":
+      zwi = filter( filter( orig, "is_wiz_level_lt", ME,
+                                        LEARNER_LVL),
+                          "is_wiz_level_ge", ME, SEER_LVL );
+      break;
+
+    case "maennlich":
+      zwi = filter( orig, "is_gender", ME, MALE );
+      break;
+      
+    case "weiblich":
+      zwi = filter( orig, "is_gender", ME, FEMALE );
+      break;
+      
+    case "ledig":
+      zwi = filter( orig, "is_unmarried" );
+      break;
+
+    case "ssl":
+    case "stunnel":
+      zwi = filter(orig,"uses_ssl");
+      break;
+      
+    case "hardcore":
+      zwi = filter( orig, "is_hc" );
+      break;
+      
+    case "geist":
+    case "tot":
+      zwi = filter( orig, "is_ghost" );
+      break;
+      
+    case "mensch":
+      zwi = filter( orig, "is_race", ME, "Mensch" );
+      break;
+      
+    case "zwerg":
+      zwi = filter( orig, "is_race", ME, "Zwerg" );
+      break;
+      
+    case "elf":
+      zwi = filter( orig, "is_race", ME, "Elf" );
+      break;
+      
+    case "feline" :
+      zwi = filter( orig, "is_race", ME, "Feline" );
+      break;
+      
+    case "hobbit":
+      zwi = filter( orig, "is_race", ME, "Hobbit" );
+      break;
+      
+    case "dunkelelf":
+      zwi = filter( orig, "is_race", ME, "Dunkelelf" );
+      break;
+
+    case "goblin":
+      zwi = filter( orig, "is_race", ME, "Goblin" );
+      break;
+
+    case "ork":
+      zwi = filter( orig, "is_race", ME, "Ork" );
+      break;
+
+    case "frosch":
+      zwi = filter( orig, "is_prop_set", ME, P_FROG );
+      break;
+
+    case "weg":
+      zwi = filter( orig, "is_prop_set", ME, P_AWAY );
+      break;
+
+    case "idle":
+      zwi = filter( orig, "is_idle", ME, 120 );
+      break;
+
+    case "idlezeit":
+      if ( ++i >= sz )
+        break;
+      sscanf(words[i],"%d",t);
+      zwi = filter( orig, "is_idle", ME, 60*t );
+      break;
+
+    case "cicerone":
+    case "cicerones":
+      // is_active_guide() ist in /std/player/guide.c
+      zwi = filter( orig, #'is_active_guide);
+      break;
+
+    case "testie":
+    case "testies":
+    case "testspieler":
+      zwi = filter( orig, "is_prop_set", ME, P_TESTPLAYER );
+      break;
+      
+    case "zweitie":
+    case "zweities":
+    case "zweitspieler":
+        if (IS_LEARNER(this_player()))
+          zwi = filter( orig, "is_second", ME, -2 );
+        else
+          zwi = filter( orig, "is_second", ME, -1 );
+      break;
+
+    case "magierzweitie":
+    case "magierzweities":
+    case "magierzweitspieler":
+        if (IS_ARCH(this_player()))
+          zwi = filter( orig, "is_mage_second");
+      break;
+
+    case "erwartete":
+      if ( i < (sz-1) && words[i+1] == "wegen" ) {
+        i++;
+
+        if ( !mappingp(x = (mixed)this_player()->QueryProp(P_WAITFOR_REASON)) ||
+             !pointerp(x = m_indices(x)) )
+          break;
+        
+        zwi = filter( orig, "is_name_in", ME, x );
+        break;
+      }
+      
+      if ( !pointerp(x = (mixed)this_player()->QueryProp(P_WAITFOR)) )
+        break;
+      zwi = filter( orig, "is_name_in", ME, x );
+      break;
+
+    case "icq":
+      zwi = filter( orig, "is_prop_set", ME, P_ICQ );
+      break;
+
+    case "url":
+    case "www":
+      zwi = filter( orig, "is_prop_set", ME, P_HOMEPAGE );
+      break;
+          
+    default:
+      continue;
+    }
+    
+    switch (f) {
+    case -1:
+      res -= zwi;
+      break;
+      
+    case 1:
+      res &= zwi;
+      break;
+
+    default:
+      res += zwi;
+    }
+  }
+  
+  return res;
+}
+
diff --git a/std/util/cidr.c b/std/util/cidr.c
new file mode 100644
index 0000000..85a00e5
--- /dev/null
+++ b/std/util/cidr.c
@@ -0,0 +1,26 @@
+// MorgenGrauen MUDlib
+/** \file /std/util/cidr.c
+* Funktionen zur Interpretation und Umrechnung von IP-Adressen in CIDR-Notation.
+* \author Zesstra
+* \date 05.02.2010
+* \version $Id$
+*/
+/* Changelog:
+*/
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma no_shadow
+#pragma pedantic
+#pragma range_check
+
+// Never do this at home. ;-)
+// Der Hintergrund ist, dass der Master moeglichst wenig Code von ausserhalb
+// /secure/ erben/inkludieren sollte, ich aber nur an einer Stelle Aenderungen
+// vornehmen moechte...
+#define private public
+#include "/secure/master/cidr.c"
+#undef private
+
+//string IPv4_test(string addr) {return (IPv4_int2addr(IPv4_addr2int(addr)));}
+
diff --git a/std/util/ex.c b/std/util/ex.c
new file mode 100644
index 0000000..eae9e27
--- /dev/null
+++ b/std/util/ex.c
@@ -0,0 +1,293 @@
+// MorgenGrauen MUDlib
+//
+// util/ex.c -- ex compatible editor
+//
+// $Id: ex.c 9142 2015-02-04 22:17:29Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/util/input";
+
+private nosave mapping ctrl = ([]);
+private nosave mixed buf;
+
+#include <sys_debug.h>
+#undef ARGS
+#include <wizlevels.h>
+#include <player.h>
+
+#define MODE	0
+#  define CMD		0
+#  define INP		1
+#define TEXT	1
+#define LINE	2
+#define FUNC	3
+#define ARGS	4
+#define FLAG	5
+#  define NUM		1
+#define CHG	6
+
+#define YANK	"@YANK"
+
+int evaluate(string in);
+
+varargs int error(string msg, string arg)
+{
+  if(stringp(arg)) write(arg+": "+msg+"\n");
+  else write(msg+"\n");
+}
+
+string substitute(string s, string regex, string n, string extra)
+{
+  mixed del; int i;
+  if((i = sizeof(del = regexplode(s, regex))) < 2) return s;
+  if(member(extra, 'g') == -1) i = 1;
+  else i -= 2;
+  while(i>0) {
+    del[i] = n;
+    i -= 2;
+  }
+  return implode(del, "");
+}
+
+// saveText() -- save edited text
+//
+int saveText()
+{
+  string text;
+  text = implode(ctrl[buf][TEXT][1..], "\n")+"\n";
+  error(sprintf("%O, %d Zeilen, %d Zeichen", 
+	       buf ? buf : "", sizeof(ctrl[buf][TEXT])-1,
+	       sizeof(text)));
+  if(ctrl[buf][FUNC]) apply(ctrl[buf][FUNC], ctrl[buf][ARGS], text);
+  ctrl = m_copy_delete(ctrl, buf);
+  ctrl = m_copy_delete(ctrl, buf+"!");
+  buf = 0;
+  return 1;
+}
+
+// cmdPrompt() -- create command prompt
+//
+string cmdPrompt(int mode, mixed text, int line, int maxl)
+{
+  return ":";
+}
+
+// inputPrompt() -- create input prompt
+//
+string inputPrompt()
+{
+  if(ctrl[buf][FLAG] & NUM) return sprintf("[%02d] ", ctrl[buf][LINE]);
+  return "";
+}
+
+string AddNum(string s)
+{
+  string r;
+  r = inputPrompt()+s;
+  ctrl[buf][LINE]++;
+  return r;
+}
+
+void showLines(int from, int to)
+{
+  write(implode(map(ctrl[buf][TEXT][from..to], #'AddNum), "\n")+"\n");
+  ctrl[buf][LINE] = from;
+}
+
+// commandMode() -- handle commands
+//
+int commandMode(string in)
+{
+  int from, to, swap;
+  string cmd;
+
+  if(!in) return 0;
+  if(in[0..1] == "se") {
+    ctrl[buf][FLAG] ^= NUM;
+    return 0;
+  }
+    
+  if(sscanf(in, "%d,%d%s", from, to, cmd) != 3)
+    if(sscanf(in, "%d%s", from, cmd) != 2)
+      if(!stringp(in)) return error("Kein Editorkommando", in);
+      else { cmd = in; from = to = ctrl[buf][LINE]; }
+    else to = from;
+
+  if(from < 0) from = sizeof(ctrl[buf][TEXT])-1+from;
+  if(to < 1) to = sizeof(ctrl[buf][TEXT])-1+to;
+  if(to && to < from) return error("Erste Adresse groesser als die Zweite");
+  if(from) ctrl[buf][LINE] = from;
+  if(from > sizeof(ctrl[buf][TEXT])-1 ||
+     to > sizeof(ctrl[buf][TEXT])-1) 
+    return error("Nicht so viele Zeilen im Speicher");
+  if(!to) to = from;
+  switch(cmd[0])
+  {
+  case 'h':
+    write("ex: Kommandohilfe\n\n"
+	 "Alle Kommandos funktionieren nach folgendem Prinzip:\n"
+	 "Adresse Kommando Argumente\n"
+	 "Adressen werden gebildet durch: ZeileVon,ZeileBis\n"
+	 "(ZeileBis kann weggelassen werden, samt Komma)\n"
+	 "Kommandos:\n"
+	 "  q,x -- Editor verlassen\n"
+	 "  r   -- Datei einfuegen\n"
+	 "         r<datei>\n"
+	 "  a   -- Text hinter der aktuellen Zeile einfuegen\n"
+	 "  i   -- Text vor der aktuellen Zeile einfuegen\n"
+	 "  d   -- Zeilen loeschen\n"
+	 "  y   -- Zeilen zwischenspeichern\n"
+	 "  pu  -- Zwischengespeicherte Zeilen einfuegen\n"
+	 "  s   -- Substitution von Text in Zeilen\n"
+	 "         s/AlterText/NeuerText/\n"
+          "  p,l -- Zeilen anzeigen\n"
+	 "  z   -- Eine Seite anzeigen\n");
+    break;
+  case 'q':
+    if(ctrl[buf][CHG])
+      if(!(sizeof(cmd) > 1 && cmd[1]=='!'))
+        return error("Datei wurde geaendert! Abbrechen mit q!");
+      else ctrl[buf] = ctrl[buf+"!"];
+  case 'x':
+    if(saveText()) return 1;
+  case 'r':
+    if(!IS_WIZARD(this_player()))
+      return error("Kommando gesperrt", cmd[0..0]);
+    if(file_size(cmd[1..]) < 0) 
+      return error("Datei nicht gefunden", "\""+cmd[1..]+"\"");
+    ctrl[buf][TEXT] = ctrl[buf][TEXT][0..from] 
+		  + explode(read_file(cmd[1..]), "\n")
+	           + ctrl[buf][TEXT][(from == to ? to+1 : to)..];
+    ctrl[buf][CHG] = 1;
+    break;
+  case 'a':
+    ctrl[buf][MODE] = INP;
+    write("Texteingabe: (. beendet zum Kommandomodus, ** beendet ganz)\n");
+    input(#'inputPrompt, 0, #'evaluate); 
+    ctrl[buf][CHG] = 1;
+    return 1;
+  case 'i':
+    ctrl[buf][MODE] = INP;
+    ctrl[buf][LINE]--;
+    write("Texteingabe: (. beendet zum Kommandomodus, ** beendet ganz)\n");
+    input(#'inputPrompt, 0, #'evaluate);
+    ctrl[buf][CHG] = 1;
+    return 1;
+  case 'd':
+    ctrl[buf][TEXT][from..to] = ({});
+    ctrl[buf][CHG] = 1;
+    break;
+  case 'y':
+    ctrl[YANK] = ctrl[buf][TEXT][from..to];
+    if(to - from) error(to-from+1+" Zeilen gespeichert");
+    break;
+  case 's':
+  {
+    mixed reg, new, extra, scmd;
+    if(sizeof(scmd = old_explode(cmd[1..], cmd[1..1])) < 2)
+      return error("Substitution fehlgeschlagen");
+    reg = scmd[0]; new = scmd[1];
+    if(sizeof(scmd) > 2) extra = scmd[2];
+    else extra = "";
+    ctrl[buf][TEXT][from..to] = map(ctrl[buf][TEXT][from..to], 
+				      #'substitute, 
+                                          reg, new, extra);
+    showLines(from, to);
+    ctrl[buf][CHG] = 1;
+    break;
+  }
+  case 'z':
+    showLines(ctrl[buf][LINE], 
+	     ctrl[buf][LINE]+this_player()->QueryProp(P_SCREENSIZE));
+    ctrl[buf][LINE] += this_player()->QueryProp(P_SCREENSIZE);
+    break;
+  case 'p':
+    if(sizeof(cmd) > 1 && cmd[1] == 'u')
+    {
+      if(!pointerp(ctrl[YANK])) return error("Keine Zeilen im Speicher");
+      if(from >= ctrl[buf][LINE]) ctrl[buf][TEXT] += ctrl[YANK];
+      else
+        ctrl[buf][TEXT][0..from] + ctrl[YANK] + ctrl[buf][TEXT][from+1..];
+      ctrl[buf][LINE] += sizeof(ctrl[YANK]);
+      ctrl[YANK] = 0;
+      showLines(ctrl[buf][LINE], ctrl[buf][LINE]);
+      break;
+    }
+  case 'l':
+  default:
+    if(!from) 
+    {
+      error("Kein Editorkommando", sprintf("%c", cmd[0]));
+      return 0;
+    }
+    showLines(from, to);
+  }
+  input(#'cmdPrompt, 0, #'evaluate);
+  return 1;
+}
+
+// inputMode() -- handle input mode commands
+//
+int inputMode(string in)
+{
+  switch(in)
+  {
+  case ".": 
+    ctrl[buf][MODE] = CMD;
+    ctrl[buf][LINE]--;
+    input(#'cmdPrompt, 0, #'evaluate);
+    break;
+  case "**":
+    return saveText();
+  default:
+    if(!in) /* do nothing now */;
+    if(ctrl[buf][LINE] < sizeof(ctrl[buf][TEXT])-1)
+      ctrl[buf][TEXT] = ctrl[buf][TEXT][0..ctrl[buf][LINE]-1] + ({ in }) 
+	        + ctrl[buf][TEXT][ctrl[buf][LINE]..];
+    else ctrl[buf][TEXT] += ({ in });
+    ctrl[buf][LINE]++;
+    input(#'inputPrompt, 0, #'evaluate);
+    break;
+  }
+  return 1;
+}
+
+// evaluate() -- evaluate input (in) in context (ctrl[buf])
+//
+int evaluate(string in)
+{
+  switch(ctrl[buf][MODE])
+  {
+    case INP: return inputMode(in);
+    case CMD: return commandMode(in);
+    default : return 0;
+  }
+}
+
+void StartEX(string in, mixed c)
+{
+  if(!in || in == "j" || in == "J") ctrl[buf] = c;
+  else if(in && (in != "n"  || in != "N"))
+    return 0;
+  ctrl[buf+"!"] = ctrl[buf];
+  input(#'cmdPrompt, 0, #'evaluate);
+}
+
+varargs int ex(mixed text, closure func, mixed fargs, string buffer)
+{
+  int c, l;
+  mixed ct;
+  if(!text) text = "";
+  c = sizeof(text);
+  l = sizeof(text = explode(text, "\n")) - 1;
+  ct = ({ CMD, text, 0, func, fargs, 0, 0});
+  if(!ctrl[buffer]) StartEX(0, ct);
+  else input(sprintf("Es existiert bereits Text! Verwerfen? [j]"),
+	    0, #'StartEX, ct);
+  return 1;
+}
diff --git a/std/util/executer.c b/std/util/executer.c
new file mode 100644
index 0000000..4e0f707
--- /dev/null
+++ b/std/util/executer.c
@@ -0,0 +1,39 @@
+// MorgenGrauen MUDlib
+//
+// utils/executer.c - Helfer zum Ausfuehren vom Kram
+//
+// $Id: skills.c 6673 2008-01-05 20:57:43Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+protected mixed execute_anything(mixed fun, varargs mixed args)
+{
+  if ( closurep(fun) && objectp(query_closure_object(fun)) )
+    return apply(fun, args);
+
+  if (stringp(fun))
+    return call_other(this_object(), fun, args...);
+
+  if ( pointerp(fun) && sizeof(fun)==2 )
+  {
+    object ob;
+    if (sizeof(fun)>2)
+      raise_error(sprintf("execute_anything(): first argument may only "
+                         "have 2 elements if array.\n"));
+
+    if ( stringp(fun[0]) )
+      ob=find_object(fun[0]);
+    else
+      ob=fun[0];
+
+    if ( !objectp(ob) || !stringp(fun[1]) )
+      return 0;
+
+    return call_other(ob, fun[1], args...);
+  }
+  return 0;
+}
+
diff --git a/std/util/input.c b/std/util/input.c
new file mode 100644
index 0000000..e65bc84
--- /dev/null
+++ b/std/util/input.c
@@ -0,0 +1,31 @@
+// MorgenGrauen MUDlib
+//
+// util/input.c -- generic input handling
+//
+// $Id: input.c 8349 2013-02-04 21:15:28Z Zesstra $
+
+#include <input_to.h>
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+varargs void input(mixed prompt, mixed pargs, mixed ctrl, mixed ctrlargs)
+{
+  mixed prompttext;
+  if(closurep(prompt))
+    prompttext = apply(prompt, pointerp(pargs) ? pargs : ({}));
+  else prompttext = prompt;
+  if (!stringp(prompttext)) prompttext="";
+  input_to("done", INPUT_PROMPT, prompttext, prompt, pargs, ctrl, ctrlargs);
+}
+
+void done(string in, mixed prompt, mixed pargs, mixed ctrl, mixed ctrlargs)
+{
+  if(closurep(ctrl) &&
+     apply(ctrl, ({ in }) + (pointerp(ctrlargs) ? ctrlargs : ({})))) 
+    return;
+  input(prompt, pargs, ctrl, ctrlargs);
+}
diff --git a/std/util/pager.c b/std/util/pager.c
new file mode 100644
index 0000000..9d71b85
--- /dev/null
+++ b/std/util/pager.c
@@ -0,0 +1,142 @@
+// MorgenGrauen MUDlib
+//
+// util/pager.c -- generic pager
+//
+// $Id: pager.c 8755 2014-04-26 13:13:40Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/util/input";
+
+#include <pager.h>
+#include <sys_debug.h>
+#include <wizlevels.h>
+
+string prompt(mixed pinfo, string add)
+{
+  return sprintf(MSG_PROMPT+"%s", stringp(add)?add:"");
+}
+
+string fread(mixed pinfo, int begin, int c)
+{
+  if(begin > pinfo[MAXL] || begin < 1) return 0;
+  if(pinfo[FILE]) return read_file(pinfo[TEXT], begin, c);
+  else
+  {
+    int start, end, l, x;
+
+    if(member(pinfo[JUNK], begin)) 
+    {
+      start = pinfo[JUNK][begin];
+      if(!(end = pinfo[JUNK][begin+c]))
+        end = sizeof(pinfo[TEXT]);
+      else end--;
+      return pinfo[TEXT][start..end];
+    }
+    return 0;
+  }
+}
+
+mixed call(closure ctrl, mixed ctrlargs)
+{
+  if (!ctrl||!closurep(ctrl)) return 0;
+  if(!pointerp(ctrlargs)) return funcall(ctrl);
+  return apply(ctrl, ctrlargs);
+}
+
+ 
+varargs int eval_command(mixed in, mixed pinfo)
+{
+  string cprompt, tmp;
+  cprompt = "";
+  if(in == "q" || in == "x")
+  {
+    if(closurep(pinfo[CTRL])) apply(pinfo[CTRL], pinfo[CARG]);
+    return 1;
+  }
+  else
+    if(!in || (stringp(in) && !sizeof(in))) pinfo[CURL] += pinfo[PAGE];
+    else if(in != -1) return 0;
+
+  if(pinfo[CURL] >= pinfo[MAXL]) 
+    return (call(pinfo[CTRL], pinfo[CARG]), 1); 
+  if(pinfo[CURL] <= 0) pinfo[CURL] = 1;
+  if(pinfo[CURL] == 1) cprompt = MSG_TOP;
+  if(pinfo[CURL] + pinfo[PAGE] >= pinfo[MAXL]) cprompt = MSG_BOTTOM;
+  if(!tmp = fread(pinfo, pinfo[CURL], pinfo[PAGE]))
+    return 0; /* (write(MSG_OOPS+"\n"), 0); */
+  if (query_once_interactive(this_object()))
+     tell_object(this_object(), tmp);
+  else write(tmp);
+  if(!(pinfo[FLAG] & E_PROMPT) &&
+     (pinfo[CURL] + pinfo[PAGE] >= pinfo[MAXL] || !pinfo[PAGE]))
+    return (call(pinfo[CTRL], pinfo[CARG]), 1);
+  if(!(pinfo[FLAG] & E_CAT) &&
+     (pinfo[MAXL] > pinfo[PAGE] || (pinfo[FLAG] & E_PROMPT)))
+    input(#'prompt, ({ pinfo, cprompt }), #'eval_command, ({ pinfo }));
+  return 1;
+}
+
+varargs public void More(string txt, int file,
+			 mixed ctrl, mixed ctrlargs,
+			 int flags)
+{
+  int j;
+  mixed tmp, pinfo;
+  if(!txt) return call(ctrl, ctrlargs);
+  //         TEXT, FILE, CURL, MAXL, REGX, PAGE, CTRL, CARG
+  pinfo = ({ txt, file, 1, 1, 20, 0, ctrl, ctrlargs, flags });
+
+  if (pinfo[FILE])
+  { 
+     if (file_size(pinfo[TEXT])<50000)
+     {
+       pinfo[TEXT]=read_file(pinfo[TEXT]);
+       if (!pinfo[TEXT])
+       { 
+         call(ctrl, ctrlargs);
+         return;
+       }  
+       pinfo[FILE]=0;
+     }
+     else if (file_size(pinfo[TEXT])>1048576)
+     {
+	tell_object(this_player()||this_object(),break_string(sprintf(
+               "Die Datei '%s' kann nicht angezeigt werden, da " 
+               "sie groesser als 1 MB ist. %s",pinfo[TEXT],
+               IS_LEARNER(this_player()||this_object())?"":"Bitte verstaendige "
+               "diesbezueglich umgehend einen Magier."),78));
+	call(ctrl,ctrlargs);
+        return;
+     }
+  }
+
+  if(!pinfo[FILE] && pinfo[TEXT][<1..<1] != "\n") pinfo[TEXT] += "\n";
+  pinfo += ({ ([1:0]) });
+  if(!pinfo[FILE]) 
+    while((j = strstr(pinfo[TEXT], "\n", j+1)) != -1)
+  	pinfo[JUNK][++pinfo[MAXL]] = j+1;
+  else
+    while(tmp = read_file(pinfo[TEXT], pinfo[MAXL], MAX_LINE_READ))
+      pinfo[MAXL] += sizeof(explode(tmp, "\n"))+1;
+  pinfo[PAGE] = PAGELENGTH;
+  if(!pinfo[PAGE]) pinfo[FLAG] |= E_CAT;
+  if ((this_interactive() && (j=(int)this_interactive()->QueryProp(P_MORE_FLAGS))) ||
+      (this_player() && interactive(this_player()) &&
+       (j=(int)this_player()->QueryProp(P_MORE_FLAGS))))
+    pinfo[FLAG] |= j;
+  
+  pinfo[CURL] = 1;
+  eval_command(-1, pinfo);
+  return;
+}
+
+
+
+
+
+
diff --git a/std/util/pl_iterator.c b/std/util/pl_iterator.c
new file mode 100644
index 0000000..555a75e
--- /dev/null
+++ b/std/util/pl_iterator.c
@@ -0,0 +1,53 @@
+// MorgenGrauen MUDlib
+/** \file /file.c
+* Kurzbeschreibung.
+* Langbeschreibung...
+* \author <Autor>
+* \date <date>
+* \version $Id$
+*/
+/* Changelog:
+*/
+#pragma strong_types, save_types, rtt_checks
+#pragma no_clone, no_shadow
+#pragma pedantic, range_check
+#pragma warn_deprecated
+
+private void check_all_player(mapping allplayer, closure check_cl,
+                             closure finish_cl, int res,
+                             varargs mixed extra)
+{
+  // offenbar fertig mit allen Spielern, Aufrufer informieren.
+  if (!sizeof(allplayer))
+  {
+    funcall(finish_cl, extra...);
+    return;
+  }
+
+  string dir=m_indices(allplayer)[0];
+  string *pls=allplayer[dir];
+  foreach(string pl: &pls)
+  {
+    if (get_eval_cost() < res)
+      break; // spaeter weitermachen
+    catch(funcall(check_cl, pl, extra...) ; publish);
+    pl = 0;
+  }
+  pls-=({0});
+  allplayer[dir] = pls;
+
+  if (!sizeof(allplayer[dir]))
+    m_delete(allplayer,dir);
+
+  call_out(#'check_all_player,2, allplayer, check_cl, finish_cl, res, extra...);
+}
+
+protected int start_player_check(closure check_cl, closure finish_cl, int res,
+                             varargs mixed extra)
+{
+  res ||= 1250000;
+  mapping allplayer=(mapping)master()->get_all_players();
+  call_out(#'check_all_player,2, allplayer, check_cl, finish_cl, res, extra...);
+  return 1;
+}
+
diff --git a/std/util/rand-glfsr.c b/std/util/rand-glfsr.c
new file mode 100644
index 0000000..8bd624c
--- /dev/null
+++ b/std/util/rand-glfsr.c
@@ -0,0 +1,155 @@
+/* Dieses File implementiert einen Pseudo-Zufallszahlengenerator mit einem
+ * Linear Feedback Shift Register in der Galois-Variante.
+ * Dieses PRNG ist schlechter und viel ineffizienter als der im Driver
+ * eingebaute.
+ * Die einzige sinnvolle Anwendung ist, wenn man aus irgendeinem Grund das
+ * seed selber waehlen muss, damit man die Sequenz von Pseudozufall immer
+ * wieder reproduzieren kann.
+ *
+ * Das Seed von der Blueprint wird vermutlich staendig veraendert (d.h.
+ * verlasst euch nicht drauf, dass es konstant bleibt), wollt ihr eine
+ * 'private' Instanz mit eurem Seed, clont das Objekt (aber verliert den Clone
+ * nicht).
+ *
+ * Es wird standardmaessig ein Polynom fuer eine Periodenlaenge von 2^32 - 1
+ * verwendet.
+ * 
+ * Im Netz finden sich Infos ueber LFSRs und Tabellen fuer maximum-length
+ * feedback polynomials (M-Sequence Feedback Taps), falls jemand braucht.
+ *
+*/
+#pragma strong_types,rtt_checks,pedantic
+
+#include <tls.h>
+
+// Default-Polynom ist das:
+// x^32 + x^31 + x^28 + x^27 + x^24 + x^23 + x^20 + x^19 + x^16 + x^15 + x^12
+//      + x^11 + x^8 + x^7 + x^5 + x^3 + 1
+// Taps: 32, 31, 28, 27, 24, 23, 20, 19, 16, 15, 12, 11, 8, 7, 5, 3
+// Binary: 110011001100110011001100110101000
+#define DEFAULTP 0x1999999a8
+// Andere gebraeuchliche:
+// x^32 + x^30 + x^26 + x^25 + 1,
+// Taps 32, 30, 26, 25
+// Binary: 101000110000000000000000000000000
+//#define DEFAULTP 0x146000000
+// x^16 + x^14 + x^13 + x^11 + 1
+// Taps 16, 14, 13, 11
+// Binary: 1011010000000000
+//#define DEFAULTP 0xB400
+
+private int polynom = DEFAULTP;
+private int state = 2553647223;
+//private int period;
+
+public varargs void init(int seed, int newp)
+{
+  if (!seed)
+    raise_error("Illegal seed 0\n");
+  // Es darf nur ein 32 bit breiter Seed verwendet werden. Die oberen 32 bit
+  // werden mit den unteren 32 bit XOR-rt (also, einmal die oberen 32 shiften
+  // und einmal die oberen 32 wegmaskieren.
+  seed = ((seed>>32) & 0xffffffff) ^ (seed & 0xffffffff);
+  //printf("Seed: %064b - 0x%x\n",seed,seed);
+  
+  if (!seed)
+    raise_error("Illegal reduced seed: 0\n");
+
+  state = seed;
+  polynom = newp || DEFAULTP;
+}
+
+public void InitWithUUID(string uuid)
+{
+  string md5hash = hash(TLS_HASH_MD5, uuid);
+  int seed;
+  // 8 Bytes aus dem Hash ermitteln
+  foreach(int b: 16)
+  {
+    // Jeweils zwei Zeichen herausschneiden und als Hexadezimalzahl
+    // interpretieren, was jeweils ein byte (8 bit) gibt. 
+    int tmp = to_int("0x"+md5hash[b*2..b*2+1]);
+    // diese werden dann in eine 64 bit breite Zahl zusammengefasst. Das
+    // gerade ermittelte Byte wird nach links geshiftet und mit dem, was da
+    // ggf. schon steht, XORred. Ist der int voll, faengt man wegen Modulo 64
+    // wieder rechts an.
+    //printf("S: %064b - %08b\n",seed,tmp);
+    seed = seed ^ (tmp << ((b*8)%64));
+    //printf("S: %064b - 0x%x\n",seed, seed);
+  }
+  //printf("Seed: 0x%x - %b\n",seed,seed);
+  init(seed);
+}
+
+public int nextbit()
+{
+  // Get LSB (i.e., the output bit).
+  int lsb = state & 1;
+  // Shift register by one bit
+  state >>= 1;
+  // Apply a toggle mask, which flips all the tap bits, _if_ the output bit is
+  // 1. The mask has 1 at bits corresponding to taps, 0 elsewhere. In other
+  // words, the polynom from above.
+  if (lsb)
+      state ^= polynom;
+  // debug check ;-)
+  if (!state)
+    raise_error("State must not be zero, but it is.\n");
+  //++period; 
+  //printf("State: %032b (Period: %d)\n",state,period);
+  return lsb;
+}
+
+public int nextbits(int count)
+{
+  int result;
+  if (count>64)
+    raise_error(sprintf("Count is %d, but must be <= 64.\n",count));
+  foreach(int i: count)
+    result = (result << 1) | nextbit();
+  return result;
+}
+
+public int nextbyte()
+{
+  return nextbits(8);
+}
+
+public int random(int n)
+{
+  int rnd = nextbits(32);
+  //generates a random number on [0,1)-real-interval 
+  float tmp = rnd * (1.0/4294967296.0);
+  // Skalieren auf [0,n)
+  return to_int(tmp * n);
+}
+
+// Just skip some bits and discard them.
+public void skipbits(int count)
+{
+  foreach(int i: count)
+    nextbit();
+}
+
+
+public void dumpstream(string file, int bytes)
+{
+  int *stream = allocate(bytes);
+  foreach(int i: bytes)
+  {
+    stream[i] = nextbits(8);
+  }
+  write_file(file, sprintf("%@c",stream));
+}
+
+public int Configure(int data)
+{
+  if (!data)
+    return state;
+  
+  if (!intp(data))
+    return 0;
+  state = data;
+  return 1;
+}
+
diff --git a/std/util/ringbuffer.c b/std/util/ringbuffer.c
new file mode 100644
index 0000000..19338e1
--- /dev/null
+++ b/std/util/ringbuffer.c
@@ -0,0 +1,210 @@
+// MorgenGrauen MUDlib
+/** \file /std/util/ringbuffer.c
+* Implmentiert einen Ringbuffer in LPC.
+* Ab und an hat man den Fall, dass man Ereignisse/Meldungen o.ae. hat, von
+* denen man immer genau die letzten x gespeichert und abrufbar vorhalten
+* moechte. Dieses File stellt eine Datenstruktur und dazugehoerige
+* Verwaltungsfunktionen zu Verfuegung, mit denen dies effizient moeglich ist.
+* Insbesondere umgehen diese das staendige Slicing von Arrays, mit denen
+* haeufig Ringpuffer gebaut werden. Hierbei wird das Schreiben in den Puffer
+* billiger, das Abrufen der Daten allerdings teurer. Wird der Puffer haeufiger
+* abgefragt als beschrieben, ist dies keine effiziente Loesung.
+* 
+* \author Zesstra
+* \date 22.05.2008
+* \version $Id$
+*/
+/* Changelog:
+*/
+#pragma strong_types
+#pragma save_types
+#pragma no_clone
+#pragma no_inherit
+#pragma no_shadow
+#pragma pedantic
+#pragma range_check
+
+#include <util/ringbuffer.h>
+
+/** Standardgroesse fuer Ringbuffer.
+  */
+#define MAXCOUNT 30
+
+
+/** Struktur, die einen Ringpuffer speichert.
+  Der Puffer selber ist im wesentlichen ein Array. Hinzu kommt noch ein
+  Zaehler, der den naechsten zu ueberschreibenden Wert indiziert sowie einen
+  Modus, der die Reihenfolge beim Abrufen beshreibt:
+  MODE_FIFO: First-in-First-out (default)
+  MODE_LIFO: Last-in-First-out
+  */
+struct std_ringbuffer {
+    mixed rbuffer;
+    int mode;
+    int next;
+};
+
+/** Erzeugt einen neuen Ringbuffer und liefert ihn zurueck.
+  Die Funktion erzeugt einen neuen, leeren Ringbuffer der Groesse \a size
+  und mit Ausgabemodus \a newmode und liefert die entsprechende
+  Ringbuffer-Struktur zurueck.
+  \attention Die zurueckgelieferte Datenstruktur bitte nicht per Hand
+  manipulieren.
+  \param[in] size Groesse des zu erzeugenden Ringbuffers. Default: 30
+  \param[in] newmode Ausgabemodus des Ringbuffers: \a MODE_FIFO oder
+  \a MODE_LIFO. Default: MODE_FIFO
+  \return Der erzeugte Ringbuffer (struct vom Typ std_ringbuffer).
+  \sa ResizeRingBuffer, RingBufferPut, RingBufferGet
+  */
+protected struct std_ringbuffer CreateRingBuffer(int size, int newmode) {
+  
+  struct std_ringbuffer buffer = (<std_ringbuffer>
+       rbuffer: allocate(size||MAXCOUNT, 0), 
+       mode: newmode || MODE_FIFO, 
+       next: 0 );
+
+  // Bei LIFO von oben anfangen.
+  if (newmode==MODE_LIFO)
+    buffer->next = sizeof(buffer->rbuffer);
+  
+  return buffer;
+}
+
+/** Schreibt einen neuen Wert in den Ringbuffer.
+  Diese Funktion schreibt einen neuen Wert in den Ringbuffer \a buffer und
+  loescht dabei ggf. den aeltesten Wert im Puffer.
+  \param[in,out] buffer zu aendernder Ringbuffer
+  \param[in] val hinzuzufuegender Wert
+  \sa CreateRingBuffer, ResizeRingBuffer, RingBufferGet
+  */
+protected void RingBufferPut(struct std_ringbuffer buffer, mixed val) {
+  int next = buffer->next;
+  // je nach Ausgabemodus von oben nach unten oder von unten nach oben die
+  // Werte reinschreiben.
+  switch(buffer->mode) {
+    case MODE_LIFO:
+      // next ist hier eigentlich ein 'last'. Dekrementieren und dann zum
+      // Indizieren benutzen.
+      buffer->rbuffer[--next] = val;
+      // wenn man gerade in Element 0 geschrieben hat, ist naechstes Element
+      // sizeof()-1, d.h. sizeof() als next vermerken.
+      next ||= sizeof(buffer->rbuffer);
+      break;
+    default:
+      // Wert schreiben und next inkrementieren.
+      buffer->rbuffer[next++] = val;
+      // wird sizeof() erreicht, ist das naechste Element 0. Etwas schneller
+      // waere ein Modulo mit der Buffergroesse direkt bei der Indizierung
+      // oben, aber dann kann es u.U. durch einen numerischen Overflow buggen.
+      if (next==sizeof(buffer->rbuffer))
+	next = 0;
+  }
+  buffer->next = next;
+}
+
+/** Liefert ein Array mit allen Werten des Ringbuffers \a buffer.
+  Das zurueckgelieferte Array enthaelt alle Daten des Ringbuffers in der
+  beim Erstellen des Puffers gewuenschten Reihenfolge (s. MODE_FIFO,
+  MODE_LIFO).
+  Ist der Puffer noch nicht vollstaendig gefuellt, enthalten die bisher noch
+  nie beschriebenen Elemente 0.
+  \param[in] buffer Ringpuffer, dessen Daten ausgeben werden sollen.
+  \return Array mit Daten des Puffers.
+  \sa CreateRingBuffer, RingBufferPut, ResizeRingBuffer
+  */
+protected mixed RingBufferGet(struct std_ringbuffer buffer) {
+  int size = sizeof(buffer->rbuffer);
+  int next = buffer->next;
+  mixed rbuffer = buffer->rbuffer;
+
+  switch(buffer->mode) {
+    case MODE_LIFO:
+      // der hintere Teil enthaelt die neueren Daten. Die kommen zuerst.
+      // Wenn next == sizeof(), dann kann das interne Array einfach kopiert
+      // werden.
+      if (next == size)
+	return copy(rbuffer);
+      else
+	return rbuffer[next .. size-1] + rbuffer[0 .. next-1];
+    default:
+      // der vordere Teil enthaelt die neueren Daten. Also zuerst den hinteren
+      // ausgeben, dann den vorderen.
+      // Wenn next 0, kann das interne Array einfach kopiert werden. Sonst
+      // muss es zusammengesetzt werden.
+      if (next)
+	return rbuffer[next .. size-1] + rbuffer[0 .. next-1];
+      else
+	return copy(rbuffer);
+  }
+  return 0;
+}
+
+/** Erzeugt einen neuen Ringbuffer der Groesse \a size und dem gleichen Modus
+ * wie \a buf, der dieselben Daten enthaelt wie \a buf.
+ * Diese Funktion erzeugt einen neuen Ringbuffer und kopiert alle Daten aus
+ * dem alten Puffer in den neuen um.
+ \param[in] buf alter Ringbuffer
+ \param[in] size gewuenschte neue Groesse
+ \return Liefert neuen Ringbuffer mit gewuenschten Groesse.
+ \sa CreateRingBuffer, RingBufferPut, RingBufferGet
+ \attention Wird der Ringbuffer verkleinert, kommt es zum Verlust der
+ aeltesten Daten, die nicht mehr in den neuen hineinpassen.
+ \todo Effizientere Implementation.
+ */
+protected struct std_ringbuffer ResizeRingBuffer(struct std_ringbuffer buf, 
+                                                 int size) {
+  // neuen Ringbuffer anlegen.
+  struct std_ringbuffer nbuf = CreateRingBuffer(size, buf->mode);
+  // und alle daten aus dem alten wieder reinstopfen.
+  // ja, das geht effizienter, hab ich gerade aber keinen Bock drauf. Die
+  // Funktion wird vermutlich eh sehr selten gerufen.
+  foreach(mixed val: RingBufferGet(buf))
+    RingBufferPut(nbuf, val);
+  // neuen Buffer zurueckgeben. Der alte bleibt unveraendert!
+  return nbuf;
+}
+
+/* halbfertige Implementation mit Arrays.
+#define RBUFFER   0
+#define MODE      1
+#define COUNTER   2
+
+protected mixed CreateRingBuffer(int size, int mode) {
+  mixed buffer = ({ allocate(size||MAXCOUNT, 0), 
+                    mode || MODE_FIFO, 
+	            0 });
+  if (mode==MODE_LIFO)
+    buffer[COUNTER] = sizeof(buffer[RBUFFER]);
+  return buffer;
+}
+
+protected void RingBufferPut(mixed buffer, mixed val) {
+  int counter = buffer[COUNTER];
+  switch(mode) {
+    case MODE_LIFO:
+      rbuffer[--counter] = val;
+      counter ||= sizeof(rbuffer);
+      break;
+    default:
+      rbuffer[(counter++)%sizeof(rbuffer)] = val;
+  }
+  buffer[COUNTER]=counter;
+}
+
+protected mixed RingBufferGet(mixed buffer) {
+  int size=sizeof(rbuffer);
+
+  switch(mode) {
+    case MODE_LIFO:
+      return rbuffer[counter..size] + rbuffer[0..counter-1];
+    default:
+      if (counter%size)
+	return rbuffer[(counter%size) .. size-1] 
+	       + rbuffer[0..(counter%size)-1];
+      else
+	return copy(rbuffer);
+  }
+}
+
+protected mixed test() {return rbuffer;}
+*/
diff --git a/std/util/testedit.c b/std/util/testedit.c
new file mode 100644
index 0000000..f31f6ed
--- /dev/null
+++ b/std/util/testedit.c
@@ -0,0 +1,25 @@
+// MorgenGrauen MUDlib
+//
+// util/testedit.c -- Tragbarer Editor
+//
+// $Id: testedit.c 6371 2007-07-17 22:46:50Z Zesstra $
+#pragma strict_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/thing";
+inherit "/std/util/ex";
+
+#include <properties.h>
+
+void create()
+{
+  ::create();
+  AddId("ex");
+  SetProp(P_NAME, "Editor");
+  SetProp(P_SHORT, "Ein Testeditor");
+  SetProp(P_LONG, "Testeditor: Kommando: ex\n");
+  AddCmd("ex", "ex");
+}
diff --git a/std/virtual/v_compiler.c b/std/virtual/v_compiler.c
new file mode 100644
index 0000000..80eb370
--- /dev/null
+++ b/std/virtual/v_compiler.c
@@ -0,0 +1,165 @@
+// MorgenGrauen MUDlib
+//
+// virtual/v_compiler.c -- a general virtual compiler object
+//
+// $Id: v_compiler.c 9142 2015-02-04 22:17:29Z Zesstra $
+
+// principle:
+//  - inherit this object into your own 'virtual_compiler.c'
+//  - customize Validate() and CustomizeObject() for you own sake
+//  
+//  * Validate() checks if a room filename given as argument (without path)
+//    is valid and returns this filename with stripped '.c'!!
+//  * CustomizeObject() uses the previous_object()->Function() strategy to
+//    customize the standard object (for example to set a description)
+//
+// Properties: P_STD_OBJECT, P_COMPILER_PATH
+
+#pragma strict_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+inherit "/std/thing/properties";
+
+//#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <defines.h>
+#include <v_compiler.h>
+#include <exploration.h>
+#include <sys_debug.h>
+#include <living/description.h> //fuer P_PARA
+
+// Der VC braucht das 'alte' object_name()-basierte BLUE_NAME, da sonst das
+// Konfigurieren der von einem VC-Objekt geclonten Clones via
+// CustomizeObject() nicht funktioniert (load_name() ermittelt den Namen des
+// VC-Standardobjektes)
+#ifdef BLUE_NAME
+#undef BLUE_NAME
+#endif
+#define BLUE_NAME(ob) (explode(object_name(ob),"#")[0])
+
+private nosave string last_loaded_file;
+private nosave mapping objects;
+
+void create()
+{
+  ::create();
+  seteuid(getuid());
+  SetProp(P_STD_OBJECT, "/std/room");
+  SetProp(P_COMPILER_PATH, sprintf("/%s/",
+        implode(old_explode(object_name(this_object()), "/")[0..<2], "/")));
+  SetProp(P_PARA, ({}) ); // keine Para-VC-Objekte
+  objects = ([]);
+}
+
+// von den erbenen VCs zu ueberschreiben...
+// TODO: aus Standardobjekt entfernen, weil durch P_PARA und QueryValidObject
+// obsolet. 
+int NoParaObjects() { return 0; }
+
+string Validate(string file)
+{
+  if(!file) return 0;
+  if(file[<2..] == ".c") file = file[0..<3];
+  EPMASTER->PrepareVCQuery(file);
+  return file;
+}
+
+// Die Funktion bekommt einen Objektnamen uebergeben und muss entscheiden, ob
+// dieser VC dafuer zustaendig ist, das Objekt zu generieren. Jeder Wert != 0
+// zaehlt als 'zustaendig'. Es ist eine Art generalisiertem Validate(). Fuer
+// maximale Nuetzlichkeit muss diese Funktion von den erbenden VCs
+// ueberschrieben werden.
+public int QueryValidObject(string oname) {
+    string fname, path, *pelem;
+    int para;
+    mixed ppara;
+
+    //erstmal Validate fragen
+    pelem=explode(oname,"/");
+    fname=pelem[<1];
+    if (!fname=Validate(fname))
+        return(0); //nicht zustaendig
+    // nicht im richtigen Pfad?
+    path=sprintf("%s/",implode(pelem[0..<2],"/"));
+    if (path!=QueryProp(P_COMPILER_PATH))
+        return(0);
+    // Para-Objekt?
+    if (sscanf(fname,"%s^%d",fname,para) > 1) {
+        if (NoParaObjects())
+            return(0); //direkt zurueck, keine Para-Objekte
+        // bestimmte Para-Dimensionen explizit erlaubt? (Wenn P_PARA nicht
+        // gesetzt ist, sind alle erlaubt!)
+        if (ppara=QueryProp(P_PARA)) {
+            if (pointerp(ppara) && member(ppara,para)!=-1)
+                return(1);
+            else if (intp(para) && ppara==para)
+                return(1);
+            // P_PARA gesetzt, aber gewuenschtes Para nicht enthalten...
+            else return(0);
+        }
+    }
+    return(1); //fall-through, offenbar zustaendig.
+}
+
+mixed CustomizeObject()
+{
+  string file;
+
+  if(!clonep(previous_object()))
+    return Validate(explode(BLUE_NAME(previous_object()), "/")[<1]);
+  if(stringp(last_loaded_file)) file = last_loaded_file;
+  else file = Validate(explode(BLUE_NAME(previous_object()), "/")[<1]);
+  if(!file) return 0;
+  last_loaded_file = 0;
+  return file;
+}
+
+// add a new object to the object list if it compiles
+private mixed AddObject(string file)
+{
+  object ob;
+  string err;
+
+  // clean up the object list
+  objects = filter_indices(objects, function int (string f) {
+      return (objectp(objects[f])); } );
+
+  last_loaded_file = file;
+  // register new object
+  if(ob = clone_object(QueryProp(P_STD_OBJECT)))
+    objects[file] = ob;
+  return ob;
+}
+
+// try to create an object for the wanted file
+mixed compile_object(string file)
+{
+  // validate if the file name is a correct one
+  if(file = Validate(file))
+    return AddObject(file);
+  return 0;
+}  
+
+// return all cloned virtual objects
+mixed QueryObjects()
+{
+  return m_values(objects)-({0});
+}
+
+// clean up rooms that have not been destructed yet
+int remove() {
+
+  if(!mappingp(objects)) return 0;
+
+  //for(ob = QueryObjects(); sizeof(ob); ob = ob[1..])
+  foreach(object ob: QueryObjects()) {
+      ob->remove();
+      if(objectp(ob)) destruct(ob);
+  }
+  return 1;
+}
+
diff --git a/std/weapon.c b/std/weapon.c
new file mode 100644
index 0000000..7c1f97c
--- /dev/null
+++ b/std/weapon.c
@@ -0,0 +1,54 @@
+// MorgenGrauen MUDlib
+//
+// weapon.c -- weapon standard object
+//
+// $Id: weapon.c 7804 2011-07-10 20:37:52Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+//#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/properties";
+inherit "/std/thing/language";
+inherit "/std/thing/commands";
+inherit "/std/thing/restrictions";
+inherit "/std/thing/light";
+inherit "/std/weapon/moving";
+inherit "/std/weapon/combat";
+inherit "/std/weapon/description";
+inherit "/std/thing/envchk";
+
+//#define NEED_PROTOTYPES
+
+#define <thing/properties.h>
+
+#include <properties.h>
+#include <moving.h>
+#include <defines.h>
+
+void create()
+{
+  seteuid(getuid());
+  properties::create();
+  commands::create();
+  light::create();
+  restrictions::create();
+  combat::create();
+  description::create();
+  envchk::create();
+  AddId(({"Ding","waffe"}));
+}
+
+/*
+void init()
+{
+  commands::init();
+  description::init();
+}
+*/
+
+void reset()  // Man kann in ALLEN Standardobjekten ::reset aufrufen!
+{ }
+
diff --git a/std/weapon/combat.c b/std/weapon/combat.c
new file mode 100644
index 0000000..cb10ff5
--- /dev/null
+++ b/std/weapon/combat.c
@@ -0,0 +1,819 @@
+// MorgenGrauen MUDlib
+//
+// weapon/combat.c -- combat part of the weapon standard object
+//
+// $Id: combat.c 9425 2016-01-02 18:19:40Z Zesstra $
+
+#include <sys_debug.h>
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <thing/commands.h>
+#include <thing/description.h>
+#include <properties.h>
+#include <language.h>
+#include <combat.h>
+#include <attributes.h>
+#include <defines.h>
+#include <moving.h>
+#include <new_skills.h>
+
+// Globale Variablen
+               int     ftime, flaw;
+private nosave int     logged;
+private nosave closure defend_cl, hit_cl;
+
+void create() 
+{
+    // Variablen initialisieren
+    logged=ftime=flaw=0;
+    defend_cl=0;
+    hit_cl=0;
+
+    // Einige Grundwerte setzen
+    SetProp(P_WEAPON_TYPE, WT_CLUB);
+    SetProp(P_DAM_TYPE, ({DT_BLUDGEON}));
+    SetProp(P_NR_HANDS, 2);
+    SetProp(P_PARRY,PARRY_NOT);
+    SetProp(P_AC,0);
+    Set(P_RESTRICTIONS,([]),F_VALUE);
+    Set(P_LAST_USE,time(),F_VALUE);
+
+    // Einige Properties sollten nicht von aussen gesetzt werden koennen
+    Set(P_PARRY,   PROTECTED, F_MODE_AS);
+    Set(P_WIELDED, PROTECTED, F_MODE_AS);
+    Set(P_LAST_USE,PROTECTED, F_MODE_AS);
+    
+    // Diese kann von aussen gesetzt werden (noch), aber bitte nur ueber die
+    // hier definierte Setmethode.
+    Set(P_DAMAGED, PROTECTED, F_MODE_AS);
+
+    // Eine Waffe benoetigt Kommandos, mit denen man sie zuecken 
+    // und wegstecken kann
+    AddCmd( ({"zueck","zuecke","zieh","ziehe"}), "wield" );
+    AddCmd( ({"steck","stecke"}), "unwield" );
+}
+
+// aktuelles Lebewesen, was diese Waffe zur Zeit gezueckt hat.
+public object QueryUser()
+{
+  return QueryProp(P_WIELDED);
+}
+
+/*
+ * Ausgabe einer Meldung beim Zuecken geht nur an Spieler, nicht an NPC.
+ * Die Umgebung bekommt natuerlich immer eine Meldung.
+ */
+void doWieldMessage()
+{
+  string *str, s1;
+  
+  if(QueryProp(P_WIELD_MSG))  // Ist eine WieldMsg gesetzt?
+  {
+    if(closurep(QueryProp(P_WIELD_MSG)))  // Sogar als eigene Fkt.?
+    {
+      str = funcall(QueryProp(P_WIELD_MSG),this_player());
+
+      if(interactive(this_player()))
+      {
+        this_player()->ReceiveMsg(str[0],
+            MT_NOTIFICATION|MSG_BS_LEAVE_LFS, MA_WIELD, 0,
+            this_player());
+      }
+    
+      if ( objectp(environment()) && objectp(environment(environment())) )
+          send_room(environment(environment()),
+              str[1],
+              MT_LOOK|MSG_BS_LEAVE_LFS,
+              MA_WIELD, 0, ({this_player()}), environment());
+      return;
+    }
+    else if(interactive(this_player()))
+    {
+      s1 = replace_personal(sprintf(QueryProp(P_WIELD_MSG)[0],"@WEN2"),
+		      ({this_player(),this_object()}), 1);
+
+      this_player()->ReceiveMsg(s1,
+          MT_NOTIFICATION|MSG_BS_LEAVE_LFS, MA_WIELD, 0,
+          this_player()); 
+    }
+
+    // Abwaertskompatibel: Das erste %s wird zu WEN1, das zweite zu WEN2
+    s1 = replace_personal(sprintf(QueryProp(P_WIELD_MSG)[1],"@WEN1","@WEN2"),
+		    ({this_player(), this_object()}), 1);
+        
+    if ( objectp(environment()) && objectp(environment(environment())) )
+        send_room(environment(environment()),
+              s1,
+              MT_LOOK|MSG_BS_LEAVE_LFS,
+              MA_WIELD, 0, ({this_player()}), environment());
+    return;
+  }
+  /*
+   * Keine WieldMsg gesetzt, Ausgabe der Default-Meldungen.
+   */
+  else if(interactive(this_player()))
+  {
+    this_player()->ReceiveMsg(
+        "Du zueckst "+name(WEN,1)+".",
+        MT_NOTIFICATION, MA_WIELD, 0, this_player());
+  }
+  
+  //s.o. ersetzt durch tell_room()
+  //say(break_string(this_player()->Name(WER)+" zueckt "
+  // +name(WEN,0)+".",78),({ this_player() }));
+  if ( objectp(environment()) && objectp(environment(environment())) )
+      send_room(environment(environment()),    
+          this_player()->Name(WER)+" zueckt "+name(WEN,0)+".",
+          MT_LOOK,
+          MA_WIELD, 0, ({this_player()}), environment());
+}
+
+/*
+ * Ausgabe einer Meldung beim Wegstecken geht nur an Spieler, nicht an NPC.
+ * Die Umgebung bekommt immer eine Meldung.
+ */
+void doUnwieldMessage(object wielded_by)
+{
+  string *str,s1;
+
+  if(!objectp(wielded_by))  // Hoops! Gar nicht gezueckt? Abbruch!
+  {
+    return;
+  }
+
+  if(QueryProp(P_UNWIELD_MSG))  // Hier gibt es eine UnwieldMsg.
+  {
+    if(closurep(QueryProp(P_UNWIELD_MSG)))  // Sogar als Closure, wow :-)
+    {
+      str = funcall(QueryProp(P_UNWIELD_MSG),wielded_by);
+
+      if(interactive(wielded_by))
+      { 
+        wielded_by->ReceiveMsg(str[0],
+          MT_NOTIFICATION|MSG_BS_LEAVE_LFS, MA_UNWIELD, 0, wielded_by);
+      }
+      if ( objectp(environment()) && objectp(environment(environment())) )
+          send_room(environment(environment()),
+              str[1],
+              MT_LOOK|MSG_BS_LEAVE_LFS,
+              MA_UNWIELD, 0, ({wielded_by}), environment());
+      return;
+    }
+  
+    else if(interactive(wielded_by))
+    {
+      s1 = replace_personal(sprintf(QueryProp(P_UNWIELD_MSG)[0],"@WEN2"),
+		      ({this_player(),this_object()}), 1); 
+      wielded_by->ReceiveMsg(s1,
+          MT_NOTIFICATION|MSG_BS_LEAVE_LFS, MA_UNWIELD, 0, wielded_by);
+    }
+
+    s1 = replace_personal(sprintf(QueryProp(P_UNWIELD_MSG)[1],"@WEN1","@WEN2"),
+		    ({wielded_by, this_object()}), 1);
+    
+    if ( objectp(environment()) && objectp(environment(environment())) )
+          send_room(environment(environment()),
+              s1,
+              MT_LOOK|MSG_BS_LEAVE_LFS,
+              MA_UNWIELD, 0, ({wielded_by}), environment());
+    return;
+  }
+  /*
+   * Keine UnwieldMsg, also Default-Meldungen ausgeben.
+   */
+  else if(interactive(wielded_by))
+  {
+    wielded_by->ReceiveMsg(
+        "Du steckst "+name(WEN,1)+" zurueck.",
+        MT_NOTIFICATION, MA_UNWIELD, 0, wielded_by);
+  }
+  if ( objectp(environment()) && objectp(environment(environment())) )
+      send_room(environment(environment()),
+              wielded_by->Name(WER) +" steckt "+name(WEN,0)+" zurueck.",
+              MT_LOOK,
+              MA_UNWIELD, 0, ({wielded_by}), environment()); 
+}
+
+// Diese Funktion wird aufgerufen, wenn die Waffe wirklich gezueckt wird
+protected void InformWield(object pl, int silent)
+{
+    return;
+}
+     
+// Diese Funktion wird aufgerufen, wenn die Waffe wirklich weggesteckt
+// wird
+protected void InformUnwield(object pl, int silent) 
+{
+    return;
+}
+
+
+// wield_me soll knallen. 
+varargs int wield_me(int silent) 
+{
+  raise_error("wield_me() ist veraltet. Bitte nutze DoWield()\n");
+  return 1;
+}
+
+// Die Funktion, die das eigentliche Zuecken durchfuehrt.
+varargs int DoWield(int silent) 
+{   int     dex, parry;
+    closure cl;
+    mixed   res;
+  
+    // Man kann nur Waffen zuecken, die man auch im Inventory hat.
+    if (environment()!=PL) 
+    {
+        _notify_fail( break_string(
+            "Du musst "+name(WEN,1)+" schon erst nehmen!",78) );
+        return 0;
+    }
+
+    // Eine gezueckte Waffe kann man natuerlich nicht nochmal zuecken
+    if (QueryProp(P_WIELDED)) 
+    {
+        notify_fail("Das hast Du schon gemacht!\n");
+        return 0;
+    }
+
+    // Waffen, die ein oder mehrere Attribut veraendern und gegen
+    // das gesetzte Limit verstossen, haben keine Wirkung bezueglich der
+    // Attribute.
+    if ( mappingp(res=QueryProp(P_M_ATTR_MOD)) && PL->TestLimitViolation(res) )
+    {
+        write(break_string(
+            "Irgendetwas an Deiner Ausruestung verhindert, dass Du Dich mit "+
+            name(WEM,1)+" so richtig wohl fuehlst.",78));
+    }
+
+    // Ueber P_RESTRICTIONS kann man einige Restriktionen definieren, ohne
+    // gleich auf eine WieldFunc zurueckgreifen zu muessen.
+    // Die Auswertung erfolgt ueber den RestrictionChecker
+    if ( (res=QueryProp(P_RESTRICTIONS)) && mappingp(res) &&
+         (res=(string)call_other("/std/restriction_checker","check_restrictions",
+             PL,res)) && stringp(res) ) 
+    {
+        notify_fail(res);
+        return 0;
+    }
+
+    parry=(int)QueryProp(P_PARRY);
+    dex=(int)PL->QueryAttribute(A_DEX);
+
+    // Testen, ob der Spieler die noetige Geschicklichkeit besitzt, um
+    // mit dieser (Parier)Waffe umgehen zu koennen
+    if ( ((parry<PARRY_ONLY) && ((dex+8)*10)<QueryProp(P_WC)) || 
+         ((parry>PARRY_NOT)  && ((dex+5)*2 )<QueryProp(P_AC)) )
+    {
+        notify_fail(
+            "Du bist nicht geschickt genug, um mit dieser Waffe umzugehen.\n");
+        return 0;
+    }
+
+    // Eine Gezueckte Waffe muss natuerlich erst mal weggesteckt werden.
+    if ( (parry<PARRY_ONLY) && objectp(res=(object)PL->QueryProp(P_WEAPON)) )
+    {
+        if ( (res->DoUnwield(silent)) && !((object)PL->QueryProp(P_WEAPON)) )
+        {
+            // Wenn die alte Waffe weggesteckt werden konnte, nochmal
+            // versuchen zu zuecken
+            return DoWield(silent);
+        }
+        else 
+        {
+            // Sonst Meldung ausgeben
+            notify_fail("Das geht nicht, solange Du noch eine andere "+
+                 "Waffe gezueckt hast.\n");
+            return 0;
+        }
+    }
+    // Das gleiche gilt natuerlich fuer Parierwaffen
+    if ( (parry>PARRY_NOT) && objectp(res=(object)PL->QueryProp(P_PARRY_WEAPON)) )
+    {
+        if ( (res->DoUnwield(silent)) && !(PL->QueryProp(P_PARRY_WEAPON)) )
+        {
+            // Wenn die alte Parierwaffe weggesteckt werden konnte, nochmal
+            // versuchen zu zuecken
+            return DoWield(silent);
+        }
+        else 
+        {
+            // Sonst Meldung ausgeben
+            notify_fail("Das geht nicht, solange Du noch eine andere "+
+                 "Paierwaffe gezueckt hast.\n");
+            return 0;
+        }
+    }
+
+    // Ist eine WieldFunc gesetzt, wird diese aufgerufen.
+    if (objectp(res=QueryProp(P_WIELD_FUNC)) 
+	&& !(res->WieldFunc(ME,silent,environment())))
+    {
+        // Eine Meldung sollte schon von der WieldFunc ausgegeben werden.
+        return 1;
+    }
+
+    // Die zulaessigen Hoechstwerte duerfen natuerlich nicht
+    // ueberschritten werden.
+    if ( (parry<PARRY_ONLY) && (QueryProp(P_WC)>MAX_WEAPON_CLASS) )
+    {
+        notify_fail(
+            "Ungueltige Waffenklasse, bitte Erzmagier verstaendigen.\n");
+        return 0;
+    }
+    if ( (parry>PARRY_NOT) && (QueryProp(P_AC)>MAX_PARRY_CLASS) )
+    {
+        notify_fail(
+            "Ungueltige Parierklasse, bitte Erzmagier verstaendigen.\n");
+        return 0;
+    }
+
+    // Testen, ob der Zuecker genug Haende frei hat.  
+    if (!(PL->UseHands(ME,QueryProp(P_NR_HANDS))))
+    {
+        notify_fail("Du hast keine Hand mehr frei.\n");
+        return 0;
+    }
+
+    // Ok, jetzt ist alles klar, die (Parier)Waffe wird gezueckt
+    SetProp(P_WIELDED, PL);
+    SetProp(P_EQUIP_TIME,time());
+
+    cl=symbol_function("SetProp",PL);
+
+    if (parry<PARRY_ONLY)
+    {
+        // Dieses Objekt als Waffe setzen
+        funcall(cl,P_WEAPON, ME );
+    }
+    if (parry>PARRY_NOT)
+    {
+        // Dieses Objekt als Parierwaffe setzen
+        funcall(cl,P_PARRY_WEAPON, ME );
+    }
+
+    // Waffen koennen Attribute aendern/blockieren. Also muessen diese
+    // nach dem Zuecken aktualisiert werden
+    PL->register_modifier(ME);
+    PL->UpdateAttributes();
+
+    // P_TOTAL_AC/P_TOTAL_WC im Spieler aktualisieren. Da dort Attribute
+    // eingehen, kann das erst hier gemacht werden.
+    if (parry<PARRY_ONLY)
+    {
+        PL->QueryProp(P_TOTAL_WC);
+    }
+    if (parry>PARRY_NOT)
+    {
+        PL->QueryProp(P_TOTAL_AC);
+    }
+
+    // Zueck-Meldung ausgeben, wenn das silent-Flag nicht gesetzt ist
+    if (!silent)
+    {
+        doWieldMessage();
+    }
+
+    // Alle Waffen werden im awmaster registriert, sobald sie von einem
+    // Spieler gezueckt werden
+    if (!logged && query_once_interactive(PL)) 
+    {
+        call_other("/secure/awmaster","RegisterWeapon",ME);
+        logged=1;
+    }
+
+    // Inform-Funktion aufrufen
+    InformWield(PL,silent);
+
+    // Fertig mit dem Zuecken
+    return 1;
+}
+
+// Die Funktion, die das eigentliche Wegstecken durchfuehrt.
+varargs int DoUnwield(int silent) 
+{   closure cl;
+    int     parry;
+    mixed   res;
+    object  wielded_by;
+
+    // Waffen, die nicht gezueckt sind, kann man natuerlich nicht
+    // wegstecken
+    if (!objectp(wielded_by=QueryProp(P_WIELDED)))
+    {
+        return 0;
+    }
+
+    // Ist eine UnwieldFunc gesetzt, wird diese aufgerufen
+    if ( objectp(res=QueryProp(P_UNWIELD_FUNC)) &&
+         !(res->UnwieldFunc(ME,silent,wielded_by)) ) 
+    {
+        // Eine Meldung muss die UnwieldFunc schon selbst ausgeben.
+        return 1;
+    }
+
+    // Eine verfluchte Waffe kann man natuerlich nicht wegstecken - aber
+    // auch da gibts Ausnahmefaelle, wie z.B. den Tod eines Spielers
+    if ((res=QueryProp(P_CURSED)) && !(silent&M_NOCHECK)) 
+    {
+        if (stringp(res))
+        {
+            // Stand in P_CURSED ein String? Dann diesen ausgeben
+            tell_object(wielded_by, 
+                (res[<1]=='\n' ? res : break_string(res,78)));
+        }
+        else
+        {
+            // Sonst eine Standard-Meldung ausgeben
+            tell_object(wielded_by, break_string(
+                "Du kannst "+name(WEN)+" nicht wegstecken.",78));
+        }
+        return 1;
+    }
+
+    // Ok, jetzt ist alles klar, die (Parier)Waffe wird weggesteckt
+    parry=QueryProp(P_PARRY);
+    cl=symbol_function("SetProp",wielded_by);
+
+    if (parry<PARRY_ONLY)
+    {
+        // Eintrag als Waffe im Spieler loeschen
+        funcall(cl,P_WEAPON, 0);
+    }
+    if (parry>PARRY_NOT)
+    {
+        // Eintrag als Parierwaffe im Spieler loeschen
+        funcall(cl,P_PARRY_WEAPON, 0);
+    }
+
+    // Im Spieler die Zeit setzen, zu der er zuletzt eine Waffe weggesteckt
+    // hat.
+    funcall(cl,P_UNWIELD_TIME,time());
+
+    // Meldung ausgeben, wenn silent-Flag nicht gesetzt ist
+    if (!silent) 
+    {
+        doUnwieldMessage(wielded_by);
+    } 
+
+    // Die Haende, die bisher von der Waffe benutzt wurden, freigeben
+    wielded_by->FreeHands(ME);
+    SetProp(P_WIELDED, 0);
+
+    // Waffen koennen Attribute aendern/blockieren. Also muessen diese
+    // nach dem Wegstecken aktualisiert werden
+    wielded_by->deregister_modifier(ME);
+    wielded_by->UpdateAttributes();
+
+    // P_TOTAL_AC/P_TOTAL_WC im Spieler aktualisieren. Da dort Attribute
+    // eingehen, kann das erst hier gemacht werden.
+    if (parry<PARRY_ONLY)
+    {
+        wielded_by->QueryProp(P_TOTAL_WC);
+    }
+    if (parry>PARRY_NOT)
+    {
+        wielded_by->QueryProp(P_TOTAL_AC);
+    }
+
+    // Inform-Funktion aufrufen
+    InformUnwield(wielded_by, silent);
+
+    // Fertig mit dem Wegstecken
+    return 1;
+}
+
+// Die Funktion, die das "zuecken"-Kommando auswertet
+varargs int wield(string str, int silent) 
+{
+    if ( !stringp(str) ||
+         (query_verb()[0..3]=="zieh" && sscanf(str,"%s hervor",str)!=1) )
+    {
+        return 0;
+    }
+
+    if (!id(str)) 
+    {
+        _notify_fail("Du hast sowas nicht.\n");
+        return 0;
+    }
+
+    return DoWield(silent);
+}
+
+// Die Funktion, die das "wegstecken"-Kommando auswertet
+int unwield(string str) 
+{   int    parry;
+    string dummy;
+
+    // Erstmal die Eingabe auswerten. Ist dies wirklich ein Kommando
+    // zum Wegstecken?
+    if ( !stringp(str) || (sscanf(str,"%s weg",     dummy)!=1  && 
+                           sscanf(str,"%s ein",     dummy)!=1  &&
+                           sscanf(str,"%s zurueck", dummy)!=1 ) )
+    {
+        return 0;
+    }
+
+    str = dummy;
+    parry=QueryProp(P_PARRY);
+
+    // Ist wirklich diese Waffe gemeint?
+    if ( !stringp(str) || !id(str) ||
+         ((parry<PARRY_ONLY)&&((object)PL->QueryProp(P_WEAPON)!=ME)) ||
+         ((parry>PARRY_NOT)&&((object)PL->QueryProp(P_PARRY_WEAPON)!=ME)) )
+    {
+        return 0;
+    }
+
+    // Man kann nur Waffen wegstecken, die man auch bei sich hat
+    if (environment() != PL) 
+    {
+        _notify_fail("Diese Waffe gehoert Dir nicht!\n");
+        return 0;
+    }
+
+    // Und natuerlich geht das auch nur, wenn die Waffe gezueckt ist.
+    if (!QueryProp(P_WIELDED)) 
+    {
+        _notify_fail("Diese Waffe hast Du gar nicht gezueckt ...\n");
+        return 0;
+    }
+
+    return DoUnwield();
+}
+
+// Die Funktion, die den Schaden berechnet, den die Waffe verursacht
+int QueryDamage(object enemy)
+{   int    dam;
+    mixed  hit_func;
+    object wielder;
+
+    // Nur gezueckte Waffen machen Schaden
+    if (!objectp(wielder=QueryProp(P_WIELDED)))
+        return 0;
+
+    // Den Basis-Schaden berechnen. Die Staerke des Benutzers wird
+    // hier beruecksichtigt.
+    dam = (2*QueryProp(P_WC)+10*((int)wielder->QueryAttribute(A_STR)))/3;
+
+    // Wie gut man getroffen hat, wird ueber ein random() simuliert
+    dam = random(1+dam);
+
+    // Ist eine HitFunc gesetzt, dann wird diese ausgewertet. Der 
+    // Rueckgabe-Wert wird zum Schaden addiert
+    if (!hit_cl || !get_type_info(hit_cl,2))
+    {
+        if (objectp(hit_func=QueryProp(P_HIT_FUNC)))
+        {
+            hit_cl=symbol_function("HitFunc",hit_func);
+        }
+    }
+    if (hit_cl && get_type_info(hit_cl,2))
+    {
+        dam += funcall(hit_cl,enemy);
+    }
+
+    // Zeitpunkt der letzten Benutzung ausgeben
+    SetProp(P_LAST_USE,time());
+
+    // Berechneten Schaden zurueckgeben
+    return dam;
+}
+
+// Die Funktion, die bei Parierwaffen den Schutzwert berechnet.
+int QueryDefend(string* dam_type, mixed spell, object enemy) 
+{   int     prot;
+    mixed   def_func;
+    object  pl;
+
+    prot = 0;
+
+    // Ruestungen schuetzen nur gegen physikalischen Schaden
+    if (!spell || (mappingp(spell) && spell[SP_PHYSICAL_ATTACK]))
+    {
+        if (sizeof(filter(dam_type,PHYSICAL_DAMAGE_TYPES)))
+        {
+            prot = random(1+QueryProp(P_AC));
+        }
+    }
+
+    // Ist eine DefendFunc gesetzt, wird diese ausgewertet
+    if (!defend_cl || !get_type_info(defend_cl,2))
+    {
+        if (objectp(def_func=QueryProp(P_DEFEND_FUNC))) 
+        {
+            defend_cl=symbol_function("DefendFunc",def_func);
+        }
+    }
+    //Bei Netztoten keine (zurueckschlagende) DefendFunc
+    if (defend_cl && get_type_info(defend_cl,2) &&
+        objectp(pl=QueryProp(P_WIELDED)) && (!query_once_interactive(pl) ||
+        interactive(pl)) )
+    {
+        // Der Rueckgabewert der DefendFunc wird zum Schutz addiert
+        prot += funcall(defend_cl, dam_type, spell, enemy);
+    }
+
+    // Zeitpunkt der letzten Benutzung ausgeben
+    SetProp(P_LAST_USE,time());
+
+    // Berechneten Schutz zurueckgeben
+    return prot;
+}
+
+// Die Anzahl der von einer Waffe benoetigten Haende darf natuerlich nicht
+// kleiner als 1 sein.
+int _set_nr_hands(int arg)
+{
+    if (!intp(arg) || (arg<1) )
+        return Query(P_NR_HANDS, F_VALUE);
+    return Set(P_NR_HANDS, arg, F_VALUE);
+}
+
+// Der Schadenstyp einer Waffe darf zwar als string angegeben werden, wird
+// intern aber immer als array gespeichert
+mixed _set_dam_type(mixed arg)
+{
+    if (pointerp(arg))
+    {
+        return Set(P_DAM_TYPE, arg, F_VALUE);
+    }
+    else if (stringp(arg))
+    {
+        return Set(P_DAM_TYPE, ({ arg }), F_VALUE);
+    }
+    return Query(P_DAM_TYPE, F_VALUE);
+}
+
+// Objekte, die die Beschaedigung einer Waffe durch direktes Setzen von
+// P_DAMAGED durchfuehren, werden im awmaster geloggt
+mixed _set_item_damaged(mixed arg)
+{
+    if (arg && !intp(arg))
+    {
+        return Query(P_DAMAGED, F_VALUE);
+    }
+
+    if (previous_object(1))
+      call_other("/secure/awmaster","RegisterDamager",
+            previous_object(1),QueryProp(P_DAMAGED),arg);
+    
+    return Set(P_DAMAGED,arg,F_VALUE);
+}
+
+// Wird etwas an P_HIT_FUNC geaendert, muss die zugehoerige closure
+// erstmal geloescht werden.
+mixed _set_hit_func(mixed arg)
+{
+    hit_cl=0;
+    return Set(P_HIT_FUNC, arg, F_VALUE);
+}
+
+// Wird etwas an P_DEFEND_FUNC geaendert, muss die zugehoerige closure
+// erstmal geloescht werden.
+mixed _set_defend_func(mixed arg)
+{
+    defend_cl=0;
+    return Set(P_DEFEND_FUNC, arg, F_VALUE);
+}
+
+// Die maximale Waffenklasse einer Waffe berechnet sich natuerlich aus
+// der aktuellen Waffenklasse und der Beschaedigung. Eine Ausnahme bilden
+// hier Waffen, deren effektive Waffenklasse groesser ist als diese Summe
+int  _query_max_wc() 
+{   int a,b;
+
+    a=QueryProp(P_WC)+QueryProp(P_DAMAGED);
+    b=QueryProp(P_EFFECTIVE_WC);
+    if (b>a)
+        return b;
+    return a;
+}
+
+// Will man eine Waffe beschaedigen oder reparieren, so macht man das
+// am besten ueber die Funktion Damage(argument). Positive Argumente
+// bedeuten eine Beschaedigung, negative eine Reparatur. Der Rueckgabewert
+// ist die wirklich durchgefuehrte Aenderung des Beschaedigungswertes
+int Damage(int new_dam)
+{   int    wc,old_dam;
+    object w;
+
+    // Uebergebenes Argument pruefen
+    if (!new_dam || !intp(new_dam))
+    {
+        return 0;
+    }
+
+    // Bei Waffen, die nicht ausschliesslich zur Parade eingesetzt werden,
+    // geht die Beschaedigung auf die Kampfkraft, sprich: P_WC
+    if (QueryProp(P_PARRY)<PARRY_ONLY)
+    {
+        if ((wc=QueryProp(P_WC))<=MIN_WEAPON_CLASS && new_dam>0)
+        {
+            // Sonst wuerde Beschaedigung zur Reparatur fuehren
+            return 0;
+        }
+
+        // Min-WC und Max-WC beachten
+        if ((wc-new_dam) < MIN_WEAPON_CLASS)
+        {
+            new_dam = wc-MIN_WEAPON_CLASS;
+        }
+        else if ((wc-new_dam) > MAX_WEAPON_CLASS)
+        {
+            new_dam = wc-MAX_WEAPON_CLASS;
+        }
+
+        // Nie mehr als 100% reparieren
+        if (((old_dam=QueryProp(P_DAMAGED))<-new_dam) && new_dam<0)
+        {
+            new_dam=-old_dam;
+        }
+
+        // Aenderungen an der Waffenklasse und der dem Beschaedigungswert
+        // durchfuehren
+        SetProp(P_WC,(wc-new_dam));
+        // Ausnahmeweise Set, um die loggende Setmethode zu umgehen.
+	// TODO: SetProp, sobald direktes Beschaedigen raus ist.
+	Set(P_DAMAGED, old_dam+new_dam, F_VALUE);
+
+        // P_TOTAL_WC im Traeger updaten, so vorhanden
+        if (objectp(w=QueryProp(P_WIELDED)))
+            w->QueryProp(P_TOTAL_WC);
+
+        // Rueckgabewert: Durchgefuehrte Aenderung an P_DAMAGE
+        return new_dam;
+    }
+
+    // Bei reinen Parierwaffen geht die Beschaedigung auf die
+    // Schutzwirkung, sprich: P_AC
+
+    if ((wc=QueryProp(P_AC))<=0 && new_dam>0)
+    {
+        // Sonst wuerde Beschaedigung zur Reparatur fuehren
+        return 0;
+    }
+    
+    // Min-AC=0 und Max-AC beachten
+    if ((wc-new_dam) < MIN_PARRY_CLASS)
+    {
+        new_dam = wc-MIN_PARRY_CLASS;
+    }
+    else if ((wc-new_dam) > MAX_PARRY_CLASS)
+    {
+        new_dam = wc-MAX_PARRY_CLASS;
+    }
+ 
+    // Nie mehr als 100% reparieren
+    if (((old_dam=QueryProp(P_DAMAGED))<-new_dam) && new_dam<0)
+    {
+        new_dam=-old_dam;
+    }
+
+    // Aenderungen an der Ruestungsklasse und dem Beschaedigungswert
+    // durchfuehren
+    SetProp(P_AC,wc-new_dam);
+    // Ausnahmeweise Set, um die loggende Setmethode zu umgehen.
+    // TODO: SetProp, sobald direktes Beschaedigen raus ist.
+    Set(P_DAMAGED,old_dam+new_dam, F_VALUE);
+
+    // P_TOTAL_AC im Traeger updaten, so vorhanden
+    if (objectp(w=QueryProp(P_WIELDED)))
+        w->QueryProp(P_TOTAL_AC);
+    
+    // Rueckgabewert: Durchgefuehrte Aenderung an P_DAMAGE
+    return new_dam;
+}
+
+// Wird die Waffe einer Belastung ausgesetzt (z.B. wenn man damit
+// zuschlaegt), dann wird TakeFlaw() aufgerufen.
+varargs void TakeFlaw(object enemy)
+{   int c;
+
+    // Flaw-Wert erhoehen
+    flaw++;
+
+    // Ist der Waffe eine Qualitaet gesetzt worden, so kann es zu einer
+    // allmaehlichen Beschaedigung der Waffe kommen
+    if ((c=QueryProp(P_QUALITY)) && !(flaw%c))
+        Damage(1);
+
+    // Zeitpunkt des ersten Aufrufes festhalten
+    if (!ftime)
+        ftime=time();
+}
+
+// Die Flaw-Daten koennen natuerlich auch abgerufen werden
+mixed *QueryFlaw()
+{
+    return({flaw,ftime,dtime(ftime)});
+}
+
diff --git a/std/weapon/description.c b/std/weapon/description.c
new file mode 100644
index 0000000..aad2938
--- /dev/null
+++ b/std/weapon/description.c
@@ -0,0 +1,108 @@
+// MorgenGrauen MUDlib
+//
+// weapon/description.c -- weapon description handling
+//
+// $Id: description.c 6475 2007-09-19 20:56:40Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+//#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <properties.h>
+#include <combat.h>
+#include <thing/material.h>
+
+inherit "/std/thing/description";
+
+void create()
+{
+  ::create();
+  SetProp(P_DAM_DESC,DFLT_DAM_DESC);
+}
+
+string dam_descr()
+{   string re;
+    mixed desc;
+    int max,dam,pos;
+
+    if (!QueryProp(P_NAME) || !QueryProp(P_DAMAGED) || !QueryProp(P_SHORT) ||
+        !(desc=QueryProp(P_DAM_DESC)) || (!stringp(desc) && !pointerp(desc)))
+        return "";
+    re = capitalize(name(WER,2))+" ";
+    max = QueryProp(P_WC)+(dam=QueryProp(P_DAMAGED));
+    // Bei reinen Parierwaffen den AC als max nehmen!
+    if (QueryProp(P_PARRY)==PARRY_ONLY)
+    {
+	    max=QueryProp(P_AC)+dam;
+    }
+    if (stringp(desc))
+        return (dam>(max/2))?(re+desc+".\n"):"";
+    pos = (sizeof(desc)*dam/max);
+    // Sonst koennen Parierwaffen, die Schrott sind, buggen
+    if (pos==sizeof(desc)) pos--;
+    if (stringp(desc[pos]))
+        return (re+desc[pos]+".\n");
+    return "";
+}
+
+string short()
+{   string s;
+
+    if (!(s=QueryProp(P_SHORT)))
+        return 0;
+    return s + (QueryProp(P_WIELDED)?" (gezueckt).\n":".\n");
+}
+
+varargs string long()
+{   
+    return (process_string(QueryProp(P_LONG)||"") + (dam_descr()||""));
+}
+
+mixed _query_size() {
+  mixed res, wt;
+  if (intp(res=Query(P_SIZE)) && (res>0))
+    return res;
+  wt=QueryProp(P_WEAPON_TYPE);
+  switch (wt) {
+    case WT_SWORD  : return 100;  // default: Langschwert
+    case WT_AXE    : return  80;
+    case WT_CLUB   : return  80;
+    case WT_SPEAR  : return 180;
+    case WT_KNIFE  : return  20;
+    case WT_WHIP   : return 200;
+    case WT_STAFF  : return 150;
+  }
+  return 10;  // alles andere
+}
+
+mapping _query_material() {
+  mixed res,wt;
+
+  if (mappingp(res=Query(P_MATERIAL)))
+    return res;
+  wt=QueryProp(P_WEAPON_TYPE);
+  switch(wt) {
+    case WT_SWORD:
+    return ([MAT_MISC_METAL:100]);
+    case WT_KNIFE:
+    return ([MAT_MISC_METAL:80,MAT_MISC_WOOD:20]);
+    case WT_AXE:
+    return ([MAT_MISC_METAL:50,MAT_MISC_WOOD:50]);
+    case WT_SPEAR:
+    return ([MAT_MISC_METAL:20,MAT_MISC_WOOD:80]);
+    case WT_STAFF:
+    case WT_CLUB:
+    return ([MAT_MISC_WOOD:100]);
+  }
+  return ([MAT_MISC_METAL:100]);
+}
+
+// P_BALANCED_WEAPON und P_TECHNIQUE sind mangels umgesetztem Konzept durch
+// EM-Beschluss fuer obsolet erklaert worden. Zesstra. 26.06.2007
+
diff --git a/std/weapon/moving.c b/std/weapon/moving.c
new file mode 100644
index 0000000..ce81770
--- /dev/null
+++ b/std/weapon/moving.c
@@ -0,0 +1,36 @@
+// MorgenGrauen MUDlib
+//
+// weapon/moving.c -- Bewegen/Zerstoeren von Waffen
+//
+// $Id: moving.c 6312 2007-05-20 22:40:51Z Zesstra $
+
+#pragma strict_types
+#pragma save_types
+#pragma no_clone
+#pragma pedantic
+#pragma range_check
+
+inherit "/std/thing/moving";
+
+//#define NEED_PROTOTYPES 1
+
+#include <thing/properties.h>
+#include <properties.h>
+#include <moving.h>
+#include <defines.h>
+
+varargs int DoUnwield(int silent);
+
+varargs int move(mixed dest, int method)
+{
+  DoUnwield( method & (M_SILENT|M_NOCHECK));
+  if ((method & M_NOCHECK) || (!QueryProp(P_WIELDED)))
+    return ::move(dest, method );
+}
+
+varargs int remove(int silent )
+{
+  DoUnwield( silent );
+  if (!QueryProp(P_WIELDED))
+    return ::remove( silent );
+}