Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/secure/scoremaster.c b/secure/scoremaster.c
new file mode 100644
index 0000000..576a4b9
--- /dev/null
+++ b/secure/scoremaster.c
@@ -0,0 +1,1465 @@
+// MorgenGrauen MUDlib
+//
+// scoremaster.c - Verwaltung der eindeutigen Nummernvergabe fuer NPCs und
+// MiniQuests sowie der Stufenpunkte, die sie geben
+//
+// $Id: scoremaster.c 9170 2015-03-05 20:18:54Z Zesstra $
+#pragma strict_types
+#pragma no_clone
+#pragma no_shadow
+#pragma no_inherit
+#pragma verbose_errors
+#pragma combine_strings
+#pragma pedantic
+//#pragma range_check
+#pragma warn_deprecated
+
+#include "/secure/scoremaster.h"
+#include "/secure/wizlevels.h"
+#include <properties.h>
+#include <files.h>
+
+#define ZDEBUG(x) if (find_player("zesstra")) \
+ tell_object(find_player("zesstra"),sprintf("SCM: %s\n",x))
+
+// hoechste vergebene Nr.
+private int lastNum;
+
+// Liste alle EKs: ([obname: num; score; killcount])
+private mapping npcs = m_allocate(0,3);
+
+// Liste von Spielernamen-Wert-Paaren, die im Reset abgearbeitet wird:
+// ([plname: ({wert1, wert2, wert3, ...}) ])
+// wert > 0 bedeutet setzen des entsprechenden EKs, < 0 bedeutet loeschen.
+private mapping to_change = ([]);
+
+// Liste der EK-Tips: ([obname: Spruch])
+private mapping tipList = ([]);
+
+// Bit-Nr., die (wieder) vergeben werden duerfen.
+private int *free_num = ({});
+
+// zu entfernende EKs, Liste Bitnummern, also ints
+private int *to_be_removed = ({});
+
+// Liste von temporaeren EKs, die noch nicht bestaetigt wurden:
+// ([obname: ({plname1, plname2}) ])
+private mapping unconfirmed_scores = ([]);
+
+// alle Spieler kriegen diesen
+// Nach Nr. sortierte NPC-Liste: ([num: key; score])
+private nosave mapping by_num = m_allocate(0,2);
+
+// Cache fuer EKs von Spielern: ([plname: scoresumme])
+private nosave mapping users_ek = ([]);
+
+// bitstring, der alle aktiven EKs als gesetztes Bit enthaelt.
+private nosave string active_eks="";
+
+// Prototypen
+public mapping getFreeEKsForPlayer(object player);
+public int addTip(mixed key,string tip);
+public int changeTip(mixed key,string tip);
+public int removeTip(mixed key);
+private string getTipFromList(mixed key);
+public string getTip(mixed key);
+
+public void CheckNPCs(int num);
+public void check_all_player(mapping allplayer);
+public varargs int DumpNPCs(int sortkey);
+
+private void make_num(string key, int num, int score) {
+ by_num += ([ num : key; score ]);
+ // fuer aktive EKs, die also einen Scorewert > 0 haben, wird das jeweilige
+ // Bit gesetzt. Wird spaeter zum Ausfiltern inaktiver EKs aus den Bitstrings
+ // in den Spieler gebraucht.
+ if (score>0 && !member(unconfirmed_scores,num))
+ active_eks = set_bit(active_eks, num);
+}
+
+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;
+}
+
+protected void create()
+{
+ seteuid(getuid());
+ if (!restore_object(SCORESAVEFILE))
+ {
+ lastNum=0;
+ npcs=m_allocate(0,3);
+ to_change=m_allocate(0,1);
+ tipList=([]);
+ }
+ npcs-=([0]);
+ walk_mapping(npcs, #'make_num);
+}
+
+public int ClearUsersEKCache()
+{
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+ users_ek = ([]);
+ return 1;
+}
+
+public mixed QueryUsersEKCache()
+{
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+ return users_ek;
+}
+
+public mixed Query_free_num()
+{
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+ return free_num;
+}
+
+public mixed Add_free_num(int what)
+{
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+ if (!what || !intp(what) || by_num[what])
+ return SCORE_INVALID_ARG;
+ if (member(free_num,what)==-1)
+ free_num+=({what});
+ save_object(SCORESAVEFILE);
+ write_file(SCORELOGFILE,sprintf("ADDFREENUM: %s %5d (%s, %O)\n",
+ strftime("%d%m%Y-%T",time()),what,
+ geteuid(previous_object()), this_interactive()));
+
+ return free_num;
+}
+
+public mixed Remove_free_num(int what)
+{
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+ if (!what || !intp(what))
+ return SCORE_INVALID_ARG;
+ free_num-=({what});
+ save_object(SCORESAVEFILE);
+ write_file(SCORELOGFILE,sprintf("REMOVEFREENUM: %s %5d (%s, %O)\n",
+ strftime("%d%m%Y-%T",time()),what,
+ geteuid(previous_object()),this_interactive()));
+ return free_num;
+}
+
+public mixed Query_to_change(string who)
+{
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+ if (!who)
+ return to_change;
+ if (who=="")
+ return m_indices(to_change);
+ return to_change[who];
+}
+
+public mixed Add_to_change(string who, int what)
+{
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+ if (!who || !stringp(who) || !what || !intp(what))
+ return SCORE_INVALID_ARG;
+ if (member(to_change,who))
+ {
+ to_change[who]-=({-what});
+ if (member(to_change[who],what)==-1)
+ to_change[who]+=({what});
+ }
+ else
+ to_change[who]=({what});
+ save_object(SCORESAVEFILE);
+ write_file(SCORELOGFILE,sprintf("ADDTOCHANGE: %s %s %5d (%s, %O)\n",
+ strftime("%d%m%Y-%T",time()),who,what,
+ geteuid(previous_object()), this_interactive()));
+ return to_change[who];
+}
+
+public mixed Remove_to_change(string who, int what)
+{
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+ if (!who || !stringp(who) || !what || !intp(what))
+ return SCORE_INVALID_ARG;
+ if (member(to_change,who))
+ {
+ to_change[who]-=({what});
+ if (!sizeof(to_change[who]))
+ m_delete(to_change,who);
+ }
+ save_object(SCORESAVEFILE);
+ write_file(SCORELOGFILE,sprintf("REMOVETOCHANGE: %s %s %5d (%s, %O)\n",
+ strftime("%d%m%Y-%T",time()),who,what,
+ geteuid(previous_object()), this_interactive()));
+ return to_change[who];
+}
+
+void reset()
+{
+ string *whop,who,ek;
+ mixed what;
+ int i,j,value,changed;
+
+ // falls EKs global entfernt werden sollen, schonmal den noetigen Callout
+ // starten.
+ if (sizeof(to_be_removed) && find_call_out(#'check_all_player) == -1)
+ call_out(#'check_all_player, 10, 0);
+ // EK-Mainteiner ueber unbestaetigte EKs informieren
+ if (sizeof(unconfirmed_scores)) {
+ foreach(string n: SCOREMAINTAINERS) {
+ if (objectp(find_player(n)))
+ tell_object(find_player(n),break_string(
+ "Es gibt unbestaetigte EKs im Scoremaster. Schau Dir die doch "
+ "mal an. ;-)",78, "Der Scoremaster teilt Dir mit: "));
+ }
+ }
+
+ i=sizeof(whop=m_indices(to_change))-1;
+ while (i>=0 && get_eval_cost()>100000)
+ {
+ ek = (string)(MASTER->query_ek(who=whop[i]) || "");
+ for (j=sizeof(what=to_change[who])-1;j>=0;j--) {
+ if ((value=what[j])>0) {
+ // Vergabestatistik hochzaehlen.
+ npcs[by_num[value,BYNUM_KEY],NPC_COUNT]++;
+ ek=set_bit(ek,value);
+ }
+ else {
+ // Vergabestatistik hochzaehlen.
+ npcs[by_num[-value,BYNUM_KEY],NPC_COUNT]++;
+ ek=clear_bit(ek,-value);
+ }
+ // if (find_player("rikus"))
+ //tell_object(find_player("rikus"),"SCOREMASTER "+who+" "+erg+"\n");
+
+ write_file(SCOREAUTOLOG,
+ sprintf("SET_CLEAR_BIT (reset): %s %4d %s\n",
+ who, j, strftime("%d%m%Y-%T",time()) ));
+ }
+ MASTER->update_ek(who, ek);
+
+ if (member(users_ek, who))
+ m_delete(users_ek, who);
+
+ m_delete(to_change,who);
+ changed=1;
+ i--;
+ }
+ if (changed) save_object(SCORESAVEFILE);
+}
+
+public varargs mixed QueryNPC(int score)
+{
+ string key;
+ int val;
+
+ if (!previous_object())
+ return SCORE_INVALID_ARG;
+
+ key = load_name(previous_object());
+
+ // schon bekannter EK?
+ if (member(npcs,key))
+ return ({npcs[key,NPC_NUMBER],npcs[key,NPC_SCORE]});
+
+ if (score<=0 ||
+ member(inherit_list(previous_object()),"/std/living/life.c") < 0)
+ return SCORE_INVALID_ARG;
+
+ if (key[0..8]=="/players/") return SCORE_INVALID_ARG;
+
+ if (score>2) score=2;
+
+ if (sizeof(free_num)) {
+ val = free_num[0];
+ free_num -= ({val});
+ }
+ else val=++lastNum;
+
+ npcs[key,NPC_SCORE] = score;
+ npcs[key,NPC_NUMBER] = val;
+ npcs[key,NPC_COUNT] = 0;
+ by_num += ([val: key; score]);
+ // werden noch nicht als aktive EKs gewertet, damit sie nicht als Ek-Tips
+ // vergben werden.
+ //active_eks = set_bit(active_eks, val);
+
+ unconfirmed_scores += ([ val: ({}) ]);
+
+ ClearUsersEKCache();
+ save_object(SCORESAVEFILE);
+ write_file(SCOREAUTOLOG,sprintf(
+ "ADDNPC: %s %5d %4d %s (UID: %s, TI: %O, TP: %O)\n",
+ strftime("%d%m%Y-%T",time()),val,score,key,
+ getuid(previous_object()), this_interactive(), this_player()));
+
+ while(remove_call_out("DumpNPCs") != -1) ;
+ call_out("DumpNPCs",60);
+ return ({val,score});
+}
+
+public varargs mixed NewNPC(string key,int score)
+{
+ int val;
+
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+ if (!key || !stringp(key))
+ return SCORE_INVALID_ARG;
+
+ key = load_name(key);
+ if (val=npcs[key,NPC_NUMBER])
+ return ({val,npcs[key,NPC_SCORE]});
+ if (score<=0)
+ return SCORE_INVALID_ARG;
+
+ if (sizeof(free_num)) {
+ val=free_num[0];
+ free_num=free_num[1..];
+ }
+ else val=++lastNum;
+
+ npcs[key,NPC_SCORE] = score;
+ npcs[key,NPC_NUMBER] = val;
+ npcs[key,NPC_COUNT] = 0;
+ by_num += ([val: key; score]);
+ active_eks = set_bit(active_eks, val);
+
+ ClearUsersEKCache();
+ save_object(SCORESAVEFILE);
+ write_file(SCORELOGFILE,sprintf("NEWNPC: %s %5d %4d %s (%s, %O)\n",
+ strftime("%d%m%Y-%T",time()),val,score,key,
+ geteuid(previous_object()), this_interactive()));
+ while(remove_call_out("DumpNPCs") != -1) ;
+ call_out("DumpNPCs",60);
+ return ({val,score});
+}
+
+public varargs mixed AddNPC(string key,int score) { return NewNPC(key,score); }
+
+// restauriert die Daten eines frueher geloeschten, in den Spielern noch
+// enthaltenen EKs. Moeglich, wenn man Pfad, Nr. und Punkte noch kennt.
+public int RestoreEK(string key, int bit, int score) {
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+ if (!stringp(key) || !sizeof(key)
+ || !intp(bit) || bit < 0
+ || !intp(score) || score < 0)
+ return SCORE_INVALID_ARG;
+
+ if (member(npcs,key) || member(by_num,bit))
+ return SCORE_INVALID_ARG;
+
+ npcs += ([key: bit;score;0 ]);
+ by_num += ([bit: key;score ]);
+
+ ClearUsersEKCache();
+ save_object(SCORESAVEFILE);
+ write_file(SCORELOGFILE,sprintf("RESTOREEK: %s %5d %4d %s (%s, %O)\n",
+ strftime("%d%m%Y-%T",time()), bit, score, key,
+ geteuid(previous_object()), this_interactive()));
+ while(remove_call_out("DumpNPCs") != -1) ;
+ call_out("DumpNPCs",60);
+ return 1;
+
+}
+
+public int ConfirmScore(mixed key) {
+ // Bits in den Spielern in unconfirmed_scores setzen und Statistik
+ // hochzaehlen
+ // Bit in active_eks setzen
+ // Eintrag aus unconfirmed_scores loeschen
+ int bit;
+
+ if (!allowed()) return SCORE_NO_PERMISSION;
+ if (stringp(key) && member(npcs,key)) {
+ bit = npcs[key, NPC_NUMBER];
+ }
+ else if (intp(key) && member(by_num,key)) {
+ bit = key;
+ }
+ else
+ return SCORE_INVALID_ARG;
+
+ if (!member(unconfirmed_scores, bit))
+ return SCORE_INVALID_ARG;
+
+ string obname = by_num[bit, BYNUM_KEY];
+ int score = by_num[bit,BYNUM_SCORE];
+
+ foreach(string pl: unconfirmed_scores[bit]) {
+ string eks = (string)master()->query_ek(pl);
+ eks = set_bit(eks, bit);
+ master()->update_ek(pl, eks);
+ write_file(SCOREAUTOLOG, sprintf(
+ "SETBIT: %s %5d %s\n",
+ strftime("%d%m%Y-%T",time()), bit, pl));
+ }
+ //Vergabestatistik hochzaehlen...
+ npcs[obname,NPC_COUNT]+=sizeof(unconfirmed_scores[bit]);
+
+ m_delete(unconfirmed_scores, bit);
+ active_eks = set_bit(active_eks, bit);
+ save_object(SCORESAVEFILE);
+
+ write_file(SCORELOGFILE,sprintf(
+ "CONFIRMNPC: %s %5d Score %3d %s [%s, %O]\n",
+ strftime("%d%m%Y-%T",time()), bit, score, obname,
+ getuid(previous_object()),this_interactive()));
+
+ return 1;
+}
+
+public int RejectScore(mixed key) {
+ // Eintrag aus unconfirmed_scores, npcs, by_num loeschen
+ // Bit-Nr. in free_num eintragen
+ // evtl. EK-Spruch entfernen?
+ int bit;
+
+ if (!allowed()) return SCORE_NO_PERMISSION;
+ if (stringp(key) && member(npcs,key)) {
+ bit = npcs[key, NPC_NUMBER];
+ }
+ else if (intp(key) && member(by_num,key)) {
+ bit = key;
+ }
+ else
+ return SCORE_INVALID_ARG;
+
+ if (!member(unconfirmed_scores, bit))
+ return SCORE_INVALID_ARG;
+
+ string obname = by_num[bit, BYNUM_KEY];
+ int score = by_num[bit,BYNUM_SCORE];
+
+ m_delete(by_num, bit);
+ m_delete(npcs, obname);
+ m_delete(unconfirmed_scores,bit);
+ removeTip(obname);
+ free_num += ({bit});
+
+ save_object(SCORESAVEFILE);
+
+ write_file(SCORELOGFILE,sprintf(
+ "REJECTNPC: %s %5d Score %3d %s [%s, %O]\n",
+ strftime("%d%m%Y-%T",time()), bit, score, obname,
+ getuid(previous_object()),this_interactive()));
+ return 1;
+}
+
+// unbestaetigte NPCs in ein File ausgeben
+public void DumpUnconfirmedScores() {
+ if (!objectp(this_player())) return;
+
+ write(sprintf("%5s %5s %4s %s\n",
+ "Nr.", "Cnt", "Sc", "Objekt"));
+ foreach(int bit, string *pls: unconfirmed_scores) {
+ write(sprintf("%5d %5d %4d %s\n",
+ bit, sizeof(pls), by_num[bit,BYNUM_SCORE], by_num[bit,BYNUM_KEY]));
+ }
+}
+
+public mapping _query_unconfirmed() {
+ if (allowed()) return unconfirmed_scores;
+ return 0;
+}
+
+public varargs int SetScore(mixed key,int score)
+{
+ int num;
+ string ob;
+ int oldscore;
+
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+ if (!key) return SCORE_INVALID_ARG;
+
+ if (stringp(key) && sizeof(key)) {
+ ob = load_name(key);
+ if (!member(npcs, ob)) return SCORE_INVALID_ARG;
+ num = npcs[ob, NPC_NUMBER];
+ if (ob != by_num[num, BYNUM_KEY])
+ return SCORE_INVALID_ARG;
+ }
+ else if (intp(key) && member(by_num,key) ) {
+ num = key;
+ ob = by_num[num, BYNUM_KEY];
+ if (!member(npcs, ob) || (npcs[ob, NPC_NUMBER] != num))
+ return SCORE_INVALID_ARG;
+ }
+ else
+ return SCORE_INVALID_ARG;
+
+ oldscore = by_num[num,BYNUM_SCORE];
+ by_num[num,BYNUM_SCORE] = score;
+ npcs[ob, NPC_SCORE] = score;
+
+ if (score > 0)
+ active_eks = set_bit(active_eks, num);
+ else
+ active_eks = clear_bit(active_eks, num);
+
+ ClearUsersEKCache();
+ save_object(SCORESAVEFILE);
+ write_file(SCORELOGFILE,sprintf(
+ "SETSCORE: %s %5d %.3d OSc: %.3d %s (%s, %O)\n",
+ strftime("%d%m%Y-%T",time()),num,score,oldscore, ob,
+ geteuid(previous_object()), this_interactive()));
+ while(remove_call_out("DumpNPCs") != -1) ;
+ call_out("DumpNPCs",60);
+ return 1;
+}
+
+// entfernt einen EK endgueltig und unwiderruflich und gibt die Nr. wieder
+// frei.
+// Technisch wird der EK erstmal in eine Liste eingetragen. Im Reset iteriert
+// der Master ueber alle SPieler-Savefiles und loescht den Ek aus alle
+// Spielern. Nach Abschluss wird der Eintrag in npcs geloescht und seine Nr.
+// in die Liste freier Nummern eingetragen.
+public int* MarkEKForLiquidation(mixed key) {
+ int bit;
+ if (!allowed())
+ return 0;
+ // nicht in to_be_removed aendern, wenn check_all_player() laeuft.
+ if (find_call_out(#'check_all_player) != -1)
+ return 0;
+
+ if (stringp(key) && sizeof(key)) {
+ if (!member(npcs,key)) return 0;
+ bit = npcs[key,NPC_NUMBER];
+ }
+ else if (intp(key) && key>=0) {
+ bit = key;
+ }
+ else
+ return 0;
+
+ if (member(to_be_removed,bit) == -1)
+ to_be_removed += ({bit});
+ write_file(SCORELOGFILE,sprintf("DELETEFLAG: %s %5d %s (%s, %O)\n",
+ strftime("%d%m%Y-%T",time()), bit, by_num[bit,BYNUM_KEY] || "NoName",
+ geteuid(previous_object()), this_interactive()));
+
+ save_object(SCORESAVEFILE);
+
+ return to_be_removed;
+}
+
+// geht nur, solange nach einem RemoveEK() noch kein reset() gelaufen ist!
+public int* UnmarkEKForLiquidation(mixed key) {
+ int bit;
+ if (!allowed())
+ return 0;
+ // nicht in to_be_removed aendern, wenn check_all_player() laeuft.
+ if (find_call_out(#'check_all_player) != -1)
+ return 0;
+
+ if (stringp(key) && sizeof(key)) {
+ if (!member(npcs,key)) return 0;
+ bit = npcs[key,NPC_NUMBER];
+ }
+ else if (intp(key) && key>=0) {
+ bit = key;
+ }
+ else
+ return 0;
+
+ to_be_removed -= ({bit});
+ write_file(SCORELOGFILE,sprintf("UNDELETEFLAG: %s %5d %s (%s, %O\n",
+ strftime("%d%m%Y-%T",time()),bit, by_num[bit, BYNUM_KEY] || "NoName",
+ geteuid(previous_object()), this_interactive()));
+
+ save_object(SCORESAVEFILE);
+
+ return to_be_removed;
+}
+
+public int* QueryLiquidationMarks() {
+ if (allowed())
+ return to_be_removed;
+ else
+ return 0;;
+}
+
+// setzt nur den Scorewert auf 0, sonst nix. Solche EKs koennen dann spaeter
+// durch Angabe eines neues Scorewertes reaktiviert werden.
+public int RemoveScore(mixed key) {
+ int changed;
+ int oldscore;
+
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+
+ if (stringp(key) && member(npcs,key)) {
+ int num = npcs[key, NPC_NUMBER];
+ if ( key == by_num[num, BYNUM_KEY]) {
+ oldscore = by_num[num, BYNUM_SCORE];
+ npcs[key, NPC_SCORE] = 0;
+ by_num[num, BYNUM_SCORE] = 0;
+ active_eks = clear_bit(active_eks,num);
+ write_file(SCORELOGFILE,sprintf(
+ "REMOVESCORE: %s %5d OSc: %.3d %s (%s, %O)\n",
+ strftime("%d%m%Y-%T",time()), num, oldscore, key,
+ geteuid(previous_object()), this_interactive()));
+ changed = 1;
+ }
+ }
+ else if (intp(key) && member(by_num, key)) {
+ string obname = by_num[key, BYNUM_KEY];
+ if (key == npcs[obname, NPC_NUMBER]) {
+ oldscore = by_num[key, BYNUM_SCORE];
+ npcs[obname, NPC_SCORE] = 0;
+ by_num[key, BYNUM_SCORE] = 0;
+ active_eks = clear_bit(active_eks,key);
+ write_file(SCORELOGFILE,sprintf(
+ "REMOVESCORE: %s %5d OSc: %.3d %s (%s, %O)\n",
+ strftime("%d%m%Y-%T",time()),key, oldscore, obname,
+ geteuid(previous_object()), this_interactive()));
+ changed = 1;
+ }
+ }
+
+ if (changed) {
+ ClearUsersEKCache();
+ save_object(SCORESAVEFILE);
+ while(remove_call_out("DumpNPCs") != -1) ;
+ call_out("DumpNPCs",60);
+ return 1;
+ }
+ return SCORE_INVALID_ARG;
+}
+
+public varargs int MoveScore(mixed oldkey, string newpath)
+{
+ int num,score;
+ string oldpath;
+ string tip;
+
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+ if (!stringp(newpath))
+ return SCORE_INVALID_ARG;
+
+ if (stringp(oldkey)) {
+ oldkey = load_name(oldkey);
+ num=npcs[oldkey,NPC_NUMBER];
+ }
+ else if (intp(oldkey)) num=oldkey;
+ else return SCORE_INVALID_ARG;
+
+ if (!member(by_num,num)) return SCORE_INVALID_ARG;
+
+ tip=getTipFromList(oldkey);
+ oldpath = by_num[num, BYNUM_KEY];
+ score = by_num[num, BYNUM_SCORE];
+
+ if (member(npcs, oldpath)) {
+ m_delete(npcs, oldpath);
+ removeTip(oldkey);
+ if(tip!="") addTip(newpath,tip);
+ npcs[newpath, NPC_SCORE] = score;
+ npcs[newpath, NPC_NUMBER] = num;
+ }
+ else return SCORE_INVALID_ARG;
+
+ by_num += ([num: newpath; score]);
+
+ ClearUsersEKCache();
+ save_object(SCORESAVEFILE);
+ write_file(SCORELOGFILE,sprintf("MOVESCORE: %s %s %s (%s, %O)\n",
+ strftime("%d%m%Y-%T",time()),oldpath,newpath,
+ geteuid(previous_object()),this_interactive()));
+
+ while(remove_call_out("DumpNPCs") != -1) ;
+ call_out("DumpNPCs",60);
+ return 1;
+}
+
+// liefert alle Kills des Spielers zurueck, auch solche, die momentan
+// ausgetragen/deaktiviet sind.
+public string QueryAllKills(string pl)
+{
+ return (MASTER->query_ek(pl) || "");
+}
+
+// filtert alle Eintraege aus dem Bitstring heraus, die fuer
+// ausgetragene/inaktive EKs stehen.
+public string QueryKills(string pl) {
+ string res = (string)MASTER->query_ek(pl) || "";
+ // vergleichen mit den aktiven EKs aus active_eks und nur jene Bits
+ // zurueckliefern, die in beiden Strings gesetzt sind.
+ return and_bits(res,active_eks);
+}
+
+public int QueryKillPoints(mixed pl) {
+
+ if (!allowed() &&
+ (!previous_object()
+ || strstr(object_name(previous_object()), "/gilden/") != 0) )
+ return 0;
+
+ if (!stringp(pl)) pl=getuid(pl);
+
+ if (member(users_ek,pl)) return users_ek[pl];
+
+ string s = (MASTER->query_ek(pl) || "");
+
+ int p=-1;
+ int summe;
+ while ((p=next_bit(s,p)) != -1) {
+ summe+=by_num[p,BYNUM_SCORE];
+ }
+
+ users_ek += ([pl:summe]);
+ return summe;
+}
+
+public mixed *QueryNPCbyNumber(int num)
+{
+ if (!allowed())
+ return 0;
+
+ if (member(by_num, num))
+ return ({num, by_num[num, BYNUM_SCORE], by_num[num, BYNUM_KEY]});
+
+ return 0;
+}
+
+protected mixed *StaticQueryNPCbyNumber(int num)
+{
+ if (member(by_num, num))
+ return ({num, by_num[num, BYNUM_SCORE], by_num[num, BYNUM_KEY]});
+
+ return 0;
+}
+
+public mixed *QueryNPCbyObject(object o)
+{
+ string key;
+ int val;
+
+ key=load_name(o);
+ if (member(npcs,key)) {
+ val = npcs[key,NPC_NUMBER];
+ return ({val, by_num[val, BYNUM_SCORE], by_num[val, BYNUM_KEY]});
+ }
+ return 0;
+}
+
+public int GiveKill(object pl, int bit)
+{
+ mixed info;
+ object po;
+ int drin;
+ string pls, ek;
+
+
+ if (!pointerp(info = StaticQueryNPCbyNumber(bit)))
+ return -1;
+
+ if ((!po=previous_object())
+ || load_name(po) != info[SCORE_KEY])
+ return -2;
+
+ pls=getuid(pl);
+
+ // wenn unbestaetigt, Spieler fuer spaeter merken
+ if (member(unconfirmed_scores, bit)) {
+ if (member(unconfirmed_scores[bit], pls) == -1)
+ unconfirmed_scores[bit] += ({pls});
+ }
+ else {
+ // sonst wird das Bit direkt im Spieler gesetzt.
+ ek = (MASTER->query_ek(pls) || "");
+ if (test_bit(ek, bit))
+ return -3;
+ ek = set_bit(ek, bit);
+ MASTER->update_ek(pls, ek);
+ // Vergabestatistik hochzaehlen.
+ npcs[by_num[bit,BYNUM_KEY],NPC_COUNT]++;
+ }
+
+ if (member(users_ek, pls))
+ m_delete(users_ek, pls);
+
+ EK_GIVENLOG(sprintf("%s: %s", info[SCORE_KEY], pls));
+
+ return info[SCORE_SCORE];
+}
+
+public int HasKill(mixed pl, mixed npc)
+{
+ string fn, *pls;
+
+ if (!objectp(pl) && !stringp(pl) &&
+ !objectp(npc) && !stringp(npc) && !intp(npc))
+ return 0;
+ if (!stringp(pl))
+ pl=getuid(pl);
+
+ if (intp(npc))
+ npc=by_num[npc,BYNUM_KEY];
+ fn=load_name(npc);
+
+ if (!member(npcs, fn)) return 0;
+
+ int bit = npcs[fn, NPC_NUMBER];
+
+ if (pointerp(pls=unconfirmed_scores[bit]) &&
+ member(pls,pl) != -1)
+ return 1;
+
+ string eks = (MASTER->query_ek(pl) || "");
+
+ return test_bit(eks, bit);
+}
+
+private void WriteDumpFile(string *keys) {
+ int maxn;
+
+ if (!pointerp(keys)) return;
+
+ rm(SCOREDUMPFILE);
+
+ write_file(SCOREDUMPFILE,sprintf("%5s %5s %4s %s\n",
+ "Nr.", "Cnt", "Sc", "Objekt"));
+ foreach(string key: keys) {
+ write_file(SCOREDUMPFILE,sprintf("%5d %5d %4d %O\n",
+ npcs[key,NPC_NUMBER], npcs[key,NPC_COUNT],
+ npcs[key,NPC_SCORE], key));
+ maxn += npcs[key,NPC_SCORE];
+ }
+ write_file(SCOREDUMPFILE,sprintf(
+ "========================================================\n"
+ "NPCs gesamt: %d Punkte\n\n",maxn));
+}
+
+public varargs int DumpNPCs(int sortkey) {
+
+ if (extern_call() && !allowed()) return SCORE_NO_PERMISSION;
+ if (!intp(sortkey)) return SCORE_INVALID_ARG;
+
+ rm(SCOREDUMPFILE);
+
+ // sortieren
+ string *keys=sort_array(m_indices(npcs), function int (string a, string b) {
+ return(npcs[a,sortkey] < npcs[b,sortkey]); } );
+ call_out(#'WriteDumpFile, 2, keys);
+
+ return 1;
+}
+
+public int SetScoreBit(string pl, int bit)
+{
+ string ek;
+
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+
+ ek = (MASTER->query_ek(pl) || "");
+ ek = set_bit(ek, bit);
+ MASTER->update_ek(pl, ek);
+
+ // Vergabestatistik hochzaehlen.
+ npcs[by_num[bit,BYNUM_KEY],NPC_COUNT]++;
+
+ if (member(users_ek, pl))
+ m_delete(users_ek, pl);
+
+ write_file(SCORELOGFILE,sprintf("SETBIT: %s %s %5d Sc: %.3d %s (%s, %O)\n",
+ strftime("%d%m%Y-%T",time()),pl, bit,
+ by_num[bit,BYNUM_SCORE], by_num[bit,BYNUM_KEY],
+ geteuid(previous_object()), this_interactive()));
+ return 1;
+}
+
+public int ClearScoreBit(string pl, int bit)
+{
+ string ek;
+
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+
+ ek = (MASTER->query_ek(pl) || "");
+ ek = clear_bit(ek, bit);
+ MASTER->update_ek(pl, ek);
+
+ // Vergabestatistik runterzaehlen.
+ npcs[by_num[bit,BYNUM_KEY],NPC_COUNT]--;
+
+ if (member(users_ek, pl))
+ m_delete(users_ek, pl);
+
+ write_file(SCORELOGFILE,sprintf(
+ "CLEARBIT: %s %s %5d Sc: %.3d %s (%s, %O)\n",
+ strftime("%d%m%Y-%T",time()),pl,bit,
+ by_num[bit,BYNUM_SCORE],by_num[bit,BYNUM_KEY],
+ geteuid(previous_object()), this_interactive()));
+ return 1;
+}
+
+private status ektipAllowed()
+{
+ status poOK;
+ string poName;
+ status ret;
+
+ poName=load_name(previous_object());
+ poOK=previous_object() &&
+ ((previous_object()==find_object(EKTIPGIVER)) || (poName==EKTIPLIST) );
+
+ ret=allowed() ||
+ (this_player() && this_interactive() && previous_object() &&
+ this_interactive()==this_player() && poOK);
+ return ret;
+}
+
+// liefert alle EKs, die aktiv sind und die der Spieler noch nicht hat in
+// einem Mapping entsprechend npcs zurueck.
+public mapping getFreeEKsForPlayer(object player)
+{
+ if(!ektipAllowed() || !objectp(player) || !query_once_interactive(player)){
+ return ([]);
+ }
+ // alle EKs, die der Spieler hat
+ string eks = (string)master()->query_ek(getuid(player));
+ // als Tips kommen alle in Frage, die er nicht hat, vor dem Invertieren muss
+ // aber sichergestellt werden, dass eks min. so lang ist wie active_eks, da
+ // die Invertierung ja z.B. nur EKs 0-1700 beruecksichtigt, wenn 1700 der
+ // hoechste EK im Spieler ist und dann als Tips alle EKs ueber
+ // 1700 verlorengingen.
+ // hier wird das letzte Bit von active_eks ermittelt und das darauf folgende
+ // Bit im Spieler-EK-String gesetzt und wieder geloescht, woraufhin der
+ // EK-String min. so lang wie active_eks ist. (es ist egal, wenn er
+ // laenger ist, auch egal, wenn man ein Bit ueberschreibt, das faellt alles
+ // gleich beim and_bits() raus.
+ int lb = last_bit(active_eks) + 1;
+ eks = clear_bit(set_bit(eks, lb), lb);
+ // jetzt invertieren
+ string non_eks = invert_bits(eks);
+ // jetzt vorhande EK-Tips ausfiltern. Im Prinzip gleiches Spiel wie oben.
+ string ektips = (string)master()->query_ektips(getuid(player));
+ // jetzt alle nicht als Tip vergebenen NPC ermitteln, vor dem Invertieren
+ // wieder Laenge angleichen...
+ ektips = invert_bits(clear_bit(set_bit(ektips, lb), lb));
+ // verunden liefert EKs, die der Spieler nicht hat und nicht als Tip hat
+ ektips = and_bits(ektips, non_eks);
+
+ // und nun die inaktive EKs ausfiltern, nochmal verunden
+ ektips = and_bits(ektips, active_eks);
+
+ // mal Platz reservieren, entsprechend der Menge an Bits
+ mapping freeKills = m_allocate( count_bits(ektips), 2);
+ // durch alle jetzt gesetzten Bits laufen, d.h. alle EKs, die der Spieler
+ // nicht hat und ein Mapping a la npcs erstellen.
+ int p=-1;
+ while ( (p=next_bit(ektips, p)) != -1) {
+ freeKills += ([ by_num[p,0]: p; by_num[p,1] ]);
+ }
+
+ return freeKills;
+}
+
+public int addTip(mixed key,string tip)
+{
+ string fn;
+
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+
+ if (!tip || (!objectp(key) && !stringp(key)))
+ return SCORE_INVALID_ARG;
+
+ fn=load_name(key);
+
+ if (!member(npcs, fn)) return SCORE_INVALID_ARG;
+ tipList+=([fn:tip]);
+ save_object(SCORESAVEFILE);
+
+ return 1;
+}
+
+public int changeTip(mixed key,string tip)
+{
+ return addTip(key,tip);
+}
+
+public int removeTip(mixed key)
+{
+ string fn;
+
+ if (!allowed())
+ return SCORE_NO_PERMISSION;
+
+ if ((!objectp(key) && !stringp(key)))
+ return SCORE_INVALID_ARG;
+
+ fn=load_name(key);
+
+ if (!member(tipList, fn)) return SCORE_INVALID_ARG;
+
+ m_delete(tipList,fn);
+ save_object(SCORESAVEFILE);
+
+ return 1;
+}
+
+private string getTipFromList(mixed key)
+{
+ string fn;
+
+ if (!ektipAllowed())
+ return "";
+
+ if ((!objectp(key) && !stringp(key)))
+ return "";
+
+ fn=load_name(key);
+
+ if (!member(tipList, fn)) return "";
+
+ return tipList[fn];
+}
+
+private string _getTip(mixed key)
+{
+ string fn;
+ string tip;
+ string* path;
+
+ if ((!objectp(key) && !stringp(key)))
+ return "";
+
+ fn=load_name(key);
+
+ if(!member(npcs,fn)) return "";
+
+ tip=getTipFromList(fn);
+ if(!tip || tip==""){
+ path=explode(fn,"/")-({""});
+ if(sizeof(path)<3) return "";
+ if(path[0]=="players") {
+ string tiptext;
+ if ( path[1] == "ketos" )
+ tiptext = "Ketos im Gebirge";
+ else if ( path[1] == "boing" && path[2] == "friedhof" )
+ tiptext = "Boing im eisigen Polar";
+ else
+ tiptext = capitalize(path[1]);
+ return "Schau Dich doch mal bei "+tiptext+" um.";
+ }
+
+ if(path[0]=="d")
+ {
+ tip+="Schau Dich doch mal ";
+
+ if(file_size("/players/"+path[2])==-2)
+ {
+ tip+="bei "+capitalize(path[2]+" ");
+ }
+ if ( path[1]=="polar" && file_size("/players/"+path[3])==-2 )
+ {
+ tip+="bei "+capitalize(path[3])+" ";
+ }
+
+ if(path[1]=="anfaenger")
+ tip+="in den Anfaengergebieten ";
+ if(path[1]=="fernwest")
+ tip+="in Fernwest ";
+ if(path[1]=="dschungel")
+ tip+="im Dschungel ";
+ if(path[1]=="schattenwelt")
+ tip+="in der Welt der Schatten ";
+ if(path[1]=="unterwelt")
+ tip+="in der Unterwelt ";
+ if(path[1]=="gebirge")
+ tip+="im Gebirge ";
+ if(path[1]=="seher")
+ tip+="bei den Sehergebieten ";
+ if(path[1]=="vland")
+ tip+="auf dem Verlorenen Land ";
+ if(path[1]=="ebene")
+ tip+="in der Ebene ";
+ if(path[1]=="inseln")
+ tip+="auf den Inseln ";
+ if(path[1]=="wald")
+ tip+="im Wald ";
+ if(path[1]=="erzmagier")
+ tip+="bei den Erzmagiern ";
+ if(path[1]=="polar")
+ {
+ if (path[2]=="files.chaos")
+ tip+="in den Raeumen der Chaosgilde ";
+ tip+="im eisigen Polar ";
+ }
+ if(path[1]=="wueste")
+ tip+="in der Wueste ";
+ tip+="um.";
+ }
+ else if ( path[0]=="gilden" )
+ {
+ tip+="Schau Dich doch mal";
+ switch( path[1] )
+ {
+ case "mon.elementar":
+ tip+=" unter den Anfuehrern der Elementargilde";
+ break;
+ case "files.dunkelelfen":
+ tip+=" unter den Anfuehrern der Dunkelelfengilde";
+ break;
+ case "files.klerus":
+ tip+=" beim Klerus";
+ break;
+ case "files.werwoelfe":
+ tip+=" unter den Anfuehrern der Werwoelfe";
+ break;
+ case "files.chaos":
+ tip+=" unter den Anfuehrern der Chaosgilde";
+ break;
+ default:
+ tip+=" in einer der Gilden";
+ break;
+ }
+ tip+=" um.";
+ }
+ else if ( path[0] == "p" )
+ {
+ tip+="Schau Dich doch mal ";
+ switch( path[1] )
+ {
+ case "zauberer":
+ tip+="in der Zauberergilde zu Taramis";
+ break;
+ case "kaempfer":
+ tip+="bei den Angehoerigen des Koru-Tschakar-Struvs";
+ break;
+ case "katzenkrieger":
+ tip+="bei der Gilde der Katzenkrieger";
+ break;
+ case "tanjian":
+ tip+="unter den Meistern der Tanjiangilde";
+ break;
+ }
+ tip+=" um.";
+ }
+ }
+ return tip;
+}
+
+// return valid tips from database or existing
+public string getTip(mixed key)
+{
+ string fn;
+ string tip;
+ string* path;
+
+ if (!ektipAllowed())
+ return "";
+
+ return _getTip(key);
+}
+
+// liefert ein Array mit allen Objekten zurueck, auf die bitstr verweist, also
+// eine Liste aller Objekte, die als Tip vergeben wurden.
+private string* makeTiplistFromBitString(string bitstr)
+{
+ string * ret= allocate(count_bits(bitstr));
+ // ueber alle gesetzten bits laufen und Array zusammensammeln
+ int i;
+ int p=-1;
+ while ((p=next_bit(bitstr,p)) != -1) {
+ ret[i] = by_num[p, 0];
+ i++;
+ }
+ // zur Sicherheit
+ ret -= ({0});
+
+ return ret;
+}
+
+// gibt die Objektnamen der EK-Tips vom jeweiligen Spieler zurueck.
+public string *QueryTipObjects(mixed player) {
+
+ if (extern_call() && !allowed())
+ return 0;
+ if (objectp(player) && query_once_interactive(player))
+ player=getuid(player);
+ if (!stringp(player))
+ return 0;
+
+ string tipstr=(string)master()->query_ektips(player);
+ // jetzt EK-Tips ausfiltern, die erledigt sind. Dazu EK-Liste holen...
+ string eks=(string)master()->query_ek(player);
+ // als Tips kommen alle in Frage, die er nicht hat, vor dem Invertieren muss
+ // aber sichergestellt werden, dass eks min. so lang ist wie tipstr, da
+ // die Invertierung ja z.B. nur EKs 0-1700 beruecksichtigt, wenn 1700 der
+ // hoechste EK im Spieler ist und dann alle Tips ueber
+ // 1700 verlorengingen.
+ // hier wird das letzte Bit von tipstr ermittelt und das darauf folgende
+ // Bit im Spieler-EK-String gesetzt und wieder geloescht, woraufhin der
+ // EK-String min. so lang wie der Tipstring ist. (es ist egal, wenn er
+ // laenger ist, auch egal, wenn man ein Bit ueberschreibt, das faellt alles
+ // gleich beim and_bits() raus.
+ int lb = last_bit(tipstr) + 1;
+ eks = clear_bit(set_bit(eks, lb), lb);
+ // jetzt invertieren
+ string non_eks = invert_bits(eks);
+ // jetzt verunden und man hat die Tips, die noch nicht gehauen wurden.
+ tipstr = and_bits(tipstr, non_eks);
+ // noch inaktive EKs ausfiltern...
+ tipstr = and_bits(tipstr, active_eks);
+
+ return makeTiplistFromBitString(tipstr);
+}
+
+public string allTipsForPlayer(object player)
+{
+
+ if(!player || !this_interactive() ||
+ (this_interactive()!=player && !IS_ARCH(this_interactive())) )
+ return "";
+
+ string *tips = QueryTipObjects(player);
+
+ tips = map(tips, #'_getTip);
+ tips -= ({0,""});
+
+ return implode(tips, "\n");
+}
+
+public status playerMayGetTip(object player)
+{
+ int numElegible;
+ int numReceived;
+ int lvl;
+ int i;
+ string tips;
+
+ if(!ektipAllowed() || !player || !query_once_interactive(player))
+ return 0;
+
+ if(!player || !query_once_interactive(player))
+ return 0;
+
+ lvl=(int)player->QueryProp(P_LEVEL);
+ numElegible=0;
+ i=sizeof(EKTIPS_LEVEL_LIMITS)-1;
+
+ if(lvl>EKTIPS_LEVEL_LIMITS[i])
+ numElegible+=(lvl-EKTIPS_LEVEL_LIMITS[i]);
+
+ for(i;i>=0;i--){
+ if(lvl>=EKTIPS_LEVEL_LIMITS[i]) numElegible++;
+ }
+
+ tips=(string)MASTER->query_ektips(getuid(player)) || "";
+ // inaktive EKs ausfiltern.
+ tips = and_bits(tips, active_eks);
+ // und Gesamtzahl an Tips zaehlen. Hier werden erledigte Tips explizit nicht
+ // ausgefiltert!
+ numReceived=count_bits(tips);
+
+ return numElegible>numReceived;
+}
+
+public string giveTipForPlayer(object player)
+{
+ string* tmp;
+ mapping free;
+ string tip,pl,ektip;
+ int index;
+
+ if(!ektipAllowed() || !player ||
+ !query_once_interactive(player) || !playerMayGetTip(player))
+ return "";
+
+ pl=getuid(player);
+ free=getFreeEKsForPlayer(player);
+
+ if(!mappingp(free) || sizeof(free)==0)
+ return "";
+
+ tmp=m_indices(free);
+
+ ektip=(string)MASTER->query_ektips(pl) || "";
+
+ foreach(int i: EKTIPS_MAX_RETRY) {
+ index=random(sizeof(tmp));
+ tip=getTip(tmp[index]);
+ if (stringp(tip) && sizeof(tip)) {
+ ektip=set_bit(ektip,npcs[tmp[index],NPC_NUMBER]);
+ MASTER->update_ektips(pl,ektip);
+ break; //fertig
+ }
+ }
+
+ return tip;
+}
+
+// checkt NPCs auf Existenz und Ladbarkeit
+public void CheckNPCs(int num) {
+ string fn;
+ object ekob;
+ if (!num) rm(SCORECHECKFILE);
+ while (num <= lastNum && get_eval_cost() > 1480000) {
+ if (!by_num[num,1] || !by_num[num,0]) {
+ num++;
+ continue;
+ }
+ fn = by_num[num,0] + ".c";
+ if (file_size(fn) <= 0) {
+ // File nicht existent
+ write_file(SCORECHECKFILE, sprintf(
+ "EK %.4d ist nicht existent (%s)\n",num,fn));
+ }
+ else if (catch(ekob=load_object(fn)) || !objectp(ekob) ) {
+ // NPC offenbar nicht ladbar.
+ write_file(SCORECHECKFILE, sprintf(
+ "EK %.4d ist nicht ladbar (%s)\n",num,fn));
+ }
+ num++;
+ }
+ ZDEBUG(sprintf("%d NPC checked",num));
+ if (num <= lastNum)
+ call_out(#'CheckNPCs,4,num);
+ else
+ ZDEBUG("Finished!");
+}
+
+// liquidiert einen EK endgueltig. An diesem Punkt wird davon augegangen, dass
+// kein Spieler den EK mehr gesetzt hat!
+private void LiquidateEK(int bit) {
+
+ if (!intp(bit) || bit < 0) return;
+
+ string obname = by_num[bit, BYNUM_KEY];
+ int score = by_num[bit, BYNUM_SCORE];
+
+ if (member(npcs, obname) && (npcs[obname, NPC_NUMBER] == bit)) {
+ m_delete(by_num, bit);
+ m_delete(npcs, obname);
+ if (member(unconfirmed_scores,bit))
+ m_delete(unconfirmed_scores,bit);
+ active_eks = clear_bit(active_eks,bit);
+ removeTip(obname);
+ free_num += ({bit});
+ write_file(SCOREAUTOLOG,sprintf(
+ "LIQUIDATEEK: %s %5d Score %3d %s\n",
+ strftime("%d%m%Y-%T",time()), bit, score, obname));
+ }
+}
+
+private void check_player(string pl) {
+ int changed, changed2;
+
+ // EKs pruefen
+ string eks=(string)master()->query_ek(pl) || "";
+ string *opfer=allocate( (sizeof(eks)*6)+1, "");
+ int p=-1;
+ while ((p=next_bit(eks,p)) != -1) {
+ if (!member(by_num, p)) {
+ write_file(SCORECHECKFILE, sprintf(
+ "UNKNOWNEK %s %5d in %s gefunden.\n",
+ strftime("%d%m%Y-%T",time()), p, pl));
+ }
+ // wenn das aktuelle Bit geloescht werden soll, also in to_be_removed
+ // steht...
+ if (member(to_be_removed,p) != -1) {
+ eks = clear_bit(eks,p);
+ changed=1;
+ write_file(EKCLEANLOG,sprintf(
+ "CLEARBIT: %s %O %5d %s\n",
+ strftime("%d%m%Y-%T",time()), pl, p,
+ by_num[p,BYNUM_KEY] || "NoName"));
+ }
+ else {
+ // sonst statistikpflege
+ npcs[by_num[p,BYNUM_KEY],NPC_COUNT]++;
+ // loggen, welche NPC der Spieler hat
+ opfer[p]=to_string(p);
+ }
+ }
+ // und noch die Ek-Tips...
+ string ektips = (string)master()->query_ektips(pl) || "";
+ p = -1;
+ while ((p=next_bit(ektips,p)) != -1) {
+ if (!member(by_num, p)) {
+ write_file(EKCLEANLOG, sprintf(
+ "UNKNOWNEK %s %5d in %s (EKTips) gefunden - clearing.\n",
+ strftime("%d%m%Y-%T",time()), p, pl));
+ ektips = clear_bit(ektips, p); // hier direkt loeschen.
+ changed2 = 1;
+ }
+ // wenn das aktuelle Bit geloescht werden soll, also in to_be_removed
+ // steht...
+ else if (member(to_be_removed,p) != -1) {
+ ektips = clear_bit(ektips,p);
+ changed2=1;
+ write_file(EKCLEANLOG,sprintf(
+ "CLEAREKTIP: %s %O %5d %s\n",
+ strftime("%d%m%Y-%T",time()), pl, p,
+ by_num[p,BYNUM_KEY] || "NoName"));
+ }
+ }
+
+ if (changed) {
+ master()->update_ek(pl, eks);
+ }
+ if (changed2) {
+ master()->update_ektips(pl, ektips);
+ }
+ opfer-=({""});
+ write_file(WERKILLTWEN,sprintf("%s\n%=-78s\n\n",pl,CountUp(opfer)||""));
+}
+
+public void check_all_player(mapping allplayer) {
+
+ if (extern_call() && !allowed())
+ return;
+
+ if (!mappingp(allplayer)) {
+ foreach(string key: npcs) {
+ npcs[key,NPC_COUNT]=0;
+ }
+ allplayer=(mapping)master()->get_all_players();
+ rm(WERKILLTWEN);
+ call_out(#'check_all_player,2,allplayer);
+ return;
+ }
+
+ // offenbar fertig mit allen Spielern, jetzt noch den Rest erledigen.
+ if (!sizeof(allplayer)) {
+ foreach(int bit: to_be_removed) {
+ LiquidateEK(bit);
+ }
+ to_be_removed=({});
+ save_object(SCORESAVEFILE);
+ ZDEBUG("Spielerchecks und EK-Liquidation fertig.\n");
+ return;
+ }
+
+ string dir=m_indices(allplayer)[0];
+ string *pls=allplayer[dir];
+ foreach(string pl: pls) {
+ if (get_eval_cost() < 1250000)
+ break; // spaeter weitermachen.
+ catch(check_player(pl) ; publish);
+ pls-=({pl});
+ }
+ allplayer[dir] = pls;
+
+ if (!sizeof(allplayer[dir]))
+ m_delete(allplayer,dir);
+
+ call_out(#'check_all_player,2,allplayer);
+}
+