Added Padreics moving NPC
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;
+}