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