Added Padreics moving NPC
diff --git a/p/service/padreic/mnpc/.readme b/p/service/padreic/mnpc/.readme
new file mode 100644
index 0000000..ba197a4
--- /dev/null
+++ b/p/service/padreic/mnpc/.readme
@@ -0,0 +1,23 @@
+/*
+ * Der ultimative NPC : mnpc.c (C) by Sir Lunch a lot
+ * $Id: mnpc.c,v 1.1 1993/10/12 16:23:07 djc Exp djc $
+ */
+
+Dieser urspruengliche mnpc wurde von mir weiterentwickelt und stark
+optimiert. Er ist gedacht um relativ einfach, effiziente Laufnpcs zu
+programmieren und erspart einem dadurch das Rad erneut erfinden zu muessen.
+Der Urspruengliche mnpc.c von Sir liegt fuer interessierte weiterhin in
++d/unterwelt/std/mnpc.sir.
+
+                                    Padreic
+
+mnpc.h   -  die Includedatei mit den zum mnpc gehoerenden Propertys
+mnpc.doc -  eine kleine Dokumentation zum mnpc
+mnpc.c   -  dieses File wird anstelle von std/npc inheritet um einen Laufnpc
+            zu erzeugen.
+moving.c -  die eigentlich Routinen wurden ausgelagert, damit sie auch
+            mit anderen stdnpcs zusammen (z.B. Gildennpcs) benutzt werden
+            koennen.
+walk_master.c - dieser Walkmaster muss in den seltensten Faellen direkt
+            verwendet werden, da ihn z.B. der mnpc.c automatisch von selbst
+            benutzt. Sinn des masters: call_out-Ketten einsparen.
diff --git a/p/service/padreic/mnpc/mnpc.c b/p/service/padreic/mnpc/mnpc.c
new file mode 100644
index 0000000..0b22303
--- /dev/null
+++ b/p/service/padreic/mnpc/mnpc.c
@@ -0,0 +1,49 @@
+/* Changelog:
+   * 11.09.2007, Zesstra
+     strong_types und save_types aktiviert.
+*/
+#pragma strong_types
+#pragma save_types
+
+#include "/p/service/padreic/mnpc/mnpc.h"
+
+inherit "/std/npc";
+inherit MNPC_MOVING;
+
+void create()
+{
+   npc::create();
+   moving::mnpc_create();
+}
+
+void reset()
+{
+   npc::reset();
+   moving::mnpc_reset();
+}
+
+void init()
+{
+   npc::init();
+   moving::mnpc_init();
+}
+
+int InsertEnemy(object enemy)
+{
+   int i = npc::InsertEnemy(enemy);
+   if (!i) return 0;
+   moving::mnpc_InsertEnemy(enemy);
+   return i;
+}
+
+varargs int move(object|string dest, int meth, string dir, string out,
+  string in)
+{
+   int i=npc::move(dest, meth, dir, out, in);
+   if (i!=1) return i;
+   moving::mnpc_move();
+   return 1;
+}
+
+int PreventFollow(object dest)
+{  return moving::mnpc_PreventFollow(dest);  }
diff --git a/p/service/padreic/mnpc/mnpc.doc b/p/service/padreic/mnpc/mnpc.doc
new file mode 100644
index 0000000..86a9fa4
--- /dev/null
+++ b/p/service/padreic/mnpc/mnpc.doc
@@ -0,0 +1,103 @@
+/*
+ * Der ultimative NPC : mnpc.c (C) by Sir Lunch a lot
+ * $Id: mnpc.c,v 1.1 1993/10/12 16:23:07 djc Exp djc $
+ */
+
+Dieser urspruengliche mnpc wurde von mir weiterentwickelt und stark
+optimiert. Er ist gedacht um relativ einfach, effiziente Laufnpcs zu
+programmieren und erspart einem dadurch das Rad erneut erfinden zu muessen.
+Der Urspruengliche mnpc.c von Sir liegt fuer interessierte weiterhin in
++d/unterwelt/std/mnpc.sir.
+
+Die Eigenschaften im Ueberblick:
+
+MNPC_AREA - In diesem Feld stehen die Raeume, in denen der NPC herumlaufen
+   kann, ({}) bedeutet, das der NPC ueberall hin kann (bitte nur mit
+   aeusserster Vorsicht und Absprache des RMs benutzen!!!).
+   Bsp.: SetProp(MNPC_AREA, ({ "/d/ebene/room/ak_str" }));
+     Dieser NPC kann ueber saemtliche Raeume der Akademiestrasse laufen, also
+     z.b. in einen Raum mit dem Filenamen /d/ebene/room/ak_str1
+     aber auch in alle Raeume in einem Verzeichnis /d/ebene/room/ak_str/*
+
+MNPC_HOME - der Startraum des NPCs in den er spaeter ggf. auch wieder
+   zurueckkehrt. Setzt man diese Property nicht, wird versucht, den Ort des
+   Clonens zu erraten/bestimmen, aber wenn man sicher gehen moechte, dass der
+   MNPC das Home hat, was man denkt, sollte man es explizit setzen.
+
+MNPC_DELAY und. MNPC_RANDOM
+  MNPC_DELAY + random(MNPC_RANDOM) ergibt den Abstand zwischen zwei
+  Bewegungen des NPCs.
+  ACHTUNG: 
+    1. Diese Werte muessen gesetzt werden, _bevor_ MNPC_FLAGS 
+    gesetzt werden, da die Set-Methode von MNPC_FLAGS die Anmeldung im
+    walk_master durchfuehrt. Sonst werden die Default-Werte benutzt!
+    2. Wenn diese Werte geaendert werden, sollte man die MNPC_FLAGS neu
+    setzen, um so die Neuanmeldung im walk_master auszuloesen.
+
+MNPC_WALK_TIME - Zeitangabe wie lange es dauert bis der NPC stehen bleibt
+   falls er keinen Spieler mehr trifft. Dieser Wert sollte etwa 3-10 mal so
+   gross sein, wie die durchschnittliche Laufgeschwindigkeit. Wenn der NPC
+   dann einen reset lang keinen Spieler mehr trifft, movt er wieder in
+   seinen Startraum und wartet dort auf neue Spieler.
+   Defaultmaessig 600 sek.
+
+MNPC_FLAGS - property mit einem oder mehreren gesetzten Flags
+  MNPC_WALK  -  ueber dieses Flag kann das laufen ein- bzw. ausgeschaltet
+     werden.
+
+  MNPC_FOLLOW_PLAYER  -  der NPC verfolgt die Spieler, die er trifft
+
+  MNPC_NO_WALK_IN_FIGHT  -  der NPC bleibt beim kaempfen und rennt nicht weg
+
+  MNPC_GO_HOME_WHEN_STOPPED  -  der NPC geht sofort nach Hause wenn seine
+     Zeit abgelaufen ist.
+
+  MNPC_ONLY_EXITS - der NPC benutzt beim laufen keine SpecialExits
+     Diese Funktion ist sehr wichtig, da durch SpecialExits auch Raeume
+     betreten werden koennen die nicht in MNPC_AREA erlaubt wurden! Hierauf
+     muss dann bereits bei der Gebietskonstruktion geachtet werden.
+
+  MNPC_EXACT_AREA_MATCH - Die in MNPC_AREA angebeben Filenamen werden exakt
+     geprueft, d.h. tempel1 matcht auch nur tempel1, aber nicht mehr tempel14.
+     Folge: in MNPC_AREA muessten nun _alle_ Raeume einzeln angegeben werden, die
+     betreten werden duerfen.
+
+  MNPC_NO_MOVE_HOME - Der MNPC geht im reset() nicht nach Hause, wenn er
+     laenger keinen Spielerkontakt hatte, sondern bleibt an dem Standort,
+     wo er stehen blieb.
+     Achtung: sollte dieser Raum entladen werden, wird der MNPC dort
+     zuerstoert. Der Heimatraum des MNPC sollte dafuer vorsorgen (s. AddItem).
+     Ebenso kann es passieren, dass der MNPC sehr sehr lange nicht mehr
+     loslaeuft, sollte sein Standort einsam liegen.
+
+/***************************************************************************/
+
+Nun noch ein Beispiel fuer einen Laufnpc im Glockenwald:
+
+  create()
+  {
+     ::create();
+     SetProp(MNPC_DELAY, 30);
+     SetProp(MNPC_RANDOM, 30);
+     SetProp(MNPC_FLAGS, MNPC_WALK|MNPC_FOLLOW_PLAYER|MNPC_NO_WALK_IN_FIGHT);
+     SetProp(MNPC_AREA, ({ "/d/wald/troy/glockenwald" }));
+     ...
+  }
+
+  // diese Funktion entscheidet darueber ob ein Raum betreten werden darf,
+  // oder nicht (hier wird also auch MNPC_AREA abgearbeitet). Ein
+  // Ueberschreiben dieser Funktion kann manchmal sinnvoll sein, um
+  // komplexere Bedingungen zu implementieren.
+  int PreventEnter(string file)
+  {
+    call_other(file, "???");
+    if (::PreventEnter(file) || present("elster", find_object(file)))
+      return 1;
+    return 0;
+  }
+ 
+Dieser NPC laeuft durch den Glockenwald und sobald er einen Spieler trifft,
+verfolgt er diesen, bis zum Rande des Glockenwaldes. Wenn ein Spieler ihn
+angreift, bleibt er automatisch stehen. Die Spieler sind nur in Raeumen
+sicher, in denen sich eine Elster befindet, denn diese Raeume betritt der
+NPC nicht.
diff --git a/p/service/padreic/mnpc/mnpc.h b/p/service/padreic/mnpc/mnpc.h
new file mode 100644
index 0000000..023ada5
--- /dev/null
+++ b/p/service/padreic/mnpc/mnpc.h
@@ -0,0 +1,74 @@
+#ifndef _MNPC_H_
+#define _MNPC_H_
+
+/*
+     folgende Propertys koennen in mnpc's gesetzt werden...
+*/
+
+// bitte diese #define inheriten und nicht direkten Pfad benutzen
+#define MNPC                     "/p/service/padreic/mnpc/mnpc.c"
+#define MNPC_MOVING              "/p/service/padreic/mnpc/moving.c"
+
+// bitte dieses #define benutzen und nicht den direkten Pfad verwenden
+#define WALK_MASTER              "/p/service/padreic/mnpc/walk_master"
+#define MAX_MASTER_TIME          180
+
+// ist automatisch in jedem mnpc gesetzt
+#define P_MNPC                    "mnpc"
+
+// MNPC_DELAY + random(MNPC_RANDOM) ergibt den Abstand zwischen dem laufen
+#define MNPC_DELAY                "mnpc_delay"
+#define MNPC_RANDOM               "mnpc_random"
+
+// diese NPC wird aufgerufen nachdem der NPC einen Raum betreten hat.
+#define MNPC_FUNC                 "mnpc_func"
+
+// Raum angeben in dem der NPC zu Hause ist (dorthin wird im reset ggf. gemovt)
+// falls nicht angegeben, ist es der Ort des Clonens
+#define MNPC_HOME                 "mnpc_home"
+
+// array die die area in der der NPC sich bewegen darf abgrentzt
+#define MNPC_AREA                 "mnpc_area"
+
+// wie lange laeuft der NPC bis er stehen bleibt
+#define MNPC_WALK_TIME            "mnpc_walk_time"
+
+// hier koennen einige Flags gesetzt werden
+#define MNPC_FLAGS                "mnpc_flags"
+
+// die einzelnen Flags...
+#define MNPC_WALK                   0x01  /* NPC ist ein Laufnpc */
+#define MNPC_FOLLOW_PLAYER          0x02  /* NPC verfolgt Spieler */
+#define MNPC_FOLLOW_ENEMY           0x06  /* NPC verfolgt feindliche Spieler */
+#define MNPC_ONLY_EXITS             0x08  /* NPC nutzt keine SpecialExits */
+#define MNPC_NO_WALK_IN_FIGHT       0x10  /* NPC bleibt im Kampf stehen */
+#define MNPC_GO_HOME_WHEN_STOPPED   0x20  /* NPC wartet nicht bis zum reset */
+// wird intern in non livings gesetzt - manuelles setzen nicht noetig
+// non livings koennen jedoch _nie_ special exits nutzen...
+#define MNPC_DIRECT_MOVE            0x40  /* NPC laeuft mit move statt mit command_me */
+// Manche Magier wollen in MNPC_AREA keine Substrings, sondern exakte
+// Filenamen angeben. Wenn dieses Flag gesetzt wird, werden die Strings in
+// MNPC_AREA exakt geprueft, nicht mit strstr()
+#define MNPC_EXACT_AREA_MATCH      0x80
+// Manche Magier wollen nicht, dass der MNPC sich im reset nach Hause bewegt
+#define MNPC_NO_MOVE_HOME          0x100
+
+/*
+   Folgendes sind interne Defaultwerte falls die werte nicht gesetzt werden.
+*/
+
+// default delay wird genommen, falls kein bestimmtes eigenes angegeben wird
+#define MNPC_DFLT_DELAY           30
+// standardhome falls kein besseres home ermittelbar ist
+#define MNPC_DFLT_HOME            "/room/void"
+// der NPC bleibt nach dieser Zeit stehen und geht im reset nach Hause
+#define MNPC_DFLT_WALK            360              /* vorerst 6 min. */
+// Diese Funktion wird aufgerufen wenn der NPC einen raum betritt
+
+/*
+   interne defines
+*/
+#define MNPC_PURSUER             "mnpc:pursuer"  /* nur intern */
+#define MNPC_LAST_MEET           "mnpc_last_meet"
+
+#endif // _MNPC_H_
diff --git a/p/service/padreic/mnpc/moving.c b/p/service/padreic/mnpc/moving.c
new file mode 100644
index 0000000..b17d503
--- /dev/null
+++ b/p/service/padreic/mnpc/moving.c
@@ -0,0 +1,421 @@
+#pragma save_types,strong_types,rtt_checks,pedantic
+
+inherit "/std/living/moving";
+
+#include <moving.h>
+#include <defines.h>
+#include <properties.h>
+#include "/p/service/padreic/mnpc/mnpc.h"
+#define NEED_PROTOTYPES
+#include <living/combat.h>
+#undef NEED_PROTOTYPES
+#include <combat.h>
+
+#define ENV    environment
+#define PO     previous_object()
+
+// Letzter Spielerkontakt. -1, wenn der MNPC inaktiv zuhause rumsteht
+static int meet_last_player;
+
+static void mnpc_create()
+{
+  if (PO && member(inherit_list(PO), "/std/room.c")!=-1)
+    SetProp(MNPC_HOME, object_name(PO));
+  else if (PL && ENV(PL))
+    SetProp(MNPC_HOME, object_name(ENV(PL)));
+  else
+    SetProp(MNPC_HOME, MNPC_DFLT_HOME);
+  SetProp(P_MNPC, 1);
+  SetProp(MNPC_AREA, ({}));
+  SetProp(MNPC_DELAY, MNPC_DFLT_DELAY);
+  SetProp(MNPC_FUNC, 0);
+  SetProp(MNPC_RANDOM, 0);
+  SetProp(MNPC_WALK_TIME, MNPC_DFLT_WALK);
+  SetProp(MNPC_FLAGS, 0);
+  SetProp(P_ENABLE_IN_ATTACK_OUT, 1);
+  meet_last_player=time();
+}
+
+protected void RegisterWalk()
+{
+  if ((QueryProp(MNPC_DELAY)+QueryProp(MNPC_RANDOM))<=MAX_MASTER_TIME)
+    WALK_MASTER->RegisterWalker(QueryProp(MNPC_DELAY),
+                                QueryProp(MNPC_RANDOM));
+  else
+    call_out("Walk", QueryProp(MNPC_DELAY)+random(QueryProp(MNPC_RANDOM)));
+}
+
+// Can be used to manually restart the MNPC from a different object even if
+// the MNPC had not player contact.
+public int RestartWalk()
+{
+  int flags = QueryProp(MNPC_FLAGS);
+  // Falls nicht laufend, wird gar nichts gemacht.
+  if (flags & MNPC_WALK)
+  {
+    //Spielerkontakt simulieren
+    meet_last_player=time();
+    // Falls MNPC noch registriert ist oder noch einen Callout auf Walk hat,
+    // muss nichts weiter gemacht werden.
+    if (WALK_MASTER->Registration()
+        || find_call_out("Walk") > -1)
+      return -1;
+    // ansonsten MNPC registrieren, falls geeignet.
+    if ((QueryProp(MNPC_DELAY)+QueryProp(MNPC_RANDOM))<=MAX_MASTER_TIME)
+      WALK_MASTER->RegisterWalker(QueryProp(MNPC_DELAY), QueryProp(MNPC_RANDOM));
+    // und mit kurzer Verzoegerung einmal laufen. (ja, absicht, hier
+    // MNPC_DELAY zu nutzen - denn solange dauert das Walk vom Master
+    // mindestens.)
+    call_out("Walk",1+random( min(QueryProp(MNPC_DELAY)-1,8) ));
+    return 1;
+  }
+  return 0;
+}
+
+protected void Stop(int movehome)
+{
+  if (WALK_MASTER->Registration())
+    WALK_MASTER->RemoveWalker();
+  else if (find_call_out("Walk")!=-1)
+    remove_call_out("Walk");
+  if (movehome)
+  {
+    move(QueryProp(MNPC_HOME), M_TPORT|M_NOCHECK);
+    meet_last_player=-1;
+  }
+}
+
+static int _set_mnpc_flags(int flags)
+{
+  if (flags & MNPC_WALK)
+  {
+    if (!QueryProp(MNPC_HOME))
+      raise_error("unknown MNPC_HOME\n");
+    //wenn die Flags neu gesetzt werden, wird der MNPC das zweite Mal im
+    //Master angemeldet -> vorher abmelden (Zesstra)
+    if (QueryProp(MNPC_FLAGS) & MNPC_WALK)
+    {
+      Stop(0);
+    }
+    RegisterWalk();
+  }
+  // else nicht von Bedeutung, da in Walk() das flag getestet wird
+  if (flags & MNPC_FOLLOW_PLAYER)
+  {
+    if (!QueryProp(MNPC_PURSUER))
+    { // wurde dieses Flag neu eingeschaltet?
+      if (environment())
+      { // Verfolgung aufnehmen...
+        object *pursuer = filter(all_inventory(ENV()), #'interactive);
+        filter_objects(pursuer, "AddPursuer", ME);
+        SetProp(MNPC_PURSUER, pursuer);
+      }
+      else
+        SetProp(MNPC_PURSUER, ({}));
+    }
+  }
+  else if (pointerp(QueryProp(MNPC_PURSUER)))
+  { // wird derzeit irgendwer verfolgt?
+    // alle Verfolgungen abbrechen...
+    filter_objects(QueryProp(MNPC_PURSUER)-({ 0 }), "RemovePursuer", ME);
+    SetProp(MNPC_PURSUER, 0); // Speicher freigeben...
+  }
+  else
+    SetProp(MNPC_PURSUER, 0);
+
+  // nur livings koennen command_me nutzen...
+  if (!living(ME))
+    flags |= MNPC_DIRECT_MOVE;
+
+  return Set(MNPC_FLAGS, flags, F_VALUE);
+}
+
+static void mnpc_InsertEnemy(object enemy)
+{
+  if ( (QueryProp(MNPC_FLAGS) & MNPC_FOLLOW_ENEMY) &&
+         (member(QueryProp(MNPC_PURSUER), PL)==-1))
+  {
+     PL->AddPursuer(ME);
+     SetProp(MNPC_PURSUER, QueryProp(MNPC_PURSUER)+({ PL }));
+  }
+}
+
+static void mnpc_reset()
+{
+  int flags = QueryProp(MNPC_FLAGS);
+  // meet_last_player < 0 zeigt an, dass der MNPC schon zuhause ist.
+  if (meet_last_player < 0
+      || !(flags & MNPC_WALK)
+      || (flags & MNPC_NO_MOVE_HOME))
+    return;
+
+  // Lange keinen Spielerkontakt und kein Spieler im Raum: nach Hause gehen.
+  if (QueryProp(MNPC_WALK_TIME)+meet_last_player < time()
+      && environment() && !sizeof(filter(
+      all_inventory(environment()), #'query_once_interactive)))
+  {
+    // Abschalten und Heimgehen und dort warten.
+    Stop(1);
+  }
+}
+
+static int _query_mnpc_last_meet()
+{   return meet_last_player;   }
+
+static void mnpc_init()
+{
+  if (interactive(PL))
+  {
+    if (meet_last_player<=0)
+    {
+      RegisterWalk();
+    }
+    if ( (QueryProp(MNPC_FLAGS) & MNPC_FOLLOW_PLAYER) &&
+         (member(QueryProp(MNPC_PURSUER), PL)==-1) &&
+        (!(QueryProp(MNPC_FLAGS) & MNPC_FOLLOW_ENEMY) || IsEnemy(PL)))
+    {
+      PL->AddPursuer(ME);
+      SetProp(MNPC_PURSUER, QueryProp(MNPC_PURSUER)+({ PL }));
+    }
+    meet_last_player=time();
+  }
+  else
+  {
+    // Wenn der reinkommende auch ein MNPC_LAST_MEET groesser 0 hat, ist es
+    // ein MNPC, der noch laeuft. Wenn wir nicht laufen, laufen wir los.
+    // In diesem und auch im anderen Fall uebernehmen wir aber mal seinen
+    // letzten Spielerkontakt, denn der ist juenger als unserer.
+    int lm = PL->QueryProp(MNPC_LAST_MEET);
+    if (meet_last_player<=0 && lm>0)
+    {
+      RegisterWalk();
+      meet_last_player=lm;
+    }
+    else if (meet_last_player<lm)
+       meet_last_player=lm;
+  }
+}
+
+static void mnpc_move()
+{
+  if (environment() && (QueryProp(MNPC_FLAGS) & MNPC_FOLLOW_PLAYER))
+  {
+    object *liv = QueryProp(MNPC_PURSUER) & all_inventory(environment());
+    filter_objects(QueryProp(MNPC_PURSUER)-liv-({ 0 }), "RemovePursuer", ME);
+    SetProp(MNPC_PURSUER, liv);
+  }
+  if (QueryProp(MNPC_FUNC))
+    ME->QueryProp(MNPC_FUNC);
+}
+
+static int PreventEnter(string file)
+// darf der Raum betreten werden?
+{
+  string *area;
+
+  if (!sizeof(area=QueryProp(MNPC_AREA)))
+    return 0; // Raum darf betreten werden
+  else
+  {
+    int i;
+    status exactmatch;
+    exactmatch=QueryProp(MNPC_FLAGS) & MNPC_EXACT_AREA_MATCH;
+    if ((i=strstr(file, "#"))!=-1) file=file[0..i-1];
+    for (i=sizeof(area)-1; i>=0; i--)
+    {
+      if (exactmatch)
+      {
+        //exakter Vergleich, kein Substringvergleich gewuenscht
+        if (file==area[i])
+          return 0;  //betreten
+      }
+      else
+      {
+        if (strstr(file, area[i])==0)
+          return 0; // Raum betreten
+      }
+    }
+    return 1; //  Raum darf nicht betreten werden
+  }
+}
+
+static int mnpc_PreventFollow(object dest)
+{
+  if (dest && PreventEnter(object_name(dest)))
+    return 2;
+  return 0;
+}
+
+// Bewegungssimulation (Bewegungsmeldung) fuer bewegende non-livings
+static int direct_move(mixed dest, int method, string direction)
+{
+   int res, para, tmp;
+   string textout, textin, *mout, vc, fn;
+   object oldenv, *inv;
+
+   if (living(ME))
+      return call_other(ME, "move", dest, method);
+   else
+   {
+      oldenv = environment();
+      para=QueryProp(P_PARA);
+      if ((para>0) && stringp(dest))
+      {
+         fn=dest+"^"+para;
+         if (find_object(fn) || (file_size(fn+".c")>0))
+            dest=fn;
+         else if (file_size(vc=implode(explode(fn,"/")[0..<2],"/")
+                  +"/virtual_compiler.c")>0)
+         {
+            // wenn ein VC existiert, prüfen ob dieser ParaObjecte unterstuetzt
+            // wenn ja, dann testen ob sich Raum laden laesst...
+            if ((!catch(tmp=(int)call_other(vc,"NoParaObjects")) && (!tmp)) &&
+                (!catch(call_other( fn, "???" ))))
+                dest=fn;
+         }
+      }
+
+      res = (int)call_other(ME, "move", dest, M_NOCHECK);
+
+      if (oldenv==environment())
+        return res;
+
+      // als erstes die Meldung fuer das Verlassen des Raumes...
+      if ( method & M_TPORT )
+        textout = (string) QueryProp(P_MMSGOUT) || (string) QueryProp(P_MSGOUT);
+      else
+      {
+        mout = explode( (string) QueryProp(P_MSGOUT) || "", "#" );
+        textout = mout[0] || (string) QueryProp(P_MMSGOUT);
+      }
+
+      if (stringp(textout))
+      {
+         if ( !sizeof(direction) )
+           direction = 0;
+
+         inv = all_inventory(oldenv) - ({ this_object() });
+         inv = filter( inv, #'living);
+         inv -= filter_objects( inv, "CannotSee", 1 );
+         filter( inv, #'tell_object,
+                       Name( WER, 2 ) + " " + textout +
+                       (direction ? " " + direction : "") +
+                       (sizeof(mout) > 1 ? mout[1] : "") + ".\n" );
+      }
+
+      // nun die Meldung für das "Betreten" des Raumes...
+
+      if ( method & M_TPORT )
+        textin = (string) QueryProp(P_MMSGIN);
+      else
+        textin = (string) QueryProp(P_MSGIN);
+
+      if (stringp(textin))
+      {
+         inv = all_inventory(environment()) - ({ this_object() });
+         inv = filter( inv, #'living);
+         inv -= filter_objects( inv, "CannotSee", 1 );
+         filter( inv, #'tell_object,
+                       capitalize(name( WER, 0 )) + " " + textin + ".\n" );
+      }
+  }
+  return res;
+}
+
+int Walk()
+{
+  int     i;
+  mapping exits;
+  string  *rooms, *dirs, *ex, tmp;
+
+  if (!environment())
+  {
+    // darf eigentlich nicht vorkommen.
+    raise_error("MNPC ohne Environment.\n");
+  }
+
+  int flags=QueryProp(MNPC_FLAGS);
+  if (!(flags & MNPC_WALK))
+    return 0;
+
+  //ggf. neuen Callout eintragen, bevor irgendwas anderes gemacht wird.
+  if ((QueryProp(MNPC_DELAY)+QueryProp(MNPC_RANDOM))>MAX_MASTER_TIME)
+    call_out("Walk", QueryProp(MNPC_DELAY)+random(QueryProp(MNPC_RANDOM)));
+
+  // Im Kampf ggf. nicht weitergehen.
+  if ((flags & MNPC_NO_WALK_IN_FIGHT) && InFight())
+  {
+    meet_last_player=time();
+    return 1;
+  }
+
+  // MNPC anhalten, wenn lange kein Spielerkontakt
+  if (QueryProp(MNPC_WALK_TIME)+meet_last_player < time()
+      && !sizeof(filter(all_inventory(environment()),
+                 #'query_once_interactive))
+      )
+  {
+    // anhalten und ggf. auch direkt nach Hause gehen.
+    Stop(flags & MNPC_GO_HOME_WHEN_STOPPED);
+    return 0;
+  }
+
+  // Ausgaenge ermitteln.
+  exits = (environment()->QueryProp(P_EXITS));
+  rooms = m_values(exits);
+  dirs  = m_indices(exits);
+  ex = ({});
+
+  for (i=sizeof(rooms)-1; i>=0; i--)
+  {
+    if (!PreventEnter(rooms[i]))
+      ex += ({ dirs[i] });
+  }
+  /* Hier muessen wir auf die Zuverlaessigkeit unserer Magier bauen ... */
+  if (flags & MNPC_DIRECT_MOVE)
+  {
+    // im direct mode keine SEs benutzbar...
+    if (sizeof(ex))
+    {
+       tmp=ex[random(sizeof(ex))];
+       direct_move(explode(exits[tmp], "#")[<1], M_GO, "nach "+capitalize(tmp));
+    }
+    else
+    {
+       // Hngl. Nach Hause...
+       direct_move(QueryProp(MNPC_HOME), M_TPORT|M_NOCHECK, 0);
+       meet_last_player=-1;
+     }
+  }
+  else if (flags & MNPC_ONLY_EXITS)
+  {
+    // logischerweise auch keine SEs benutzen...
+    if (sizeof(ex))
+    {
+      command(ex[random(sizeof(ex))]); /* Irgendwohin gehen */
+    }
+    else
+    {
+      // Hngl. Nach Hause...
+      move(QueryProp(MNPC_HOME), M_TPORT|M_NOCHECK);
+      meet_last_player=-1;
+   }
+  }
+  else
+  {
+    // Special Exits mitbenutzen.
+    ex += m_indices(ENV()->QueryProp(P_SPECIAL_EXITS));
+    if (sizeof(ex))
+    {
+      command(ex[random(sizeof(ex))]); /* Irgendwohin gehen */
+    }
+    else
+    {
+      // Hngl. Gar keine Ausgaenge. Nach Hause...
+      move(QueryProp(MNPC_HOME), M_TPORT|M_NOCHECK);
+      meet_last_player=-1;
+    }
+  }
+
+  return 1;
+}
diff --git a/p/service/padreic/mnpc/walk_master.c b/p/service/padreic/mnpc/walk_master.c
new file mode 100644
index 0000000..3e003c8
--- /dev/null
+++ b/p/service/padreic/mnpc/walk_master.c
@@ -0,0 +1,186 @@
+// (c) by Padreic (Padreic@mg.mud.de)
+
+/* 12 Juli 1998, Padreic
+ *
+ * Dieser Master dient zum einsparen von call_out Ketten bei Laufnpcs.
+ * Anmelden bei diesem Master geschieht ueber die Funktion RegisterWalker().
+ * varargs void RegisterWalker(int time, int random, closure walk_closure)
+ *   time - in welchen Abstaenden soll das Objekt bewegt werden
+ *   rand - dieser Wert wird als random immer beim bewegen zu time addiert
+ *   walk_closure - die Closure die immer aufgerufen werden muss, wenn dieser
+ *                  Parameter weggelassen wird, wird statdessen die Funktion
+ *                  Walk() im NPC aufgerufen.
+ *
+ * Abgemeldet wird der NPC sobald die Closure bzw. die Walk-Funktion 0
+ * returned. Bei allen Werten !=0 bleibt der NPC aktiv.
+ *
+ * Hinweis: Der NPC muss sich mind. alle 180 sek. bewegen (time+random),
+ *          ansonsten kann dieser Master nicht verwendet werden.
+ */
+
+#define MAX_DELAYTIME       90   /* max. delay ist 2*MAX_DELAYTIME */
+#define DEFAULT_WALK_DELAY 180   /* ist der billigste Wert :) */
+#define MAX_JOB_COST    200000   /* Wieviel Zeit darf ein NPC max. nutzen */
+
+// Funktionen zum vereinfachten Zugriff auf die Komprimierten Daten
+// im Walkerarray
+#define TIME(t)        (t & 0x00ff)                   /* 8 Bit = 256 */
+#define RANDOM(r)     ((r & 0xff00) >> 8)             /* 8 Bit = 256 */
+#define WERT(t, r)    ((t & 0x00ff)+((r << 8) & 0xff00))   /* 16 Bit */
+
+static int counter;    // zur Orientierung im walker-array
+static int num_walker; // anzahl der walker im array
+//static mixed *walker;  // ({ ..., ({ ..., ({ wert, closure }), ...}), ...})
+nosave < < <int|closure>* >* >* walker;
+
+protected void create()
+{
+  if (clonep(this_object())) {
+    destruct(this_object());
+    raise_error("walk_master can't be clonend.\n");
+  }
+  walker=({});
+  num_walker=0;
+  enable_commands(); // ohne das, kein heart_beat()
+}
+
+#define ERROR(x) raise_error(sprintf(x, previous_object()));
+
+// Man muss selbst darauf aufpassen, das sich ein NPC immer nur einmal
+// anmeldet, da sonst auch mehrere Paralelle Walk-Ketten laufen!!!
+// Am besten nie direkt sondern nur ueber einen Standardnpc benutzen.
+varargs void RegisterWalker(int time, int rand, closure walk_closure)
+{
+  int wert, next;
+  closure func;
+  
+  // pruefen ob die Paramter zulaessig sind...
+  if (time<0) ERROR("negative time to RegisterWalker() from %O.\n");
+  if (rand<0) ERROR("negative random to RegisterWalker() from %O.\n");
+  if ((time+rand) > (2*MAX_DELAYTIME)) 
+    ERROR("Too long delaytime from %s to RegisterWalker().\n");
+
+  wert=WERT(time, rand);
+  if (!wert && !rand) wert=DEFAULT_WALK_DELAY;
+  if (walk_closure)
+    func=walk_closure;
+  else {
+    func=symbol_function("Walk", previous_object());
+    if (!func)
+      raise_error("RegisterWalker() call from Object without Walk() function.\n");
+  }
+  if (!num_walker) {
+    set_heart_beat(1);
+    if (!pointerp(walker) || !sizeof(walker))
+      walker=map(allocate(MAX_DELAYTIME+1), #'allocate);
+  }
+  next=counter;
+  next+=(time+random(rand))/2;
+  if (next>MAX_DELAYTIME) next-=MAX_DELAYTIME;
+  walker[next]+=({ ({ wert, func }) });
+  num_walker++;
+}
+
+int dummy_walk()  // liefert immer 0 fuer abbrechen...
+{   return 0;  }
+
+// Aufruf nach Moeglichkeit bitte vermeiden, da recht aufwendig. Meist ist
+// es leicht im NPC "sauber Buch zu fuehren" und dann ggf. aus Walk() 
+// 0 zu returnen.
+void RemoveWalker()
+{
+  int i, j;
+  if (!num_walker) return;
+  for (i=MAX_DELAYTIME; i>=0; i--) {
+    for (j=sizeof(walker[i])-1; j>=0; j--)
+    {
+      if (get_type_info(walker[i][j][1], 2)==previous_object())
+      {
+        if (i==counter) // koennte gerade im heart_beat stecken...
+          walker[i][j]=({ 0, #'dummy_walk });
+        else
+          walker[i][j]=0;
+        num_walker--;
+      }
+    }
+    if (i!=counter) // koennte gerade im heart_beat stecken...
+      walker[i]-=({ 0 });
+  }
+}
+
+// Aufruf nach Moeglichkeit bitte vermeiden, da recht aufwendig. Meist ist
+// es leichter im NPC "sauber Buch zu fuehren" und sich zu merken, ob er
+// bereits angemeldet ist. Liefert zurueck, wie oft ein NPC als Walker
+// angemeldet ist (normalfall nie mehr als 1).
+int Registration()
+{
+  int i, j, reg;
+  if (!num_walker) return 0;
+  reg=0;
+  for (i=MAX_DELAYTIME; i>=0; i--)
+  {
+    for (j=sizeof(walker[i])-1; j>=0; j--)
+      if (get_type_info(walker[i][j][1], 2)==previous_object())
+        reg++;
+  }
+  return reg;
+}
+
+void heart_beat()
+{
+   int i;
+   if (num_walker && i=sizeof(walker[counter])) {
+     int tmp;
+     num_walker-=i;
+     for (i--;i>=0;i--) {
+       if (get_eval_cost() < MAX_JOB_COST) {
+         // nicht abgefertigte NPCs im naechsten heart_beat ausfuehren
+         walker[counter]=walker[counter][0..i];
+         num_walker+=i+1;
+         return;
+       }
+       else {
+         if (walker[counter][i][1] &&
+             !catch(tmp=(int)funcall(walker[counter][i][1])) && tmp) {
+           tmp=counter+(TIME(walker[counter][i][0])
+              +random(RANDOM(walker[counter][i][0])))/2;
+           if (tmp>MAX_DELAYTIME) tmp-=MAX_DELAYTIME;
+           walker[tmp]+=({ walker[counter][i] });
+           num_walker++;
+         }
+       }
+     }
+     walker[counter]=({}); // komplett leeren
+   }
+   if (counter == MAX_DELAYTIME)
+     counter=0;
+   else counter++;
+   if (!num_walker) {
+     set_heart_beat(0);
+     walker=({}); // Speicher freigeben...
+   }
+}
+
+void reset()
+// kostet maximal einen unnoetigen heart_beat() pro reset -> vertretbar
+// dient zu einem wieder anwerfen im Falle eines Fehlers im heart_beat()
+{
+  if (set_heart_beat(0)<=0) {
+    int i;
+    num_walker=0; // neu berechnen...
+    if (!sizeof(walker)) return;
+    for (i=MAX_DELAYTIME; i>=0; i--)
+      num_walker+=sizeof(walker[i]);
+    if (num_walker>0) {
+       write_file(object_name()+".err", sprintf(
+         "%s: Fehler im heart_beat(). %d aktive Prozesse.\n",
+          dtime(time()), num_walker));
+       enable_commands();
+       set_heart_beat(1);
+    }
+  }
+  else set_heart_beat(1);
+}
+
+mixed *WalkerList() // nur fuer Debugzwecke
+{  return ({ num_walker, walker, counter });  }