| // 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); |
| } |
| |