Added public files

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