Added public files

Roughly added all public files. Probably missed some, though.
diff --git a/secure/explorationmaster.c b/secure/explorationmaster.c
new file mode 100644
index 0000000..ad609d4
--- /dev/null
+++ b/secure/explorationmaster.c
@@ -0,0 +1,1024 @@
+// MorgenGrauen MUDlib
+//
+// explorationmaster.c -- Verwaltung der ExplorationPoints (FP)
+//
+// $Id: explorationmaster.c 9569 2016-06-05 21:28:23Z Zesstra $
+//
+#pragma strict_types,rtt_checks
+#pragma no_clone,no_shadow,no_inherit
+//#pragma pedantic
+//#pragma range_check
+#pragma warn_deprecated
+
+#define BY_EP "/secure/ARCH/EPSTAT_BY_EP"
+#define BY_PL "/secure/ARCH/EPSTAT_BY_PL"
+#define BY_PN "/secure/ARCH/EPSTAT_BY_PN"
+#define AVGLOG "/secure/ARCH/EPSTAT_AVG"
+
+inherit "/std/util/pl_iterator";
+
+#include <config.h>
+#include <wizlevels.h>
+#include <userinfo.h>
+#include <exploration.h>
+#include <properties.h>
+#include <new_skills.h>
+
+#define DOMAIN_INFO 1
+
+#include <living/comm.h>
+#define ZDEBUG(x) if (find_player("zesstra")) \
+  find_player("zesstra")->ReceiveMsg(x,MT_DEBUG,0,object_name()+":",this_object())
+//#define ZDEBUG(x)
+
+// Struktur: ([ string filename : string* action, int number, int type ]) 
+private mapping obs;
+private int epcount;  // Anzahl FP
+private int epavg;    // Durchschnittliche FP/Spieler
+// Bitstrings fuer normale (alloc) und Bonus-FP (bonus)
+private string alloc,bonus;
+
+private nosave string vc_ob;
+private nosave string output;
+
+private nosave int changed;
+private nosave int dumping;
+private nosave mapping dumpMap, lastfound, querytime;
+
+private nosave int stat_done;
+
+protected void create()
+{
+  seteuid(getuid(this_object()));
+  if (!restore_object(EPSAVEFILE)) {
+    obs = ([]);
+    epcount = epavg = 0;
+    alloc = " ";
+    bonus = " ";
+    save_object(EPSAVEFILE);
+  }
+  if (!bonus) bonus=" ";
+  lastfound=([]);
+  querytime=([]);
+}
+
+public varargs int remove(int silent)
+{
+  save_object(EPSAVEFILE);
+  destruct(this_object());
+  return 1;
+}
+
+// Statistik erstellen
+private int SetAverage(int newavg)
+{
+  if (epavg != newavg)
+  {
+    epavg = newavg;
+    write_file(AVGLOG, sprintf("%s : %d\n",dtime(time()),newavg));
+    save_object(EPSAVEFILE);
+  }
+  return epavg;
+}
+
+private void check_player(string pl, mixed extra)
+{
+  int *fp_found = extra[0];
+  mapping plstat = extra[1];
+  mapping pnstat = extra[2];
+  object pldata = extra[3];
+  if (!pldata)
+  {
+    pldata=clone_object("/obj/playerdata");
+    extra[3]=pldata;
+  }
+  // Letzte Loginzeit ermitteln, wenn laenger als 90 Tage her und nicht
+  // eingeloggt, wird der Spieler uebersprungen.
+  // Der playerdata braucht eine UID, mit der er das Spielersavefile laden
+  // kann. Hierzu setzen wir ihm die des zu ladenden Spielers...
+  pldata->ReleasePlayer();
+  seteuid(pl);
+  efun::export_uid(pldata);
+  seteuid(getuid(this_object()));
+  (int)pldata->LoadPlayer(pl);
+  // Testspieler ausnehmen, Spieler, die 90 Tage nicht da waren.
+  if ( ((int)pldata->QueryProp(P_LAST_LOGIN) < time() - 7776000
+        && !find_player(pl))
+      || (mixed)pldata->QueryProp(P_TESTPLAYER))
+    return;
+  // Wenn kein SPieler/Seher, sondern Magier: auch ueberspringen
+  mixed* uinfo = (mixed*)master()->get_userinfo(pl);
+  if (uinfo[USER_LEVEL+1] >= LEARNER_LVL)
+    return;
+
+  string eps=(string)master()->query_ep(pl) || "";
+  int p=-1;
+  int count, avgcount;
+  while ((p=next_bit(eps,p)) != -1)
+  {
+    if (p<sizeof(fp_found))
+    {
+      ++count;
+      ++fp_found[p];
+      // es tragen nur normale EPs zum Durchschnitt bei
+      if (!test_bit(bonus,p))
+        ++avgcount;
+    }
+  }
+
+  // Spieler mit weniger als MIN_EP ignorieren.
+  if (avgcount >= MIN_EP)
+  {
+    extra[4] += avgcount;   // Summe der gefundenen FP
+    ++extra[5];          // Anzahl beruecksichtigter Spieler
+  }
+  plstat += ([ pl : count ]);
+  if (!member(pnstat, count))
+    pnstat += ([ count : ({ pl }) ]);
+  else
+    pnstat[count] = ({ pl })+pnstat[count];
+}
+
+// Mit allen Spielern fertig, aufraeumen.
+#define BAR "************************************************************"
+private void plcheck_finished(mixed extra)
+{
+  ZDEBUG("plcheck_finished entry\n");
+  if (get_eval_cost() < 1000000)
+  {
+    call_out(#'plcheck_finished, 4+random(6), extra);
+    return;
+  }
+
+  int *fp_found = extra[0];
+  mapping plstat = extra[1];
+  mapping pnstat = extra[2];
+  object pldata = extra[3];
+  int avgcount = extra[4];
+  int avgspieler = extra[5];
+
+  if (objectp(pldata))
+    pldata->remove(1);
+
+  if (file_size(BY_EP) >= 0)
+    rm(BY_EP);
+  if (file_size(BY_PL) >= 0)
+    rm(BY_PL);
+  if (file_size(BY_PN) >= 0)
+    rm(BY_PN);
+
+  // Neuen Durchschnittswert fuer gefundene FP setzen.
+  if (avgspieler)
+  {
+    SetAverage(to_int(avgcount/avgspieler));
+  }
+
+  // Histogramm ueber alle FP schreiben: wie oft wurde jeder gefunden?
+  int maxval = max(fp_found);
+  int fp_index;
+  foreach(int found : fp_found)
+  {
+    write_file(BY_EP, sprintf("%5d:%5d %s\n", fp_index, found,
+               BAR[0..(60*found)/maxval]));
+    ++fp_index;
+  }
+  // sortierte Liste der Spieler (sortiert nach gefundenen FP) erzeugen
+  foreach(int fp : sort_array(m_indices(pnstat),#'<) )
+  {
+    foreach(string pl : pnstat[fp])
+      write_file(BY_PN, sprintf("%-14s: %5d\n", pl, fp));
+  }
+  // alphabetisch sortierte Liste der Spieler erzeugen
+  foreach(string pl : sort_array(m_indices(plstat),#'>) )
+  {
+    write_file(BY_PL, sprintf("%-14s: %5d\n", pl, plstat[pl]));
+  }
+}
+#undef BAR
+
+private void make_stat()
+{
+  stat_done = time();
+  // Leider laesst sich epcount nicht nutzen Beim Loeschen von FP wird
+  // <epcount> dekrementiert, so dass hier nicht mehr alle FPs ausgewertet
+  // werden. Daher muss stattdessen bis zur Gesamtgroesse von <obs> gezaehlt
+  // werden.
+  int* fp_found = allocate(sizeof(obs));
+  mapping plstat = m_allocate(500);
+  mapping pnstat = m_allocate(500);
+  start_player_check(#'check_player, #'plcheck_finished, 1200000,
+                     ({fp_found,plstat,pnstat,
+                       0, 0, 0}) );
+}
+
+void reset()
+{
+  if (changed && !dumping)
+  {
+    catch(rm(DUMPFILE);publish);
+    dumping = 1;
+    call_out("dumpEPObjects", 0, sort_array(m_indices(obs),#'> /*'*/));
+    changed = 0;
+  }
+  // nur einmal am tag statistiken erstellen
+  if (time()%86400 < 4000
+      && stat_done < time()-80000)
+    make_stat();
+}
+
+private string strArr(string *s)
+{
+  string ret;
+  int i;
+
+  ret = ("\""+s[<1]+"\"");
+  for (i=sizeof(s)-2; i>=0; i--)
+    ret += (", \""+s[i]+"\"");
+
+  return ret;
+}
+
+static void dumpEPObjects(string *doit)
+{
+  string *toGo, id;
+  int i,j;
+
+  if (!mappingp(dumpMap))
+    dumpMap = ([]);
+
+  toGo = 0;
+  if (sizeof(doit) > 200) {
+    toGo = doit[200..];
+    doit = doit[0..199];
+  }
+
+  j = sizeof(doit);
+
+  for (i=0; i<j; i++) {
+    id = (string)master()->creator_file(doit[i]);
+    if (member(dumpMap, id))
+      dumpMap[id] += ({ doit[i] });
+    else
+      dumpMap += ([ id : ({ doit[i] }) ]);
+  }
+  if (toGo)
+    call_out("dumpEPObjects", 1, toGo);
+  else {
+    int step;
+
+    step = 0;
+    id = "";
+    toGo = sort_array(m_indices(dumpMap),#'> /*'*/ );
+    for (i=0, j=sizeof(toGo); i<j; i++) {
+      int k,l;
+      doit = dumpMap[toGo[i]];
+      id += sprintf("### %s %s\n", toGo[i], "#########################"[sizeof(toGo[i])..]);
+      for (k=0, l=sizeof(doit); k<l; k++) {
+        id += sprintf("%s %4d %s %s.c ({ %s })\n",
+                      EP_TYPES[obs[doit[k], MPOS_TYPE]],
+                      obs[doit[k], MPOS_NUM],
+                      test_bit(bonus,obs[doit[k], MPOS_NUM])?"b":"n",
+                      doit[k],
+                      strArr(obs[doit[k]]));
+        if (!(++step % 50)) {
+          write_file(DUMPFILE, id);
+          id = "";
+        }
+      }
+      id += "\n";
+    }
+    write_file(DUMPFILE,id);
+    if (dumping == 2)
+      write("Fertig! Anfrage bitte wiederholen.\n");
+    dumping = 0;
+    changed = 0;
+    dumpMap = 0;
+  }
+}
+
+private string validOb(mixed ob)
+{
+  string fn, fpart;
+
+  if (!objectp(ob))
+    return 0;
+
+  fn = old_explode(object_name(ob),"#")[0];
+  fpart = old_explode(fn,"/")[<1];
+ /*
+  if (query_once_interactive(ob))
+    return 0;
+
+  if ((file_size(fn+".c") <= 0) &&
+      this_player() &&
+      (strstr(fpart, getuid(this_player())) >= 0))
+    return 0;
+ */
+  return fn;
+}
+
+private int allowed()
+{
+  if (previous_object() && geteuid(previous_object())==ROOTID)
+    return 1;
+  if ( !process_call() && previous_object() && this_interactive()
+        && ARCH_SECURITY )
+    return 1;
+
+  return 0;
+}
+
+nomask varargs int AddEPObject(object ob, mixed keys, int type, int bonusflag)
+{
+  string fn;
+
+  if (!allowed())
+    return EPERR_NOT_ARCH;
+
+  if (!(fn = validOb(ob)))
+    return EPERR_INVALID_OB;
+
+  if (stringp(keys))
+    keys = ({ keys });
+
+  if (member(obs, fn)) {
+    if (type == obs[fn, MPOS_TYPE])
+      obs[fn] = keys;
+    else
+      obs += ([ fn : keys; obs[fn, MPOS_NUM]; type ]);
+    if (bonusflag) bonus = set_bit(bonus, obs[fn, MPOS_NUM]);
+       else bonus = clear_bit(bonus, obs[fn, MPOS_NUM]);
+  }
+  else {
+    int nr, i;
+
+    nr = 0;
+    while (test_bit(alloc,nr))
+      nr++;
+
+    obs += ([ fn : keys; nr; type ]);
+    ++epcount;
+    alloc = set_bit(alloc,nr);
+    if (bonusflag) bonus = set_bit(bonus,nr);
+       else bonus = clear_bit(bonus,nr);
+  }
+
+  changed = 1;
+  save_object(EPSAVEFILE);
+  return epcount;
+}
+
+nomask int RemoveEPObject(string|object ob)
+{
+  string fn;
+
+  if (!allowed())
+    return EPERR_NOT_ARCH;
+  
+  if (objectp(ob))
+    ob=object_name(ob);
+
+  fn = explode(ob,"#")[0];
+  if (!member(obs,fn))
+    return EPERR_NO_ENTRY;
+
+  alloc = clear_bit(alloc, obs[fn, MPOS_NUM]);
+  bonus = clear_bit(bonus, obs[fn, MPOS_NUM]);
+
+  m_delete(obs, fn);
+
+  changed = 1;
+  --epcount;
+  save_object(EPSAVEFILE);
+  return epcount;
+}
+
+nomask int ChangeEPObject(object ob, int what, mixed new)
+{
+  string fn, fn2;
+  mapping entry;
+
+  if (!allowed())
+    return EPERR_NOT_ARCH;
+
+  if (!(fn = validOb(ob)))
+    return EPERR_INVALID_OB;
+
+  if (!member(obs,fn))
+    return EPERR_NO_ENTRY;
+
+  switch(what) {
+  case CHANGE_OB:
+    if (!(fn2 = validOb(new)))
+      return EPERR_INVALID_ARG;
+    entry = ([ fn2: obs[fn]; obs[fn,MPOS_NUM]; obs[fn,MPOS_TYPE] ]);
+    obs = m_copy_delete(obs, fn);
+    obs += entry;
+    break;
+  case CHANGE_BONUS:
+    if (!(intp(new)))
+      return EPERR_INVALID_ARG;
+    if (new) bonus=set_bit(bonus,obs[fn,MPOS_NUM]);
+       else bonus=clear_bit(bonus,obs[fn,MPOS_NUM]);
+    break;
+  case CHANGE_KEY:
+    if (!stringp(new) && !pointerp(new))
+      return EPERR_INVALID_ARG;
+       
+    if (stringp(new))
+      new = ({ new });
+
+    obs[fn] = new;
+    break;
+  case CHANGE_TYPE:
+    if (!intp(new) || new < 0 || new > EP_MAX)
+      return EPERR_INVALID_ARG;
+
+    obs[fn, MPOS_TYPE] = new;
+    break;
+  default:
+    return EPERR_INVALID_ARG;
+  }
+  changed = 1;
+  save_object(EPSAVEFILE);
+  return 1;
+}
+
+nomask mixed QueryEPObject(object ob)
+{
+  string fn;
+
+  if (!allowed())
+    return EPERR_NOT_ARCH;
+
+  if (!(fn = validOb(ob)))
+    return EPERR_INVALID_OB;
+
+  if (!member(obs, fn))
+    return 0;
+
+  return ({ obs[fn], obs[fn,MPOS_NUM], obs[fn, MPOS_TYPE], test_bit(bonus,obs[fn, MPOS_NUM]) });
+}
+
+private string getMatch(string m)
+{
+  string *res;
+  int i;
+
+  res = regexp(sort_array(m_indices(obs), #'> /*'*/), m);
+  for (i=sizeof(res)-1; i>=0; i--)
+    res[i] = sprintf("%s %s %s.c ({ %s })",
+                     EP_TYPES[obs[res[i], MPOS_TYPE]],
+                     test_bit(bonus,obs[res[i], MPOS_NUM])?"b":"n",
+                     res[i],
+                     strArr(obs[res[i]]));
+  return implode(res, "\n");
+}
+
+private string getMatchArch(string m)
+{
+  string *res;
+  int i;
+
+  res = regexp(sort_array(m_indices(obs), #'> /*'*/), m);
+  for (i=sizeof(res)-1; i>=0; i--)
+    res[i] = sprintf("%s %4d %s %s.c ({ %s })",
+                     EP_TYPES[obs[res[i], MPOS_TYPE]],
+                     obs[res[i], MPOS_NUM],
+                     test_bit(bonus,obs[res[i], MPOS_NUM])?"b":"n",
+                     res[i],
+                     strArr(obs[res[i]]));
+  return implode(res, "\n");
+}
+
+/*
+ *  Anleitung fuer >=EMs:
+ *    ShowEPObjects() zeigt das gesamte Dumpfile an
+ *    ShowEPObjects("dump") erzeugt ein neues Dumpfile
+ *    ShowEPObjects("string") liefert alle EPs, die "/string/"
+ *                            im Filenamen enthalten
+ *    ShowEPObjects("string1","string2") liefert alle EPs, die
+ *                            "/string1/string2/" im Filenamen enthalten
+ *  Anleitung fuer RMs:
+ *    ShowEPObjects() liefert all Deine EPs
+ *    ShowEPObjects("domainname") liefert Deine EPs in Deiner Domanin
+ *    ShowEPObjects("domainname","magiername") liefert die EPs des
+ *                                Mitarbeiters in Deiner Region
+ *
+ *  Anleitung fuer <RMs:
+ *    ShowEPObjects() liefert Deine EPs
+ *
+ *  Alle EPObjects einer ganzen Domain kann man nicht mehr auf einmal
+ *  ziehen und sollte man auch nicht. Alle Zugriffe auf diese Funktion
+ *  werden geloggt. Auch die der EMs!
+ *
+ *                                             Rikus
+ */
+
+nomask varargs void ShowEPObjects(string what, string magname)
+{
+  if (allowed()) {
+    if (what == "dump") {
+      if (!dumping) {
+        dumping = 2;
+        catch(rm(DUMPFILE);publish);
+        call_out("dumpEPObjects", 0, sort_array(m_indices(obs),#'>/*'*/));
+      }
+      printf("Liste wird erstellt und in '%s' abgelegt!\n", DUMPFILE);
+      return;
+    }
+    if (!what || what == "") {
+      this_interactive()->More(DUMPFILE, 1);
+      log_file("ARCH/EPZugriffe", ctime(time())+": "+
+        capitalize(getuid(this_interactive()))+" schaute sich das DUMPFILE an.\n");
+      return;
+    }
+    what="/"+what+"/";
+    if (magname) what+=magname+"/";
+  }
+  else
+#ifdef DOMAIN_INFO
+    if (IS_LORD(this_interactive())) {
+      if (!what || what == "")
+       what = "/"+getuid(this_interactive())+"/";
+      else {
+       if (!master()->domain_master(getuid(this_interactive()), what)) {
+         write("Sorry, Du kannst nur Objekte in Deiner eigenen Region abfragen!\n");
+         return;
+       }
+       if (!magname || magname=="")
+         magname = getuid(this_interactive());
+//        if (!master()->domain_member(magname, what)) {
+//         write(capitalize(magname)+" ist gar kein Mitarbeiter in Deiner Region!\n");
+//          return;
+//       }
+       what = "/d/"+what+"/"+magname+"/";
+      }
+    }
+    else
+#endif
+      {
+       if (!what || what == "")
+         what = getuid(this_interactive());
+       else if (what != getuid(this_interactive())) {
+         write("Sorry, Du kannst nur Deine eigenen Objekte abfragen!\n");
+         return;
+       }
+       what="/"+what+"/";
+      }
+  if (allowed())
+    this_interactive()->More(getMatchArch(what));
+  else
+    this_interactive()->More(getMatch(what));
+  log_file("ARCH/EPZugriffe", ctime(time())+": "+
+    capitalize(getuid(this_interactive()))+" schaute sich "+what+" an.\n");
+  return;
+}
+
+nomask void PrepareVCQuery(string file)
+{
+  string path, *parts;
+
+  vc_ob = 0;
+
+  if (!previous_object() || !stringp(file))
+    return;
+
+  parts = explode(object_name(previous_object()),"/");
+
+  if (parts[<1] == "virtual_compiler") {
+    path = implode(parts[0..<2]+({ file }), "/");
+    if (file_size(path+".c") < 0)
+      vc_ob = path;
+  }
+}
+
+nomask mixed *QueryExplore()
+{
+  string fn;
+
+  if (!previous_object())
+    return 0;
+
+  if (!member(obs, fn = old_explode(object_name(previous_object()),"#")[0]))
+    if (!vc_ob || !member(obs, fn = vc_ob)) {
+      vc_ob = 0;
+      return 0;
+    }
+
+  vc_ob = 0;
+  return ({ obs[fn, MPOS_TYPE], obs[fn] });
+}
+
+nomask int QueryMaxEP()
+{
+  return (epcount||1);
+}
+
+nomask int QueryAverage()
+{
+  return (epavg||1);
+}
+
+static int check_arch(object u)
+{
+   return query_wiz_level(u)>=ARCH_LVL;
+}
+
+private int check_to_fast(string name, string fn, int gesetzt)
+{
+    if (gesetzt) return 1; // Rikus, sonst arger scroll :)
+
+    log_file(FP_LOG,sprintf("%s : %s : %s\n",name,fn,dtime(time())),500000);
+
+    if ( !member( lastfound, name ) )
+        lastfound += ([ name: time(); 1; ({ fn + "#*#" + dtime(time()) + "#*#" +gesetzt }) ]);
+    else if ( time() <= lastfound[name, 0] + LF_TIME ){
+        lastfound[name, 1]++;
+        lastfound[name, 2] = ({ fn + "#*#" + dtime(time()) + "#*#" +gesetzt })
+            + lastfound[name,2];
+    }
+    else {
+        lastfound[name, 1] = 1;
+        lastfound[name, 2] = ({ fn + "#*#" + dtime(time()) + "#*#" +gesetzt });
+    }
+
+    lastfound[name, 0] = time();
+
+    if ( lastfound[name, 1] >= LF_WARN ){
+        object *u;
+        int i;
+        string *tmp;
+
+//        u = filter( users(), "check_arch" );
+//        map( u, #'tell_object/*'*/, "**** FP-Liste/Script " +
+//                   capitalize(name) + " (" + dtime(time()) + ") ****\n" );
+
+        for ( i = sizeof(lastfound[name, 2]); i--; ){
+            tmp = explode( lastfound[name, 2][i], "#*#" );
+            log_file( LF_LOG, sprintf( "%s : %s : %s : %s\n",
+                                         tmp[1], name, tmp[2], tmp[0] ), 500000 );
+        }
+
+        lastfound[name, 2] = ({});
+    }
+    return 1;
+}
+
+nomask int GiveExplorationPoint(string key)
+{
+  string fn;
+  string ep;
+  int gesetzt;
+
+  if (!previous_object() || !this_interactive() || !this_player() ||
+       this_player() != this_interactive() ||
+       this_player()->QueryProp(P_KILLS) ||
+       this_player()->QueryGuest()       )
+    return 0;
+
+  fn = old_explode(object_name(previous_object()), "#")[0];
+
+  if (!member(obs, fn))
+    return 0;
+
+  if (member(obs[fn],key) < 0)
+    return 0;
+
+  ep = (MASTER->query_ep(getuid(this_interactive())) || "");
+
+  gesetzt=test_bit(ep,obs[fn,1]);
+  check_to_fast(getuid(this_player()),fn,gesetzt);
+  if (gesetzt) return 0;
+  
+  catch(ep = set_bit(ep, obs[fn,1]));
+
+  return (int)MASTER->update_ep(getuid(this_interactive()),ep);
+}
+
+nomask int GiveExplorationPointObject(string key, object ob)
+{
+  string fn;
+  string ep;
+  int gesetzt;
+  
+  if (!objectp(ob) || ob->QueryProp(P_KILLS ))
+    return 0;
+
+  fn = old_explode(object_name(previous_object()), "#")[0];
+
+  if (!member(obs, fn))
+    return 0;
+
+  if (member(obs[fn],key) < 0)
+    return 0;
+
+  ep = (MASTER->query_ep(getuid(ob)) || "");
+
+  gesetzt=test_bit(ep,obs[fn,1]);
+  check_to_fast(getuid(this_player()),fn,gesetzt);
+  if (gesetzt) return 0;
+
+  catch(ep = set_bit(ep, obs[fn,1]));
+
+  return (int)MASTER->update_ep(getuid(ob),ep);
+}
+
+
+private int QueryRealExplorationPoints(string pl)
+{
+  return count_bits(MASTER->query_ep(pl) || " ");
+}
+
+nomask int QueryExplorationPoints(mixed pl)
+{
+  mixed val;
+  int ep;
+
+  if (!stringp(pl)) pl=getuid(pl);
+  ep=QueryRealExplorationPoints(pl);
+
+  if (allowed() || !ep) return ep;
+
+  val=querytime[pl];
+
+  if (!pointerp(val) || sizeof(val)<2)
+    val=({0,time()});
+
+  if (time()>=val[1]) {
+    val = ({ ep + random(6)-3, time()+300+random(300) });
+    if (val[0]<0) val[0]=0;
+    querytime+=([pl:val]);
+  }
+  return val[0];
+}
+
+private int remove_fp(int num, string pl)
+{
+  int i,j,k,t,maxEP;
+  string ep;
+  ep = (MASTER->query_ep(pl) || "");
+
+  maxEP = QueryMaxEP();
+  for( i=0; i<num; i++)
+  {
+    t = random(maxEP);
+    for( j=0; j<maxEP; j++ ) {
+      if( test_bit(ep, k=(t+j)%maxEP ) ) break;
+    }
+    if( j==maxEP ) break;
+    ep = clear_bit(ep, k);
+  }
+  MASTER->update_ep(pl,ep);
+  return i;
+}
+
+/* */
+
+// quoted from /sys/mail.h
+#define MSG_FROM 0
+#define MSG_SENDER 1
+#define MSG_RECIPIENT 2
+#define MSG_CC 3
+#define MSG_BCC 4
+#define MSG_SUBJECT 5
+#define MSG_DATE 6
+#define MSG_ID 7
+#define MSG_BODY 8
+
+nomask int RemoveFP(int num, string pl, string grund)
+{
+  int i;
+  string text;
+  mixed* mail;
+
+  if (!allowed()) return -1;
+  if( num<0 ) return -3;
+  if (!grund || grund=="") grund="<unbekannt>";
+  if (!pl) return -2;
+  i=remove_fp(num, pl);
+  log_file("ARCH/fp_strafen", ctime(time())+": "
+    +this_interactive()->Name(WER)+" loescht "+pl+" "+i
+    +" FPs\nGrund:"+grund+"\n");
+  if( i>0 ) {
+     text =
+     "Hallo "+capitalize(pl)+",\n\n"+
+     break_string(
+      this_interactive()->Name(WER)+" hat soeben veranlasst, dass Dir "+i
+      +" FPs abgezogen wurden.\nGrund:"+grund+"\n", 78 );
+
+     mail = allocate(9);
+     mail[MSG_FROM] = getuid(this_interactive());
+     mail[MSG_SENDER] = MUDNAME;
+     mail[MSG_RECIPIENT] = pl;
+     mail[MSG_CC]=0;
+     mail[MSG_BCC]=0;
+     mail[MSG_SUBJECT]="FP-Reduktion";
+     mail[MSG_DATE]=dtime(time());
+     mail[MSG_ID]=MUDNAME":"+time();
+     mail[MSG_BODY]=text;
+
+     "/secure/mailer"->DeliverMail(mail,1);
+  }
+  return i;
+}
+/* */
+
+private int add_fp(int num, string pl)
+{
+  int i,j,k,t,maxEP;
+  string ep;
+  ep = (MASTER->query_ep(pl) || "");
+
+  maxEP = QueryMaxEP();
+  for( i=0; i<num; i++)
+  {
+    t = random(maxEP);
+    for( j=0; j<maxEP; j++ ) {
+      if( !test_bit(ep, k=(t+j)%maxEP ) ) break;
+    }
+    if( j==maxEP ) break;
+    ep = set_bit(ep, k);
+  }
+  MASTER->update_ep(pl,ep);
+  return i;
+}
+
+nomask int AddFP(int num, string pl)
+{
+  int i;
+  if (!allowed()) return -1;
+  if ( num<0 ) return -3;
+  if (!pl) return -2;
+  i=add_fp(num, pl);
+  log_file("ARCH/fp_strafen", ctime(time())+": "
+    +this_interactive()->Name(WER)+" gibt "+pl+" "+i
+    +" FPs\n");
+
+  return i;
+}
+
+nomask int SetFP(int num, string pl)
+{
+  int maxEP;
+  string ep;
+  if (!allowed()) return -1;
+  if ( num<0 ) return -3;
+  if (!pl) return -2;
+  ep = (MASTER->query_ep(pl) || "");
+
+  maxEP = QueryMaxEP();
+  if (num<0 || num>=maxEP) return -4;
+  ep = set_bit(ep, num);
+  MASTER->update_ep(pl,ep);
+  return num;
+}
+
+nomask int ClearFP(int num, string pl)
+{
+  int maxEP;
+  string ep;
+  if (!allowed()) return -1;
+  if ( num<0 ) return -3;
+  if (!pl) return -2;
+  ep = (MASTER->query_ep(pl) || "");
+
+  maxEP = QueryMaxEP();
+  if (num<0 || num>=maxEP) return -4;
+  ep = clear_bit(ep, num);
+  MASTER->update_ep(pl,ep);
+  return num;
+}
+
+private void printep( int nr, string key, int kind, string* det )
+{
+  output+=sprintf("%4d %s %s.c %s ({ %s })\n",nr,test_bit(bonus,nr)?"b":"n",key,EP_TYPES[kind],
+                strArr(det));
+}
+
+nomask varargs int ShowPlayerEPs(string pl,string pattern)
+{
+  string ep,teststring;
+  if (!allowed()) return -1;
+  ep = (MASTER->query_ep(pl) || "");
+
+  output="";
+  if (!pattern || pattern=="")
+    teststring="%s";
+  else teststring="%s"+pattern+"%s";
+  walk_mapping( obs, lambda( ({ 'key, 'v1, 'v2, 'v3 }),
+                          // v1 -- details, v2 -- Nummer, v3 -- art
+                          // key -- Filename
+                          ({ #'if, ({ #'test_bit, ep, 'v2 }),
+                          ({#'if,({#'sscanf,'key,teststring,'v4,'v5}),
+                          ({ #'printep, 'v2, 'key, 'v3, 'v1 })
+                          }) }) ));
+  this_interactive()->More(output);
+  return 1;
+}
+
+// Hier kommen Funktionen fuer die Levelpunkte
+// Funktion fuer Levelpunkte steht im LEPMASTER!
+
+nomask int QueryLEP(int lep) {
+    raise_error("Bitte QueryLEP() im LEPMASTER abfragen, nicht im "
+       "EPMASTER!");
+    return(-1); //never reached
+}
+
+string QueryForschung()
+{
+  int my;
+  string ret;
+
+  if ((my=QueryRealExplorationPoints(getuid(previous_object()))) < MIN_EP)
+    return "Du kennst Dich im "MUDNAME" so gut wie gar nicht aus.\n";
+
+  my *= 100;
+  int absolute = my/QueryMaxEP();
+  int relative = my/QueryAverage();
+
+  ret = "Verglichen mit Deinen Mitspielern, kennst Du Dich im "MUDNAME" ";
+  switch(relative) {
+  case 0..10:
+    ret += "kaum";
+    break;
+  case 11..40:
+    ret += "aeusserst schlecht";
+    break;
+  case 41..56:
+    ret += "sehr schlecht";
+    break;
+  case 57..72:
+    ret += "schlecht";
+    break;
+  case 73..93:
+    ret += "unterdurchschnittlich";
+    break;
+  case 94..109:
+    ret += "durchschnittlich gut";
+    break;
+  case 110..125:
+    ret += "besser als der Durchschnitt";
+    break;
+  case 126..145:
+    ret += "recht gut";
+    break;
+  case 146..170:
+    ret += "ziemlich gut";
+    break;
+  case 171..210:
+    ret += "gut";
+    break;
+  case 211..300:
+    ret += "sehr gut";
+    break;
+  case 301..400:
+    ret += "ausserordentlich gut";
+    break;
+  case 401..500:
+    ret += "unheimlich gut";
+    break;
+  default:
+    ret += "einfach hervorragend";
+    break;
+  }
+  ret += " aus.\nAbsolut gesehen ";
+
+  switch(absolute) {
+  case 0..5:
+    ret += "kennst Du nur wenig vom "MUDNAME".";
+    break;
+  case 6..10:
+    ret += "solltest Du Dich vielleicht noch genauer umsehen.";
+    break;
+  case 11..17:
+    ret += "bist Du durchaus schon herumgekommen.";
+    break;
+  case 18..25:
+    ret += "hast Du schon einiges gesehen.";
+    break;
+  case 26..35:
+    ret += "bist Du schon weit herumgekommen.";
+    break;
+  case 36..50:
+    ret += "koenntest Du eigentlich einen Reisefuehrer herausbringen.";
+    break;
+  case 51..75:
+    ret += "hast Du schon sehr viel gesehen.";
+    break;
+  default:
+    ret += "besitzt Du eine hervorragende Ortskenntnis.";
+  }
+  return break_string(ret, 78, 0, 1);
+}
+
+// Nicht jeder Magier muss den EPMASTER entsorgen koennen.
+string NotifyDestruct(object caller) {
+  if( (caller!=this_object() && !ARCH_SECURITY) || process_call() )
+  {
+    return "Du darfst den Exploration Master nicht zerstoeren!\n";
+  }
+  return 0;
+}
+