blob: db265ad95500c51aa673d292ddd60e347c748e1f [file] [log] [blame]
// 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 hat als UID unsere eigene, weil wir ihn geclont haben.
// Damit darf er alle Spielersavefiles lesen...
pldata->ReleasePlayer();
(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, function void (string fn, string* v1, int v2, int v3)
{
string unused;
if ( test_bit(ep, v2) && sscanf(fn, teststring, unused, unused) )
printep(v2, fn, 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;
}