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;
+}
+