blob: 576a4b9906275c1980075c47db925c7b1c473760 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// scoremaster.c - Verwaltung der eindeutigen Nummernvergabe fuer NPCs und
4// MiniQuests sowie der Stufenpunkte, die sie geben
5//
6// $Id: scoremaster.c 9170 2015-03-05 20:18:54Z Zesstra $
7#pragma strict_types
8#pragma no_clone
9#pragma no_shadow
10#pragma no_inherit
11#pragma verbose_errors
12#pragma combine_strings
13#pragma pedantic
14//#pragma range_check
15#pragma warn_deprecated
16
17#include "/secure/scoremaster.h"
18#include "/secure/wizlevels.h"
19#include <properties.h>
20#include <files.h>
21
22#define ZDEBUG(x) if (find_player("zesstra")) \
23 tell_object(find_player("zesstra"),sprintf("SCM: %s\n",x))
24
25// hoechste vergebene Nr.
26private int lastNum;
27
28// Liste alle EKs: ([obname: num; score; killcount])
29private mapping npcs = m_allocate(0,3);
30
31// Liste von Spielernamen-Wert-Paaren, die im Reset abgearbeitet wird:
32// ([plname: ({wert1, wert2, wert3, ...}) ])
33// wert > 0 bedeutet setzen des entsprechenden EKs, < 0 bedeutet loeschen.
34private mapping to_change = ([]);
35
36// Liste der EK-Tips: ([obname: Spruch])
37private mapping tipList = ([]);
38
39// Bit-Nr., die (wieder) vergeben werden duerfen.
40private int *free_num = ({});
41
42// zu entfernende EKs, Liste Bitnummern, also ints
43private int *to_be_removed = ({});
44
45// Liste von temporaeren EKs, die noch nicht bestaetigt wurden:
46// ([obname: ({plname1, plname2}) ])
47private mapping unconfirmed_scores = ([]);
48
49// alle Spieler kriegen diesen
50// Nach Nr. sortierte NPC-Liste: ([num: key; score])
51private nosave mapping by_num = m_allocate(0,2);
52
53// Cache fuer EKs von Spielern: ([plname: scoresumme])
54private nosave mapping users_ek = ([]);
55
56// bitstring, der alle aktiven EKs als gesetztes Bit enthaelt.
57private nosave string active_eks="";
58
59// Prototypen
60public mapping getFreeEKsForPlayer(object player);
61public int addTip(mixed key,string tip);
62public int changeTip(mixed key,string tip);
63public int removeTip(mixed key);
64private string getTipFromList(mixed key);
65public string getTip(mixed key);
66
67public void CheckNPCs(int num);
68public void check_all_player(mapping allplayer);
69public varargs int DumpNPCs(int sortkey);
70
71private void make_num(string key, int num, int score) {
72 by_num += ([ num : key; score ]);
73 // fuer aktive EKs, die also einen Scorewert > 0 haben, wird das jeweilige
74 // Bit gesetzt. Wird spaeter zum Ausfiltern inaktiver EKs aus den Bitstrings
75 // in den Spieler gebraucht.
76 if (score>0 && !member(unconfirmed_scores,num))
77 active_eks = set_bit(active_eks, num);
78}
79
80private int allowed()
81{
82 if (previous_object() && geteuid(previous_object())==ROOTID)
83 return 1;
84 if (!process_call() && previous_object() && this_interactive() && ARCH_SECURITY)
85 return 1;
86 return 0;
87}
88
89protected void create()
90{
91 seteuid(getuid());
92 if (!restore_object(SCORESAVEFILE))
93 {
94 lastNum=0;
95 npcs=m_allocate(0,3);
96 to_change=m_allocate(0,1);
97 tipList=([]);
98 }
99 npcs-=([0]);
100 walk_mapping(npcs, #'make_num);
101}
102
103public int ClearUsersEKCache()
104{
105 if (!allowed())
106 return SCORE_NO_PERMISSION;
107 users_ek = ([]);
108 return 1;
109}
110
111public mixed QueryUsersEKCache()
112{
113 if (!allowed())
114 return SCORE_NO_PERMISSION;
115 return users_ek;
116}
117
118public mixed Query_free_num()
119{
120 if (!allowed())
121 return SCORE_NO_PERMISSION;
122 return free_num;
123}
124
125public mixed Add_free_num(int what)
126{
127 if (!allowed())
128 return SCORE_NO_PERMISSION;
129 if (!what || !intp(what) || by_num[what])
130 return SCORE_INVALID_ARG;
131 if (member(free_num,what)==-1)
132 free_num+=({what});
133 save_object(SCORESAVEFILE);
134 write_file(SCORELOGFILE,sprintf("ADDFREENUM: %s %5d (%s, %O)\n",
135 strftime("%d%m%Y-%T",time()),what,
136 geteuid(previous_object()), this_interactive()));
137
138 return free_num;
139}
140
141public mixed Remove_free_num(int what)
142{
143 if (!allowed())
144 return SCORE_NO_PERMISSION;
145 if (!what || !intp(what))
146 return SCORE_INVALID_ARG;
147 free_num-=({what});
148 save_object(SCORESAVEFILE);
149 write_file(SCORELOGFILE,sprintf("REMOVEFREENUM: %s %5d (%s, %O)\n",
150 strftime("%d%m%Y-%T",time()),what,
151 geteuid(previous_object()),this_interactive()));
152 return free_num;
153}
154
155public mixed Query_to_change(string who)
156{
157 if (!allowed())
158 return SCORE_NO_PERMISSION;
159 if (!who)
160 return to_change;
161 if (who=="")
162 return m_indices(to_change);
163 return to_change[who];
164}
165
166public mixed Add_to_change(string who, int what)
167{
168 if (!allowed())
169 return SCORE_NO_PERMISSION;
170 if (!who || !stringp(who) || !what || !intp(what))
171 return SCORE_INVALID_ARG;
172 if (member(to_change,who))
173 {
174 to_change[who]-=({-what});
175 if (member(to_change[who],what)==-1)
176 to_change[who]+=({what});
177 }
178 else
179 to_change[who]=({what});
180 save_object(SCORESAVEFILE);
181 write_file(SCORELOGFILE,sprintf("ADDTOCHANGE: %s %s %5d (%s, %O)\n",
182 strftime("%d%m%Y-%T",time()),who,what,
183 geteuid(previous_object()), this_interactive()));
184 return to_change[who];
185}
186
187public mixed Remove_to_change(string who, int what)
188{
189 if (!allowed())
190 return SCORE_NO_PERMISSION;
191 if (!who || !stringp(who) || !what || !intp(what))
192 return SCORE_INVALID_ARG;
193 if (member(to_change,who))
194 {
195 to_change[who]-=({what});
196 if (!sizeof(to_change[who]))
197 m_delete(to_change,who);
198 }
199 save_object(SCORESAVEFILE);
200 write_file(SCORELOGFILE,sprintf("REMOVETOCHANGE: %s %s %5d (%s, %O)\n",
201 strftime("%d%m%Y-%T",time()),who,what,
202 geteuid(previous_object()), this_interactive()));
203 return to_change[who];
204}
205
206void reset()
207{
208 string *whop,who,ek;
209 mixed what;
210 int i,j,value,changed;
211
212 // falls EKs global entfernt werden sollen, schonmal den noetigen Callout
213 // starten.
214 if (sizeof(to_be_removed) && find_call_out(#'check_all_player) == -1)
215 call_out(#'check_all_player, 10, 0);
216 // EK-Mainteiner ueber unbestaetigte EKs informieren
217 if (sizeof(unconfirmed_scores)) {
218 foreach(string n: SCOREMAINTAINERS) {
219 if (objectp(find_player(n)))
220 tell_object(find_player(n),break_string(
221 "Es gibt unbestaetigte EKs im Scoremaster. Schau Dir die doch "
222 "mal an. ;-)",78, "Der Scoremaster teilt Dir mit: "));
223 }
224 }
225
226 i=sizeof(whop=m_indices(to_change))-1;
227 while (i>=0 && get_eval_cost()>100000)
228 {
229 ek = (string)(MASTER->query_ek(who=whop[i]) || "");
230 for (j=sizeof(what=to_change[who])-1;j>=0;j--) {
231 if ((value=what[j])>0) {
232 // Vergabestatistik hochzaehlen.
233 npcs[by_num[value,BYNUM_KEY],NPC_COUNT]++;
234 ek=set_bit(ek,value);
235 }
236 else {
237 // Vergabestatistik hochzaehlen.
238 npcs[by_num[-value,BYNUM_KEY],NPC_COUNT]++;
239 ek=clear_bit(ek,-value);
240 }
241 // if (find_player("rikus"))
242 //tell_object(find_player("rikus"),"SCOREMASTER "+who+" "+erg+"\n");
243
244 write_file(SCOREAUTOLOG,
245 sprintf("SET_CLEAR_BIT (reset): %s %4d %s\n",
246 who, j, strftime("%d%m%Y-%T",time()) ));
247 }
248 MASTER->update_ek(who, ek);
249
250 if (member(users_ek, who))
251 m_delete(users_ek, who);
252
253 m_delete(to_change,who);
254 changed=1;
255 i--;
256 }
257 if (changed) save_object(SCORESAVEFILE);
258}
259
260public varargs mixed QueryNPC(int score)
261{
262 string key;
263 int val;
264
265 if (!previous_object())
266 return SCORE_INVALID_ARG;
267
268 key = load_name(previous_object());
269
270 // schon bekannter EK?
271 if (member(npcs,key))
272 return ({npcs[key,NPC_NUMBER],npcs[key,NPC_SCORE]});
273
274 if (score<=0 ||
275 member(inherit_list(previous_object()),"/std/living/life.c") < 0)
276 return SCORE_INVALID_ARG;
277
278 if (key[0..8]=="/players/") return SCORE_INVALID_ARG;
279
280 if (score>2) score=2;
281
282 if (sizeof(free_num)) {
283 val = free_num[0];
284 free_num -= ({val});
285 }
286 else val=++lastNum;
287
288 npcs[key,NPC_SCORE] = score;
289 npcs[key,NPC_NUMBER] = val;
290 npcs[key,NPC_COUNT] = 0;
291 by_num += ([val: key; score]);
292 // werden noch nicht als aktive EKs gewertet, damit sie nicht als Ek-Tips
293 // vergben werden.
294 //active_eks = set_bit(active_eks, val);
295
296 unconfirmed_scores += ([ val: ({}) ]);
297
298 ClearUsersEKCache();
299 save_object(SCORESAVEFILE);
300 write_file(SCOREAUTOLOG,sprintf(
301 "ADDNPC: %s %5d %4d %s (UID: %s, TI: %O, TP: %O)\n",
302 strftime("%d%m%Y-%T",time()),val,score,key,
303 getuid(previous_object()), this_interactive(), this_player()));
304
305 while(remove_call_out("DumpNPCs") != -1) ;
306 call_out("DumpNPCs",60);
307 return ({val,score});
308}
309
310public varargs mixed NewNPC(string key,int score)
311{
312 int val;
313
314 if (!allowed())
315 return SCORE_NO_PERMISSION;
316 if (!key || !stringp(key))
317 return SCORE_INVALID_ARG;
318
319 key = load_name(key);
320 if (val=npcs[key,NPC_NUMBER])
321 return ({val,npcs[key,NPC_SCORE]});
322 if (score<=0)
323 return SCORE_INVALID_ARG;
324
325 if (sizeof(free_num)) {
326 val=free_num[0];
327 free_num=free_num[1..];
328 }
329 else val=++lastNum;
330
331 npcs[key,NPC_SCORE] = score;
332 npcs[key,NPC_NUMBER] = val;
333 npcs[key,NPC_COUNT] = 0;
334 by_num += ([val: key; score]);
335 active_eks = set_bit(active_eks, val);
336
337 ClearUsersEKCache();
338 save_object(SCORESAVEFILE);
339 write_file(SCORELOGFILE,sprintf("NEWNPC: %s %5d %4d %s (%s, %O)\n",
340 strftime("%d%m%Y-%T",time()),val,score,key,
341 geteuid(previous_object()), this_interactive()));
342 while(remove_call_out("DumpNPCs") != -1) ;
343 call_out("DumpNPCs",60);
344 return ({val,score});
345}
346
347public varargs mixed AddNPC(string key,int score) { return NewNPC(key,score); }
348
349// restauriert die Daten eines frueher geloeschten, in den Spielern noch
350// enthaltenen EKs. Moeglich, wenn man Pfad, Nr. und Punkte noch kennt.
351public int RestoreEK(string key, int bit, int score) {
352 if (!allowed())
353 return SCORE_NO_PERMISSION;
354 if (!stringp(key) || !sizeof(key)
355 || !intp(bit) || bit < 0
356 || !intp(score) || score < 0)
357 return SCORE_INVALID_ARG;
358
359 if (member(npcs,key) || member(by_num,bit))
360 return SCORE_INVALID_ARG;
361
362 npcs += ([key: bit;score;0 ]);
363 by_num += ([bit: key;score ]);
364
365 ClearUsersEKCache();
366 save_object(SCORESAVEFILE);
367 write_file(SCORELOGFILE,sprintf("RESTOREEK: %s %5d %4d %s (%s, %O)\n",
368 strftime("%d%m%Y-%T",time()), bit, score, key,
369 geteuid(previous_object()), this_interactive()));
370 while(remove_call_out("DumpNPCs") != -1) ;
371 call_out("DumpNPCs",60);
372 return 1;
373
374}
375
376public int ConfirmScore(mixed key) {
377 // Bits in den Spielern in unconfirmed_scores setzen und Statistik
378 // hochzaehlen
379 // Bit in active_eks setzen
380 // Eintrag aus unconfirmed_scores loeschen
381 int bit;
382
383 if (!allowed()) return SCORE_NO_PERMISSION;
384 if (stringp(key) && member(npcs,key)) {
385 bit = npcs[key, NPC_NUMBER];
386 }
387 else if (intp(key) && member(by_num,key)) {
388 bit = key;
389 }
390 else
391 return SCORE_INVALID_ARG;
392
393 if (!member(unconfirmed_scores, bit))
394 return SCORE_INVALID_ARG;
395
396 string obname = by_num[bit, BYNUM_KEY];
397 int score = by_num[bit,BYNUM_SCORE];
398
399 foreach(string pl: unconfirmed_scores[bit]) {
400 string eks = (string)master()->query_ek(pl);
401 eks = set_bit(eks, bit);
402 master()->update_ek(pl, eks);
403 write_file(SCOREAUTOLOG, sprintf(
404 "SETBIT: %s %5d %s\n",
405 strftime("%d%m%Y-%T",time()), bit, pl));
406 }
407 //Vergabestatistik hochzaehlen...
408 npcs[obname,NPC_COUNT]+=sizeof(unconfirmed_scores[bit]);
409
410 m_delete(unconfirmed_scores, bit);
411 active_eks = set_bit(active_eks, bit);
412 save_object(SCORESAVEFILE);
413
414 write_file(SCORELOGFILE,sprintf(
415 "CONFIRMNPC: %s %5d Score %3d %s [%s, %O]\n",
416 strftime("%d%m%Y-%T",time()), bit, score, obname,
417 getuid(previous_object()),this_interactive()));
418
419 return 1;
420}
421
422public int RejectScore(mixed key) {
423 // Eintrag aus unconfirmed_scores, npcs, by_num loeschen
424 // Bit-Nr. in free_num eintragen
425 // evtl. EK-Spruch entfernen?
426 int bit;
427
428 if (!allowed()) return SCORE_NO_PERMISSION;
429 if (stringp(key) && member(npcs,key)) {
430 bit = npcs[key, NPC_NUMBER];
431 }
432 else if (intp(key) && member(by_num,key)) {
433 bit = key;
434 }
435 else
436 return SCORE_INVALID_ARG;
437
438 if (!member(unconfirmed_scores, bit))
439 return SCORE_INVALID_ARG;
440
441 string obname = by_num[bit, BYNUM_KEY];
442 int score = by_num[bit,BYNUM_SCORE];
443
444 m_delete(by_num, bit);
445 m_delete(npcs, obname);
446 m_delete(unconfirmed_scores,bit);
447 removeTip(obname);
448 free_num += ({bit});
449
450 save_object(SCORESAVEFILE);
451
452 write_file(SCORELOGFILE,sprintf(
453 "REJECTNPC: %s %5d Score %3d %s [%s, %O]\n",
454 strftime("%d%m%Y-%T",time()), bit, score, obname,
455 getuid(previous_object()),this_interactive()));
456 return 1;
457}
458
459// unbestaetigte NPCs in ein File ausgeben
460public void DumpUnconfirmedScores() {
461 if (!objectp(this_player())) return;
462
463 write(sprintf("%5s %5s %4s %s\n",
464 "Nr.", "Cnt", "Sc", "Objekt"));
465 foreach(int bit, string *pls: unconfirmed_scores) {
466 write(sprintf("%5d %5d %4d %s\n",
467 bit, sizeof(pls), by_num[bit,BYNUM_SCORE], by_num[bit,BYNUM_KEY]));
468 }
469}
470
471public mapping _query_unconfirmed() {
472 if (allowed()) return unconfirmed_scores;
473 return 0;
474}
475
476public varargs int SetScore(mixed key,int score)
477{
478 int num;
479 string ob;
480 int oldscore;
481
482 if (!allowed())
483 return SCORE_NO_PERMISSION;
484 if (!key) return SCORE_INVALID_ARG;
485
486 if (stringp(key) && sizeof(key)) {
487 ob = load_name(key);
488 if (!member(npcs, ob)) return SCORE_INVALID_ARG;
489 num = npcs[ob, NPC_NUMBER];
490 if (ob != by_num[num, BYNUM_KEY])
491 return SCORE_INVALID_ARG;
492 }
493 else if (intp(key) && member(by_num,key) ) {
494 num = key;
495 ob = by_num[num, BYNUM_KEY];
496 if (!member(npcs, ob) || (npcs[ob, NPC_NUMBER] != num))
497 return SCORE_INVALID_ARG;
498 }
499 else
500 return SCORE_INVALID_ARG;
501
502 oldscore = by_num[num,BYNUM_SCORE];
503 by_num[num,BYNUM_SCORE] = score;
504 npcs[ob, NPC_SCORE] = score;
505
506 if (score > 0)
507 active_eks = set_bit(active_eks, num);
508 else
509 active_eks = clear_bit(active_eks, num);
510
511 ClearUsersEKCache();
512 save_object(SCORESAVEFILE);
513 write_file(SCORELOGFILE,sprintf(
514 "SETSCORE: %s %5d %.3d OSc: %.3d %s (%s, %O)\n",
515 strftime("%d%m%Y-%T",time()),num,score,oldscore, ob,
516 geteuid(previous_object()), this_interactive()));
517 while(remove_call_out("DumpNPCs") != -1) ;
518 call_out("DumpNPCs",60);
519 return 1;
520}
521
522// entfernt einen EK endgueltig und unwiderruflich und gibt die Nr. wieder
523// frei.
524// Technisch wird der EK erstmal in eine Liste eingetragen. Im Reset iteriert
525// der Master ueber alle SPieler-Savefiles und loescht den Ek aus alle
526// Spielern. Nach Abschluss wird der Eintrag in npcs geloescht und seine Nr.
527// in die Liste freier Nummern eingetragen.
528public int* MarkEKForLiquidation(mixed key) {
529 int bit;
530 if (!allowed())
531 return 0;
532 // nicht in to_be_removed aendern, wenn check_all_player() laeuft.
533 if (find_call_out(#'check_all_player) != -1)
534 return 0;
535
536 if (stringp(key) && sizeof(key)) {
537 if (!member(npcs,key)) return 0;
538 bit = npcs[key,NPC_NUMBER];
539 }
540 else if (intp(key) && key>=0) {
541 bit = key;
542 }
543 else
544 return 0;
545
546 if (member(to_be_removed,bit) == -1)
547 to_be_removed += ({bit});
548 write_file(SCORELOGFILE,sprintf("DELETEFLAG: %s %5d %s (%s, %O)\n",
549 strftime("%d%m%Y-%T",time()), bit, by_num[bit,BYNUM_KEY] || "NoName",
550 geteuid(previous_object()), this_interactive()));
551
552 save_object(SCORESAVEFILE);
553
554 return to_be_removed;
555}
556
557// geht nur, solange nach einem RemoveEK() noch kein reset() gelaufen ist!
558public int* UnmarkEKForLiquidation(mixed key) {
559 int bit;
560 if (!allowed())
561 return 0;
562 // nicht in to_be_removed aendern, wenn check_all_player() laeuft.
563 if (find_call_out(#'check_all_player) != -1)
564 return 0;
565
566 if (stringp(key) && sizeof(key)) {
567 if (!member(npcs,key)) return 0;
568 bit = npcs[key,NPC_NUMBER];
569 }
570 else if (intp(key) && key>=0) {
571 bit = key;
572 }
573 else
574 return 0;
575
576 to_be_removed -= ({bit});
577 write_file(SCORELOGFILE,sprintf("UNDELETEFLAG: %s %5d %s (%s, %O\n",
578 strftime("%d%m%Y-%T",time()),bit, by_num[bit, BYNUM_KEY] || "NoName",
579 geteuid(previous_object()), this_interactive()));
580
581 save_object(SCORESAVEFILE);
582
583 return to_be_removed;
584}
585
586public int* QueryLiquidationMarks() {
587 if (allowed())
588 return to_be_removed;
589 else
590 return 0;;
591}
592
593// setzt nur den Scorewert auf 0, sonst nix. Solche EKs koennen dann spaeter
594// durch Angabe eines neues Scorewertes reaktiviert werden.
595public int RemoveScore(mixed key) {
596 int changed;
597 int oldscore;
598
599 if (!allowed())
600 return SCORE_NO_PERMISSION;
601
602 if (stringp(key) && member(npcs,key)) {
603 int num = npcs[key, NPC_NUMBER];
604 if ( key == by_num[num, BYNUM_KEY]) {
605 oldscore = by_num[num, BYNUM_SCORE];
606 npcs[key, NPC_SCORE] = 0;
607 by_num[num, BYNUM_SCORE] = 0;
608 active_eks = clear_bit(active_eks,num);
609 write_file(SCORELOGFILE,sprintf(
610 "REMOVESCORE: %s %5d OSc: %.3d %s (%s, %O)\n",
611 strftime("%d%m%Y-%T",time()), num, oldscore, key,
612 geteuid(previous_object()), this_interactive()));
613 changed = 1;
614 }
615 }
616 else if (intp(key) && member(by_num, key)) {
617 string obname = by_num[key, BYNUM_KEY];
618 if (key == npcs[obname, NPC_NUMBER]) {
619 oldscore = by_num[key, BYNUM_SCORE];
620 npcs[obname, NPC_SCORE] = 0;
621 by_num[key, BYNUM_SCORE] = 0;
622 active_eks = clear_bit(active_eks,key);
623 write_file(SCORELOGFILE,sprintf(
624 "REMOVESCORE: %s %5d OSc: %.3d %s (%s, %O)\n",
625 strftime("%d%m%Y-%T",time()),key, oldscore, obname,
626 geteuid(previous_object()), this_interactive()));
627 changed = 1;
628 }
629 }
630
631 if (changed) {
632 ClearUsersEKCache();
633 save_object(SCORESAVEFILE);
634 while(remove_call_out("DumpNPCs") != -1) ;
635 call_out("DumpNPCs",60);
636 return 1;
637 }
638 return SCORE_INVALID_ARG;
639}
640
641public varargs int MoveScore(mixed oldkey, string newpath)
642{
643 int num,score;
644 string oldpath;
645 string tip;
646
647 if (!allowed())
648 return SCORE_NO_PERMISSION;
649 if (!stringp(newpath))
650 return SCORE_INVALID_ARG;
651
652 if (stringp(oldkey)) {
653 oldkey = load_name(oldkey);
654 num=npcs[oldkey,NPC_NUMBER];
655 }
656 else if (intp(oldkey)) num=oldkey;
657 else return SCORE_INVALID_ARG;
658
659 if (!member(by_num,num)) return SCORE_INVALID_ARG;
660
661 tip=getTipFromList(oldkey);
662 oldpath = by_num[num, BYNUM_KEY];
663 score = by_num[num, BYNUM_SCORE];
664
665 if (member(npcs, oldpath)) {
666 m_delete(npcs, oldpath);
667 removeTip(oldkey);
668 if(tip!="") addTip(newpath,tip);
669 npcs[newpath, NPC_SCORE] = score;
670 npcs[newpath, NPC_NUMBER] = num;
671 }
672 else return SCORE_INVALID_ARG;
673
674 by_num += ([num: newpath; score]);
675
676 ClearUsersEKCache();
677 save_object(SCORESAVEFILE);
678 write_file(SCORELOGFILE,sprintf("MOVESCORE: %s %s %s (%s, %O)\n",
679 strftime("%d%m%Y-%T",time()),oldpath,newpath,
680 geteuid(previous_object()),this_interactive()));
681
682 while(remove_call_out("DumpNPCs") != -1) ;
683 call_out("DumpNPCs",60);
684 return 1;
685}
686
687// liefert alle Kills des Spielers zurueck, auch solche, die momentan
688// ausgetragen/deaktiviet sind.
689public string QueryAllKills(string pl)
690{
691 return (MASTER->query_ek(pl) || "");
692}
693
694// filtert alle Eintraege aus dem Bitstring heraus, die fuer
695// ausgetragene/inaktive EKs stehen.
696public string QueryKills(string pl) {
697 string res = (string)MASTER->query_ek(pl) || "";
698 // vergleichen mit den aktiven EKs aus active_eks und nur jene Bits
699 // zurueckliefern, die in beiden Strings gesetzt sind.
700 return and_bits(res,active_eks);
701}
702
703public int QueryKillPoints(mixed pl) {
704
705 if (!allowed() &&
706 (!previous_object()
707 || strstr(object_name(previous_object()), "/gilden/") != 0) )
708 return 0;
709
710 if (!stringp(pl)) pl=getuid(pl);
711
712 if (member(users_ek,pl)) return users_ek[pl];
713
714 string s = (MASTER->query_ek(pl) || "");
715
716 int p=-1;
717 int summe;
718 while ((p=next_bit(s,p)) != -1) {
719 summe+=by_num[p,BYNUM_SCORE];
720 }
721
722 users_ek += ([pl:summe]);
723 return summe;
724}
725
726public mixed *QueryNPCbyNumber(int num)
727{
728 if (!allowed())
729 return 0;
730
731 if (member(by_num, num))
732 return ({num, by_num[num, BYNUM_SCORE], by_num[num, BYNUM_KEY]});
733
734 return 0;
735}
736
737protected mixed *StaticQueryNPCbyNumber(int num)
738{
739 if (member(by_num, num))
740 return ({num, by_num[num, BYNUM_SCORE], by_num[num, BYNUM_KEY]});
741
742 return 0;
743}
744
745public mixed *QueryNPCbyObject(object o)
746{
747 string key;
748 int val;
749
750 key=load_name(o);
751 if (member(npcs,key)) {
752 val = npcs[key,NPC_NUMBER];
753 return ({val, by_num[val, BYNUM_SCORE], by_num[val, BYNUM_KEY]});
754 }
755 return 0;
756}
757
758public int GiveKill(object pl, int bit)
759{
760 mixed info;
761 object po;
762 int drin;
763 string pls, ek;
764
765
766 if (!pointerp(info = StaticQueryNPCbyNumber(bit)))
767 return -1;
768
769 if ((!po=previous_object())
770 || load_name(po) != info[SCORE_KEY])
771 return -2;
772
773 pls=getuid(pl);
774
775 // wenn unbestaetigt, Spieler fuer spaeter merken
776 if (member(unconfirmed_scores, bit)) {
777 if (member(unconfirmed_scores[bit], pls) == -1)
778 unconfirmed_scores[bit] += ({pls});
779 }
780 else {
781 // sonst wird das Bit direkt im Spieler gesetzt.
782 ek = (MASTER->query_ek(pls) || "");
783 if (test_bit(ek, bit))
784 return -3;
785 ek = set_bit(ek, bit);
786 MASTER->update_ek(pls, ek);
787 // Vergabestatistik hochzaehlen.
788 npcs[by_num[bit,BYNUM_KEY],NPC_COUNT]++;
789 }
790
791 if (member(users_ek, pls))
792 m_delete(users_ek, pls);
793
794 EK_GIVENLOG(sprintf("%s: %s", info[SCORE_KEY], pls));
795
796 return info[SCORE_SCORE];
797}
798
799public int HasKill(mixed pl, mixed npc)
800{
801 string fn, *pls;
802
803 if (!objectp(pl) && !stringp(pl) &&
804 !objectp(npc) && !stringp(npc) && !intp(npc))
805 return 0;
806 if (!stringp(pl))
807 pl=getuid(pl);
808
809 if (intp(npc))
810 npc=by_num[npc,BYNUM_KEY];
811 fn=load_name(npc);
812
813 if (!member(npcs, fn)) return 0;
814
815 int bit = npcs[fn, NPC_NUMBER];
816
817 if (pointerp(pls=unconfirmed_scores[bit]) &&
818 member(pls,pl) != -1)
819 return 1;
820
821 string eks = (MASTER->query_ek(pl) || "");
822
823 return test_bit(eks, bit);
824}
825
826private void WriteDumpFile(string *keys) {
827 int maxn;
828
829 if (!pointerp(keys)) return;
830
831 rm(SCOREDUMPFILE);
832
833 write_file(SCOREDUMPFILE,sprintf("%5s %5s %4s %s\n",
834 "Nr.", "Cnt", "Sc", "Objekt"));
835 foreach(string key: keys) {
836 write_file(SCOREDUMPFILE,sprintf("%5d %5d %4d %O\n",
837 npcs[key,NPC_NUMBER], npcs[key,NPC_COUNT],
838 npcs[key,NPC_SCORE], key));
839 maxn += npcs[key,NPC_SCORE];
840 }
841 write_file(SCOREDUMPFILE,sprintf(
842 "========================================================\n"
843 "NPCs gesamt: %d Punkte\n\n",maxn));
844}
845
846public varargs int DumpNPCs(int sortkey) {
847
848 if (extern_call() && !allowed()) return SCORE_NO_PERMISSION;
849 if (!intp(sortkey)) return SCORE_INVALID_ARG;
850
851 rm(SCOREDUMPFILE);
852
853 // sortieren
854 string *keys=sort_array(m_indices(npcs), function int (string a, string b) {
855 return(npcs[a,sortkey] < npcs[b,sortkey]); } );
856 call_out(#'WriteDumpFile, 2, keys);
857
858 return 1;
859}
860
861public int SetScoreBit(string pl, int bit)
862{
863 string ek;
864
865 if (!allowed())
866 return SCORE_NO_PERMISSION;
867
868 ek = (MASTER->query_ek(pl) || "");
869 ek = set_bit(ek, bit);
870 MASTER->update_ek(pl, ek);
871
872 // Vergabestatistik hochzaehlen.
873 npcs[by_num[bit,BYNUM_KEY],NPC_COUNT]++;
874
875 if (member(users_ek, pl))
876 m_delete(users_ek, pl);
877
878 write_file(SCORELOGFILE,sprintf("SETBIT: %s %s %5d Sc: %.3d %s (%s, %O)\n",
879 strftime("%d%m%Y-%T",time()),pl, bit,
880 by_num[bit,BYNUM_SCORE], by_num[bit,BYNUM_KEY],
881 geteuid(previous_object()), this_interactive()));
882 return 1;
883}
884
885public int ClearScoreBit(string pl, int bit)
886{
887 string ek;
888
889 if (!allowed())
890 return SCORE_NO_PERMISSION;
891
892 ek = (MASTER->query_ek(pl) || "");
893 ek = clear_bit(ek, bit);
894 MASTER->update_ek(pl, ek);
895
896 // Vergabestatistik runterzaehlen.
897 npcs[by_num[bit,BYNUM_KEY],NPC_COUNT]--;
898
899 if (member(users_ek, pl))
900 m_delete(users_ek, pl);
901
902 write_file(SCORELOGFILE,sprintf(
903 "CLEARBIT: %s %s %5d Sc: %.3d %s (%s, %O)\n",
904 strftime("%d%m%Y-%T",time()),pl,bit,
905 by_num[bit,BYNUM_SCORE],by_num[bit,BYNUM_KEY],
906 geteuid(previous_object()), this_interactive()));
907 return 1;
908}
909
910private status ektipAllowed()
911{
912 status poOK;
913 string poName;
914 status ret;
915
916 poName=load_name(previous_object());
917 poOK=previous_object() &&
918 ((previous_object()==find_object(EKTIPGIVER)) || (poName==EKTIPLIST) );
919
920 ret=allowed() ||
921 (this_player() && this_interactive() && previous_object() &&
922 this_interactive()==this_player() && poOK);
923 return ret;
924}
925
926// liefert alle EKs, die aktiv sind und die der Spieler noch nicht hat in
927// einem Mapping entsprechend npcs zurueck.
928public mapping getFreeEKsForPlayer(object player)
929{
930 if(!ektipAllowed() || !objectp(player) || !query_once_interactive(player)){
931 return ([]);
932 }
933 // alle EKs, die der Spieler hat
934 string eks = (string)master()->query_ek(getuid(player));
935 // als Tips kommen alle in Frage, die er nicht hat, vor dem Invertieren muss
936 // aber sichergestellt werden, dass eks min. so lang ist wie active_eks, da
937 // die Invertierung ja z.B. nur EKs 0-1700 beruecksichtigt, wenn 1700 der
938 // hoechste EK im Spieler ist und dann als Tips alle EKs ueber
939 // 1700 verlorengingen.
940 // hier wird das letzte Bit von active_eks ermittelt und das darauf folgende
941 // Bit im Spieler-EK-String gesetzt und wieder geloescht, woraufhin der
942 // EK-String min. so lang wie active_eks ist. (es ist egal, wenn er
943 // laenger ist, auch egal, wenn man ein Bit ueberschreibt, das faellt alles
944 // gleich beim and_bits() raus.
945 int lb = last_bit(active_eks) + 1;
946 eks = clear_bit(set_bit(eks, lb), lb);
947 // jetzt invertieren
948 string non_eks = invert_bits(eks);
949 // jetzt vorhande EK-Tips ausfiltern. Im Prinzip gleiches Spiel wie oben.
950 string ektips = (string)master()->query_ektips(getuid(player));
951 // jetzt alle nicht als Tip vergebenen NPC ermitteln, vor dem Invertieren
952 // wieder Laenge angleichen...
953 ektips = invert_bits(clear_bit(set_bit(ektips, lb), lb));
954 // verunden liefert EKs, die der Spieler nicht hat und nicht als Tip hat
955 ektips = and_bits(ektips, non_eks);
956
957 // und nun die inaktive EKs ausfiltern, nochmal verunden
958 ektips = and_bits(ektips, active_eks);
959
960 // mal Platz reservieren, entsprechend der Menge an Bits
961 mapping freeKills = m_allocate( count_bits(ektips), 2);
962 // durch alle jetzt gesetzten Bits laufen, d.h. alle EKs, die der Spieler
963 // nicht hat und ein Mapping a la npcs erstellen.
964 int p=-1;
965 while ( (p=next_bit(ektips, p)) != -1) {
966 freeKills += ([ by_num[p,0]: p; by_num[p,1] ]);
967 }
968
969 return freeKills;
970}
971
972public int addTip(mixed key,string tip)
973{
974 string fn;
975
976 if (!allowed())
977 return SCORE_NO_PERMISSION;
978
979 if (!tip || (!objectp(key) && !stringp(key)))
980 return SCORE_INVALID_ARG;
981
982 fn=load_name(key);
983
984 if (!member(npcs, fn)) return SCORE_INVALID_ARG;
985 tipList+=([fn:tip]);
986 save_object(SCORESAVEFILE);
987
988 return 1;
989}
990
991public int changeTip(mixed key,string tip)
992{
993 return addTip(key,tip);
994}
995
996public int removeTip(mixed key)
997{
998 string fn;
999
1000 if (!allowed())
1001 return SCORE_NO_PERMISSION;
1002
1003 if ((!objectp(key) && !stringp(key)))
1004 return SCORE_INVALID_ARG;
1005
1006 fn=load_name(key);
1007
1008 if (!member(tipList, fn)) return SCORE_INVALID_ARG;
1009
1010 m_delete(tipList,fn);
1011 save_object(SCORESAVEFILE);
1012
1013 return 1;
1014}
1015
1016private string getTipFromList(mixed key)
1017{
1018 string fn;
1019
1020 if (!ektipAllowed())
1021 return "";
1022
1023 if ((!objectp(key) && !stringp(key)))
1024 return "";
1025
1026 fn=load_name(key);
1027
1028 if (!member(tipList, fn)) return "";
1029
1030 return tipList[fn];
1031}
1032
1033private string _getTip(mixed key)
1034{
1035 string fn;
1036 string tip;
1037 string* path;
1038
1039 if ((!objectp(key) && !stringp(key)))
1040 return "";
1041
1042 fn=load_name(key);
1043
1044 if(!member(npcs,fn)) return "";
1045
1046 tip=getTipFromList(fn);
1047 if(!tip || tip==""){
1048 path=explode(fn,"/")-({""});
1049 if(sizeof(path)<3) return "";
1050 if(path[0]=="players") {
1051 string tiptext;
1052 if ( path[1] == "ketos" )
1053 tiptext = "Ketos im Gebirge";
1054 else if ( path[1] == "boing" && path[2] == "friedhof" )
1055 tiptext = "Boing im eisigen Polar";
1056 else
1057 tiptext = capitalize(path[1]);
1058 return "Schau Dich doch mal bei "+tiptext+" um.";
1059 }
1060
1061 if(path[0]=="d")
1062 {
1063 tip+="Schau Dich doch mal ";
1064
1065 if(file_size("/players/"+path[2])==-2)
1066 {
1067 tip+="bei "+capitalize(path[2]+" ");
1068 }
1069 if ( path[1]=="polar" && file_size("/players/"+path[3])==-2 )
1070 {
1071 tip+="bei "+capitalize(path[3])+" ";
1072 }
1073
1074 if(path[1]=="anfaenger")
1075 tip+="in den Anfaengergebieten ";
1076 if(path[1]=="fernwest")
1077 tip+="in Fernwest ";
1078 if(path[1]=="dschungel")
1079 tip+="im Dschungel ";
1080 if(path[1]=="schattenwelt")
1081 tip+="in der Welt der Schatten ";
1082 if(path[1]=="unterwelt")
1083 tip+="in der Unterwelt ";
1084 if(path[1]=="gebirge")
1085 tip+="im Gebirge ";
1086 if(path[1]=="seher")
1087 tip+="bei den Sehergebieten ";
1088 if(path[1]=="vland")
1089 tip+="auf dem Verlorenen Land ";
1090 if(path[1]=="ebene")
1091 tip+="in der Ebene ";
1092 if(path[1]=="inseln")
1093 tip+="auf den Inseln ";
1094 if(path[1]=="wald")
1095 tip+="im Wald ";
1096 if(path[1]=="erzmagier")
1097 tip+="bei den Erzmagiern ";
1098 if(path[1]=="polar")
1099 {
1100 if (path[2]=="files.chaos")
1101 tip+="in den Raeumen der Chaosgilde ";
1102 tip+="im eisigen Polar ";
1103 }
1104 if(path[1]=="wueste")
1105 tip+="in der Wueste ";
1106 tip+="um.";
1107 }
1108 else if ( path[0]=="gilden" )
1109 {
1110 tip+="Schau Dich doch mal";
1111 switch( path[1] )
1112 {
1113 case "mon.elementar":
1114 tip+=" unter den Anfuehrern der Elementargilde";
1115 break;
1116 case "files.dunkelelfen":
1117 tip+=" unter den Anfuehrern der Dunkelelfengilde";
1118 break;
1119 case "files.klerus":
1120 tip+=" beim Klerus";
1121 break;
1122 case "files.werwoelfe":
1123 tip+=" unter den Anfuehrern der Werwoelfe";
1124 break;
1125 case "files.chaos":
1126 tip+=" unter den Anfuehrern der Chaosgilde";
1127 break;
1128 default:
1129 tip+=" in einer der Gilden";
1130 break;
1131 }
1132 tip+=" um.";
1133 }
1134 else if ( path[0] == "p" )
1135 {
1136 tip+="Schau Dich doch mal ";
1137 switch( path[1] )
1138 {
1139 case "zauberer":
1140 tip+="in der Zauberergilde zu Taramis";
1141 break;
1142 case "kaempfer":
1143 tip+="bei den Angehoerigen des Koru-Tschakar-Struvs";
1144 break;
1145 case "katzenkrieger":
1146 tip+="bei der Gilde der Katzenkrieger";
1147 break;
1148 case "tanjian":
1149 tip+="unter den Meistern der Tanjiangilde";
1150 break;
1151 }
1152 tip+=" um.";
1153 }
1154 }
1155 return tip;
1156}
1157
1158// return valid tips from database or existing
1159public string getTip(mixed key)
1160{
1161 string fn;
1162 string tip;
1163 string* path;
1164
1165 if (!ektipAllowed())
1166 return "";
1167
1168 return _getTip(key);
1169}
1170
1171// liefert ein Array mit allen Objekten zurueck, auf die bitstr verweist, also
1172// eine Liste aller Objekte, die als Tip vergeben wurden.
1173private string* makeTiplistFromBitString(string bitstr)
1174{
1175 string * ret= allocate(count_bits(bitstr));
1176 // ueber alle gesetzten bits laufen und Array zusammensammeln
1177 int i;
1178 int p=-1;
1179 while ((p=next_bit(bitstr,p)) != -1) {
1180 ret[i] = by_num[p, 0];
1181 i++;
1182 }
1183 // zur Sicherheit
1184 ret -= ({0});
1185
1186 return ret;
1187}
1188
1189// gibt die Objektnamen der EK-Tips vom jeweiligen Spieler zurueck.
1190public string *QueryTipObjects(mixed player) {
1191
1192 if (extern_call() && !allowed())
1193 return 0;
1194 if (objectp(player) && query_once_interactive(player))
1195 player=getuid(player);
1196 if (!stringp(player))
1197 return 0;
1198
1199 string tipstr=(string)master()->query_ektips(player);
1200 // jetzt EK-Tips ausfiltern, die erledigt sind. Dazu EK-Liste holen...
1201 string eks=(string)master()->query_ek(player);
1202 // als Tips kommen alle in Frage, die er nicht hat, vor dem Invertieren muss
1203 // aber sichergestellt werden, dass eks min. so lang ist wie tipstr, da
1204 // die Invertierung ja z.B. nur EKs 0-1700 beruecksichtigt, wenn 1700 der
1205 // hoechste EK im Spieler ist und dann alle Tips ueber
1206 // 1700 verlorengingen.
1207 // hier wird das letzte Bit von tipstr ermittelt und das darauf folgende
1208 // Bit im Spieler-EK-String gesetzt und wieder geloescht, woraufhin der
1209 // EK-String min. so lang wie der Tipstring ist. (es ist egal, wenn er
1210 // laenger ist, auch egal, wenn man ein Bit ueberschreibt, das faellt alles
1211 // gleich beim and_bits() raus.
1212 int lb = last_bit(tipstr) + 1;
1213 eks = clear_bit(set_bit(eks, lb), lb);
1214 // jetzt invertieren
1215 string non_eks = invert_bits(eks);
1216 // jetzt verunden und man hat die Tips, die noch nicht gehauen wurden.
1217 tipstr = and_bits(tipstr, non_eks);
1218 // noch inaktive EKs ausfiltern...
1219 tipstr = and_bits(tipstr, active_eks);
1220
1221 return makeTiplistFromBitString(tipstr);
1222}
1223
1224public string allTipsForPlayer(object player)
1225{
1226
1227 if(!player || !this_interactive() ||
1228 (this_interactive()!=player && !IS_ARCH(this_interactive())) )
1229 return "";
1230
1231 string *tips = QueryTipObjects(player);
1232
1233 tips = map(tips, #'_getTip);
1234 tips -= ({0,""});
1235
1236 return implode(tips, "\n");
1237}
1238
1239public status playerMayGetTip(object player)
1240{
1241 int numElegible;
1242 int numReceived;
1243 int lvl;
1244 int i;
1245 string tips;
1246
1247 if(!ektipAllowed() || !player || !query_once_interactive(player))
1248 return 0;
1249
1250 if(!player || !query_once_interactive(player))
1251 return 0;
1252
1253 lvl=(int)player->QueryProp(P_LEVEL);
1254 numElegible=0;
1255 i=sizeof(EKTIPS_LEVEL_LIMITS)-1;
1256
1257 if(lvl>EKTIPS_LEVEL_LIMITS[i])
1258 numElegible+=(lvl-EKTIPS_LEVEL_LIMITS[i]);
1259
1260 for(i;i>=0;i--){
1261 if(lvl>=EKTIPS_LEVEL_LIMITS[i]) numElegible++;
1262 }
1263
1264 tips=(string)MASTER->query_ektips(getuid(player)) || "";
1265 // inaktive EKs ausfiltern.
1266 tips = and_bits(tips, active_eks);
1267 // und Gesamtzahl an Tips zaehlen. Hier werden erledigte Tips explizit nicht
1268 // ausgefiltert!
1269 numReceived=count_bits(tips);
1270
1271 return numElegible>numReceived;
1272}
1273
1274public string giveTipForPlayer(object player)
1275{
1276 string* tmp;
1277 mapping free;
1278 string tip,pl,ektip;
1279 int index;
1280
1281 if(!ektipAllowed() || !player ||
1282 !query_once_interactive(player) || !playerMayGetTip(player))
1283 return "";
1284
1285 pl=getuid(player);
1286 free=getFreeEKsForPlayer(player);
1287
1288 if(!mappingp(free) || sizeof(free)==0)
1289 return "";
1290
1291 tmp=m_indices(free);
1292
1293 ektip=(string)MASTER->query_ektips(pl) || "";
1294
1295 foreach(int i: EKTIPS_MAX_RETRY) {
1296 index=random(sizeof(tmp));
1297 tip=getTip(tmp[index]);
1298 if (stringp(tip) && sizeof(tip)) {
1299 ektip=set_bit(ektip,npcs[tmp[index],NPC_NUMBER]);
1300 MASTER->update_ektips(pl,ektip);
1301 break; //fertig
1302 }
1303 }
1304
1305 return tip;
1306}
1307
1308// checkt NPCs auf Existenz und Ladbarkeit
1309public void CheckNPCs(int num) {
1310 string fn;
1311 object ekob;
1312 if (!num) rm(SCORECHECKFILE);
1313 while (num <= lastNum && get_eval_cost() > 1480000) {
1314 if (!by_num[num,1] || !by_num[num,0]) {
1315 num++;
1316 continue;
1317 }
1318 fn = by_num[num,0] + ".c";
1319 if (file_size(fn) <= 0) {
1320 // File nicht existent
1321 write_file(SCORECHECKFILE, sprintf(
1322 "EK %.4d ist nicht existent (%s)\n",num,fn));
1323 }
1324 else if (catch(ekob=load_object(fn)) || !objectp(ekob) ) {
1325 // NPC offenbar nicht ladbar.
1326 write_file(SCORECHECKFILE, sprintf(
1327 "EK %.4d ist nicht ladbar (%s)\n",num,fn));
1328 }
1329 num++;
1330 }
1331 ZDEBUG(sprintf("%d NPC checked",num));
1332 if (num <= lastNum)
1333 call_out(#'CheckNPCs,4,num);
1334 else
1335 ZDEBUG("Finished!");
1336}
1337
1338// liquidiert einen EK endgueltig. An diesem Punkt wird davon augegangen, dass
1339// kein Spieler den EK mehr gesetzt hat!
1340private void LiquidateEK(int bit) {
1341
1342 if (!intp(bit) || bit < 0) return;
1343
1344 string obname = by_num[bit, BYNUM_KEY];
1345 int score = by_num[bit, BYNUM_SCORE];
1346
1347 if (member(npcs, obname) && (npcs[obname, NPC_NUMBER] == bit)) {
1348 m_delete(by_num, bit);
1349 m_delete(npcs, obname);
1350 if (member(unconfirmed_scores,bit))
1351 m_delete(unconfirmed_scores,bit);
1352 active_eks = clear_bit(active_eks,bit);
1353 removeTip(obname);
1354 free_num += ({bit});
1355 write_file(SCOREAUTOLOG,sprintf(
1356 "LIQUIDATEEK: %s %5d Score %3d %s\n",
1357 strftime("%d%m%Y-%T",time()), bit, score, obname));
1358 }
1359}
1360
1361private void check_player(string pl) {
1362 int changed, changed2;
1363
1364 // EKs pruefen
1365 string eks=(string)master()->query_ek(pl) || "";
1366 string *opfer=allocate( (sizeof(eks)*6)+1, "");
1367 int p=-1;
1368 while ((p=next_bit(eks,p)) != -1) {
1369 if (!member(by_num, p)) {
1370 write_file(SCORECHECKFILE, sprintf(
1371 "UNKNOWNEK %s %5d in %s gefunden.\n",
1372 strftime("%d%m%Y-%T",time()), p, pl));
1373 }
1374 // wenn das aktuelle Bit geloescht werden soll, also in to_be_removed
1375 // steht...
1376 if (member(to_be_removed,p) != -1) {
1377 eks = clear_bit(eks,p);
1378 changed=1;
1379 write_file(EKCLEANLOG,sprintf(
1380 "CLEARBIT: %s %O %5d %s\n",
1381 strftime("%d%m%Y-%T",time()), pl, p,
1382 by_num[p,BYNUM_KEY] || "NoName"));
1383 }
1384 else {
1385 // sonst statistikpflege
1386 npcs[by_num[p,BYNUM_KEY],NPC_COUNT]++;
1387 // loggen, welche NPC der Spieler hat
1388 opfer[p]=to_string(p);
1389 }
1390 }
1391 // und noch die Ek-Tips...
1392 string ektips = (string)master()->query_ektips(pl) || "";
1393 p = -1;
1394 while ((p=next_bit(ektips,p)) != -1) {
1395 if (!member(by_num, p)) {
1396 write_file(EKCLEANLOG, sprintf(
1397 "UNKNOWNEK %s %5d in %s (EKTips) gefunden - clearing.\n",
1398 strftime("%d%m%Y-%T",time()), p, pl));
1399 ektips = clear_bit(ektips, p); // hier direkt loeschen.
1400 changed2 = 1;
1401 }
1402 // wenn das aktuelle Bit geloescht werden soll, also in to_be_removed
1403 // steht...
1404 else if (member(to_be_removed,p) != -1) {
1405 ektips = clear_bit(ektips,p);
1406 changed2=1;
1407 write_file(EKCLEANLOG,sprintf(
1408 "CLEAREKTIP: %s %O %5d %s\n",
1409 strftime("%d%m%Y-%T",time()), pl, p,
1410 by_num[p,BYNUM_KEY] || "NoName"));
1411 }
1412 }
1413
1414 if (changed) {
1415 master()->update_ek(pl, eks);
1416 }
1417 if (changed2) {
1418 master()->update_ektips(pl, ektips);
1419 }
1420 opfer-=({""});
1421 write_file(WERKILLTWEN,sprintf("%s\n%=-78s\n\n",pl,CountUp(opfer)||""));
1422}
1423
1424public void check_all_player(mapping allplayer) {
1425
1426 if (extern_call() && !allowed())
1427 return;
1428
1429 if (!mappingp(allplayer)) {
1430 foreach(string key: npcs) {
1431 npcs[key,NPC_COUNT]=0;
1432 }
1433 allplayer=(mapping)master()->get_all_players();
1434 rm(WERKILLTWEN);
1435 call_out(#'check_all_player,2,allplayer);
1436 return;
1437 }
1438
1439 // offenbar fertig mit allen Spielern, jetzt noch den Rest erledigen.
1440 if (!sizeof(allplayer)) {
1441 foreach(int bit: to_be_removed) {
1442 LiquidateEK(bit);
1443 }
1444 to_be_removed=({});
1445 save_object(SCORESAVEFILE);
1446 ZDEBUG("Spielerchecks und EK-Liquidation fertig.\n");
1447 return;
1448 }
1449
1450 string dir=m_indices(allplayer)[0];
1451 string *pls=allplayer[dir];
1452 foreach(string pl: pls) {
1453 if (get_eval_cost() < 1250000)
1454 break; // spaeter weitermachen.
1455 catch(check_player(pl) ; publish);
1456 pls-=({pl});
1457 }
1458 allplayer[dir] = pls;
1459
1460 if (!sizeof(allplayer[dir]))
1461 m_delete(allplayer,dir);
1462
1463 call_out(#'check_all_player,2,allplayer);
1464}
1465