blob: 83dc4665536a7c9d3878608f00c74244b7a9a713 [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 {
bugfixd94d0932020-04-08 11:27:13 +0200229 ek = (({string})MASTER->query_ek(who=whop[i]) || "");
MG Mud User88f12472016-06-24 23:31:02 +0200230 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 }
bugfixd94d0932020-04-08 11:27:13 +0200248 ({int})MASTER->update_ek(who, ek);
MG Mud User88f12472016-06-24 23:31:02 +0200249
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]) {
Vanion50652322020-03-10 21:13:25 +0100400 string eks = ({string})master()->query_ek(pl);
MG Mud User88f12472016-06-24 23:31:02 +0200401 eks = set_bit(eks, bit);
bugfixd94d0932020-04-08 11:27:13 +0200402 ({int})master()->update_ek(pl, eks);
MG Mud User88f12472016-06-24 23:31:02 +0200403 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);
Zesstra41f0b4c2021-05-07 09:35:40 +0200654 if (member(npcs, oldkey))
655 num=npcs[oldkey,NPC_NUMBER];
656 else
657 return SCORE_INVALID_ARG;
MG Mud User88f12472016-06-24 23:31:02 +0200658 }
659 else if (intp(oldkey)) num=oldkey;
660 else return SCORE_INVALID_ARG;
661
662 if (!member(by_num,num)) return SCORE_INVALID_ARG;
663
664 tip=getTipFromList(oldkey);
665 oldpath = by_num[num, BYNUM_KEY];
666 score = by_num[num, BYNUM_SCORE];
667
668 if (member(npcs, oldpath)) {
669 m_delete(npcs, oldpath);
670 removeTip(oldkey);
671 if(tip!="") addTip(newpath,tip);
672 npcs[newpath, NPC_SCORE] = score;
673 npcs[newpath, NPC_NUMBER] = num;
674 }
675 else return SCORE_INVALID_ARG;
676
677 by_num += ([num: newpath; score]);
678
679 ClearUsersEKCache();
680 save_object(SCORESAVEFILE);
681 write_file(SCORELOGFILE,sprintf("MOVESCORE: %s %s %s (%s, %O)\n",
682 strftime("%d%m%Y-%T",time()),oldpath,newpath,
683 geteuid(previous_object()),this_interactive()));
684
685 while(remove_call_out("DumpNPCs") != -1) ;
686 call_out("DumpNPCs",60);
687 return 1;
688}
689
690// liefert alle Kills des Spielers zurueck, auch solche, die momentan
691// ausgetragen/deaktiviet sind.
692public string QueryAllKills(string pl)
693{
bugfixd94d0932020-04-08 11:27:13 +0200694 return (({string})MASTER->query_ek(pl) || "");
MG Mud User88f12472016-06-24 23:31:02 +0200695}
696
697// filtert alle Eintraege aus dem Bitstring heraus, die fuer
698// ausgetragene/inaktive EKs stehen.
699public string QueryKills(string pl) {
Arathornd5c9c022020-01-08 22:04:28 +0100700 string res = ({string})MASTER->query_ek(pl) || "";
MG Mud User88f12472016-06-24 23:31:02 +0200701 // vergleichen mit den aktiven EKs aus active_eks und nur jene Bits
702 // zurueckliefern, die in beiden Strings gesetzt sind.
703 return and_bits(res,active_eks);
704}
705
706public int QueryKillPoints(mixed pl) {
707
708 if (!allowed() &&
709 (!previous_object()
710 || strstr(object_name(previous_object()), "/gilden/") != 0) )
711 return 0;
712
713 if (!stringp(pl)) pl=getuid(pl);
714
715 if (member(users_ek,pl)) return users_ek[pl];
716
bugfixd94d0932020-04-08 11:27:13 +0200717 string s = (({string})MASTER->query_ek(pl) || "");
MG Mud User88f12472016-06-24 23:31:02 +0200718
719 int p=-1;
720 int summe;
721 while ((p=next_bit(s,p)) != -1) {
722 summe+=by_num[p,BYNUM_SCORE];
723 }
724
725 users_ek += ([pl:summe]);
726 return summe;
727}
728
729public mixed *QueryNPCbyNumber(int num)
730{
731 if (!allowed())
732 return 0;
733
734 if (member(by_num, num))
735 return ({num, by_num[num, BYNUM_SCORE], by_num[num, BYNUM_KEY]});
736
737 return 0;
738}
739
740protected mixed *StaticQueryNPCbyNumber(int num)
741{
742 if (member(by_num, num))
743 return ({num, by_num[num, BYNUM_SCORE], by_num[num, BYNUM_KEY]});
744
745 return 0;
746}
747
748public mixed *QueryNPCbyObject(object o)
749{
750 string key;
751 int val;
752
753 key=load_name(o);
754 if (member(npcs,key)) {
755 val = npcs[key,NPC_NUMBER];
756 return ({val, by_num[val, BYNUM_SCORE], by_num[val, BYNUM_KEY]});
757 }
758 return 0;
759}
760
761public int GiveKill(object pl, int bit)
762{
763 mixed info;
764 object po;
765 int drin;
766 string pls, ek;
767
768
769 if (!pointerp(info = StaticQueryNPCbyNumber(bit)))
770 return -1;
771
772 if ((!po=previous_object())
773 || load_name(po) != info[SCORE_KEY])
774 return -2;
775
776 pls=getuid(pl);
777
778 // wenn unbestaetigt, Spieler fuer spaeter merken
779 if (member(unconfirmed_scores, bit)) {
780 if (member(unconfirmed_scores[bit], pls) == -1)
781 unconfirmed_scores[bit] += ({pls});
782 }
783 else {
784 // sonst wird das Bit direkt im Spieler gesetzt.
bugfixd94d0932020-04-08 11:27:13 +0200785 ek = (({string})MASTER->query_ek(pls) || "");
MG Mud User88f12472016-06-24 23:31:02 +0200786 if (test_bit(ek, bit))
787 return -3;
788 ek = set_bit(ek, bit);
bugfixd94d0932020-04-08 11:27:13 +0200789 ({int})MASTER->update_ek(pls, ek);
MG Mud User88f12472016-06-24 23:31:02 +0200790 // Vergabestatistik hochzaehlen.
791 npcs[by_num[bit,BYNUM_KEY],NPC_COUNT]++;
792 }
793
794 if (member(users_ek, pls))
795 m_delete(users_ek, pls);
796
797 EK_GIVENLOG(sprintf("%s: %s", info[SCORE_KEY], pls));
798
799 return info[SCORE_SCORE];
800}
801
802public int HasKill(mixed pl, mixed npc)
803{
804 string fn, *pls;
805
806 if (!objectp(pl) && !stringp(pl) &&
807 !objectp(npc) && !stringp(npc) && !intp(npc))
808 return 0;
809 if (!stringp(pl))
810 pl=getuid(pl);
811
812 if (intp(npc))
813 npc=by_num[npc,BYNUM_KEY];
814 fn=load_name(npc);
815
816 if (!member(npcs, fn)) return 0;
817
818 int bit = npcs[fn, NPC_NUMBER];
819
820 if (pointerp(pls=unconfirmed_scores[bit]) &&
821 member(pls,pl) != -1)
822 return 1;
823
bugfixd94d0932020-04-08 11:27:13 +0200824 string eks = (({string})MASTER->query_ek(pl) || "");
MG Mud User88f12472016-06-24 23:31:02 +0200825
826 return test_bit(eks, bit);
827}
828
829private void WriteDumpFile(string *keys) {
830 int maxn;
831
832 if (!pointerp(keys)) return;
833
834 rm(SCOREDUMPFILE);
835
836 write_file(SCOREDUMPFILE,sprintf("%5s %5s %4s %s\n",
837 "Nr.", "Cnt", "Sc", "Objekt"));
838 foreach(string key: keys) {
839 write_file(SCOREDUMPFILE,sprintf("%5d %5d %4d %O\n",
840 npcs[key,NPC_NUMBER], npcs[key,NPC_COUNT],
841 npcs[key,NPC_SCORE], key));
842 maxn += npcs[key,NPC_SCORE];
843 }
844 write_file(SCOREDUMPFILE,sprintf(
845 "========================================================\n"
846 "NPCs gesamt: %d Punkte\n\n",maxn));
847}
848
849public varargs int DumpNPCs(int sortkey) {
850
851 if (extern_call() && !allowed()) return SCORE_NO_PERMISSION;
852 if (!intp(sortkey)) return SCORE_INVALID_ARG;
853
854 rm(SCOREDUMPFILE);
855
856 // sortieren
857 string *keys=sort_array(m_indices(npcs), function int (string a, string b) {
858 return(npcs[a,sortkey] < npcs[b,sortkey]); } );
859 call_out(#'WriteDumpFile, 2, keys);
860
861 return 1;
862}
863
864public int SetScoreBit(string pl, int bit)
865{
866 string ek;
867
868 if (!allowed())
869 return SCORE_NO_PERMISSION;
870
bugfixd94d0932020-04-08 11:27:13 +0200871 ek = (({string})MASTER->query_ek(pl) || "");
MG Mud User88f12472016-06-24 23:31:02 +0200872 ek = set_bit(ek, bit);
bugfixd94d0932020-04-08 11:27:13 +0200873 ({int})MASTER->update_ek(pl, ek);
MG Mud User88f12472016-06-24 23:31:02 +0200874
875 // Vergabestatistik hochzaehlen.
876 npcs[by_num[bit,BYNUM_KEY],NPC_COUNT]++;
877
878 if (member(users_ek, pl))
879 m_delete(users_ek, pl);
880
881 write_file(SCORELOGFILE,sprintf("SETBIT: %s %s %5d Sc: %.3d %s (%s, %O)\n",
882 strftime("%d%m%Y-%T",time()),pl, bit,
883 by_num[bit,BYNUM_SCORE], by_num[bit,BYNUM_KEY],
884 geteuid(previous_object()), this_interactive()));
885 return 1;
886}
887
888public int ClearScoreBit(string pl, int bit)
889{
890 string ek;
891
892 if (!allowed())
893 return SCORE_NO_PERMISSION;
894
bugfixd94d0932020-04-08 11:27:13 +0200895 ek = (({string})MASTER->query_ek(pl) || "");
MG Mud User88f12472016-06-24 23:31:02 +0200896 ek = clear_bit(ek, bit);
bugfixd94d0932020-04-08 11:27:13 +0200897 ({int})MASTER->update_ek(pl, ek);
MG Mud User88f12472016-06-24 23:31:02 +0200898
899 // Vergabestatistik runterzaehlen.
900 npcs[by_num[bit,BYNUM_KEY],NPC_COUNT]--;
901
902 if (member(users_ek, pl))
903 m_delete(users_ek, pl);
904
905 write_file(SCORELOGFILE,sprintf(
906 "CLEARBIT: %s %s %5d Sc: %.3d %s (%s, %O)\n",
907 strftime("%d%m%Y-%T",time()),pl,bit,
908 by_num[bit,BYNUM_SCORE],by_num[bit,BYNUM_KEY],
909 geteuid(previous_object()), this_interactive()));
910 return 1;
911}
912
913private status ektipAllowed()
914{
915 status poOK;
916 string poName;
917 status ret;
918
919 poName=load_name(previous_object());
920 poOK=previous_object() &&
921 ((previous_object()==find_object(EKTIPGIVER)) || (poName==EKTIPLIST) );
922
923 ret=allowed() ||
924 (this_player() && this_interactive() && previous_object() &&
925 this_interactive()==this_player() && poOK);
926 return ret;
927}
928
929// liefert alle EKs, die aktiv sind und die der Spieler noch nicht hat in
930// einem Mapping entsprechend npcs zurueck.
931public mapping getFreeEKsForPlayer(object player)
932{
933 if(!ektipAllowed() || !objectp(player) || !query_once_interactive(player)){
934 return ([]);
935 }
936 // alle EKs, die der Spieler hat
Arathornd5c9c022020-01-08 22:04:28 +0100937 string eks = ({string})master()->query_ek(getuid(player));
MG Mud User88f12472016-06-24 23:31:02 +0200938 // als Tips kommen alle in Frage, die er nicht hat, vor dem Invertieren muss
939 // aber sichergestellt werden, dass eks min. so lang ist wie active_eks, da
940 // die Invertierung ja z.B. nur EKs 0-1700 beruecksichtigt, wenn 1700 der
941 // hoechste EK im Spieler ist und dann als Tips alle EKs ueber
942 // 1700 verlorengingen.
943 // hier wird das letzte Bit von active_eks ermittelt und das darauf folgende
944 // Bit im Spieler-EK-String gesetzt und wieder geloescht, woraufhin der
945 // EK-String min. so lang wie active_eks ist. (es ist egal, wenn er
946 // laenger ist, auch egal, wenn man ein Bit ueberschreibt, das faellt alles
947 // gleich beim and_bits() raus.
948 int lb = last_bit(active_eks) + 1;
949 eks = clear_bit(set_bit(eks, lb), lb);
950 // jetzt invertieren
951 string non_eks = invert_bits(eks);
952 // jetzt vorhande EK-Tips ausfiltern. Im Prinzip gleiches Spiel wie oben.
Arathornd5c9c022020-01-08 22:04:28 +0100953 string ektips = ({string})master()->query_ektips(getuid(player));
MG Mud User88f12472016-06-24 23:31:02 +0200954 // jetzt alle nicht als Tip vergebenen NPC ermitteln, vor dem Invertieren
955 // wieder Laenge angleichen...
956 ektips = invert_bits(clear_bit(set_bit(ektips, lb), lb));
957 // verunden liefert EKs, die der Spieler nicht hat und nicht als Tip hat
958 ektips = and_bits(ektips, non_eks);
959
960 // und nun die inaktive EKs ausfiltern, nochmal verunden
961 ektips = and_bits(ektips, active_eks);
962
963 // mal Platz reservieren, entsprechend der Menge an Bits
964 mapping freeKills = m_allocate( count_bits(ektips), 2);
965 // durch alle jetzt gesetzten Bits laufen, d.h. alle EKs, die der Spieler
966 // nicht hat und ein Mapping a la npcs erstellen.
967 int p=-1;
968 while ( (p=next_bit(ektips, p)) != -1) {
969 freeKills += ([ by_num[p,0]: p; by_num[p,1] ]);
970 }
971
972 return freeKills;
973}
974
975public int addTip(mixed key,string tip)
976{
977 string fn;
978
979 if (!allowed())
980 return SCORE_NO_PERMISSION;
981
982 if (!tip || (!objectp(key) && !stringp(key)))
983 return SCORE_INVALID_ARG;
984
985 fn=load_name(key);
986
987 if (!member(npcs, fn)) return SCORE_INVALID_ARG;
988 tipList+=([fn:tip]);
989 save_object(SCORESAVEFILE);
990
991 return 1;
992}
993
994public int changeTip(mixed key,string tip)
995{
996 return addTip(key,tip);
997}
998
999public int removeTip(mixed key)
1000{
1001 string fn;
1002
1003 if (!allowed())
1004 return SCORE_NO_PERMISSION;
1005
1006 if ((!objectp(key) && !stringp(key)))
1007 return SCORE_INVALID_ARG;
1008
1009 fn=load_name(key);
1010
1011 if (!member(tipList, fn)) return SCORE_INVALID_ARG;
1012
1013 m_delete(tipList,fn);
1014 save_object(SCORESAVEFILE);
1015
1016 return 1;
1017}
1018
1019private string getTipFromList(mixed key)
1020{
1021 string fn;
1022
1023 if (!ektipAllowed())
1024 return "";
1025
1026 if ((!objectp(key) && !stringp(key)))
1027 return "";
1028
1029 fn=load_name(key);
1030
1031 if (!member(tipList, fn)) return "";
1032
1033 return tipList[fn];
1034}
1035
1036private string _getTip(mixed key)
1037{
1038 string fn;
1039 string tip;
1040 string* path;
1041
1042 if ((!objectp(key) && !stringp(key)))
1043 return "";
1044
1045 fn=load_name(key);
1046
1047 if(!member(npcs,fn)) return "";
1048
1049 tip=getTipFromList(fn);
1050 if(!tip || tip==""){
1051 path=explode(fn,"/")-({""});
1052 if(sizeof(path)<3) return "";
1053 if(path[0]=="players") {
1054 string tiptext;
1055 if ( path[1] == "ketos" )
1056 tiptext = "Ketos im Gebirge";
1057 else if ( path[1] == "boing" && path[2] == "friedhof" )
1058 tiptext = "Boing im eisigen Polar";
1059 else
1060 tiptext = capitalize(path[1]);
1061 return "Schau Dich doch mal bei "+tiptext+" um.";
1062 }
1063
1064 if(path[0]=="d")
1065 {
1066 tip+="Schau Dich doch mal ";
1067
1068 if(file_size("/players/"+path[2])==-2)
1069 {
1070 tip+="bei "+capitalize(path[2]+" ");
1071 }
1072 if ( path[1]=="polar" && file_size("/players/"+path[3])==-2 )
1073 {
1074 tip+="bei "+capitalize(path[3])+" ";
1075 }
1076
1077 if(path[1]=="anfaenger")
1078 tip+="in den Anfaengergebieten ";
1079 if(path[1]=="fernwest")
1080 tip+="in Fernwest ";
1081 if(path[1]=="dschungel")
1082 tip+="im Dschungel ";
1083 if(path[1]=="schattenwelt")
1084 tip+="in der Welt der Schatten ";
1085 if(path[1]=="unterwelt")
1086 tip+="in der Unterwelt ";
1087 if(path[1]=="gebirge")
1088 tip+="im Gebirge ";
1089 if(path[1]=="seher")
1090 tip+="bei den Sehergebieten ";
1091 if(path[1]=="vland")
1092 tip+="auf dem Verlorenen Land ";
1093 if(path[1]=="ebene")
1094 tip+="in der Ebene ";
1095 if(path[1]=="inseln")
1096 tip+="auf den Inseln ";
1097 if(path[1]=="wald")
1098 tip+="im Wald ";
1099 if(path[1]=="erzmagier")
1100 tip+="bei den Erzmagiern ";
1101 if(path[1]=="polar")
1102 {
1103 if (path[2]=="files.chaos")
1104 tip+="in den Raeumen der Chaosgilde ";
1105 tip+="im eisigen Polar ";
1106 }
1107 if(path[1]=="wueste")
1108 tip+="in der Wueste ";
1109 tip+="um.";
1110 }
1111 else if ( path[0]=="gilden" )
1112 {
1113 tip+="Schau Dich doch mal";
1114 switch( path[1] )
1115 {
1116 case "mon.elementar":
1117 tip+=" unter den Anfuehrern der Elementargilde";
1118 break;
1119 case "files.dunkelelfen":
1120 tip+=" unter den Anfuehrern der Dunkelelfengilde";
1121 break;
1122 case "files.klerus":
1123 tip+=" beim Klerus";
1124 break;
1125 case "files.werwoelfe":
1126 tip+=" unter den Anfuehrern der Werwoelfe";
1127 break;
1128 case "files.chaos":
1129 tip+=" unter den Anfuehrern der Chaosgilde";
1130 break;
1131 default:
1132 tip+=" in einer der Gilden";
1133 break;
1134 }
1135 tip+=" um.";
1136 }
1137 else if ( path[0] == "p" )
1138 {
1139 tip+="Schau Dich doch mal ";
1140 switch( path[1] )
1141 {
1142 case "zauberer":
1143 tip+="in der Zauberergilde zu Taramis";
1144 break;
1145 case "kaempfer":
1146 tip+="bei den Angehoerigen des Koru-Tschakar-Struvs";
1147 break;
1148 case "katzenkrieger":
1149 tip+="bei der Gilde der Katzenkrieger";
1150 break;
1151 case "tanjian":
1152 tip+="unter den Meistern der Tanjiangilde";
1153 break;
1154 }
1155 tip+=" um.";
1156 }
1157 }
1158 return tip;
1159}
1160
1161// return valid tips from database or existing
1162public string getTip(mixed key)
1163{
1164 string fn;
1165 string tip;
1166 string* path;
1167
1168 if (!ektipAllowed())
1169 return "";
1170
1171 return _getTip(key);
1172}
1173
1174// liefert ein Array mit allen Objekten zurueck, auf die bitstr verweist, also
1175// eine Liste aller Objekte, die als Tip vergeben wurden.
1176private string* makeTiplistFromBitString(string bitstr)
1177{
1178 string * ret= allocate(count_bits(bitstr));
1179 // ueber alle gesetzten bits laufen und Array zusammensammeln
1180 int i;
1181 int p=-1;
1182 while ((p=next_bit(bitstr,p)) != -1) {
1183 ret[i] = by_num[p, 0];
1184 i++;
1185 }
1186 // zur Sicherheit
1187 ret -= ({0});
1188
1189 return ret;
1190}
1191
1192// gibt die Objektnamen der EK-Tips vom jeweiligen Spieler zurueck.
1193public string *QueryTipObjects(mixed player) {
1194
1195 if (extern_call() && !allowed())
1196 return 0;
1197 if (objectp(player) && query_once_interactive(player))
1198 player=getuid(player);
1199 if (!stringp(player))
1200 return 0;
1201
Arathornd5c9c022020-01-08 22:04:28 +01001202 string tipstr = ({string})master()->query_ektips(player);
MG Mud User88f12472016-06-24 23:31:02 +02001203 // jetzt EK-Tips ausfiltern, die erledigt sind. Dazu EK-Liste holen...
Arathornd5c9c022020-01-08 22:04:28 +01001204 string eks = ({string})master()->query_ek(player);
MG Mud User88f12472016-06-24 23:31:02 +02001205 // als Tips kommen alle in Frage, die er nicht hat, vor dem Invertieren muss
1206 // aber sichergestellt werden, dass eks min. so lang ist wie tipstr, da
1207 // die Invertierung ja z.B. nur EKs 0-1700 beruecksichtigt, wenn 1700 der
1208 // hoechste EK im Spieler ist und dann alle Tips ueber
1209 // 1700 verlorengingen.
1210 // hier wird das letzte Bit von tipstr ermittelt und das darauf folgende
1211 // Bit im Spieler-EK-String gesetzt und wieder geloescht, woraufhin der
1212 // EK-String min. so lang wie der Tipstring ist. (es ist egal, wenn er
1213 // laenger ist, auch egal, wenn man ein Bit ueberschreibt, das faellt alles
1214 // gleich beim and_bits() raus.
1215 int lb = last_bit(tipstr) + 1;
1216 eks = clear_bit(set_bit(eks, lb), lb);
1217 // jetzt invertieren
1218 string non_eks = invert_bits(eks);
1219 // jetzt verunden und man hat die Tips, die noch nicht gehauen wurden.
1220 tipstr = and_bits(tipstr, non_eks);
1221 // noch inaktive EKs ausfiltern...
1222 tipstr = and_bits(tipstr, active_eks);
1223
1224 return makeTiplistFromBitString(tipstr);
1225}
1226
1227public string allTipsForPlayer(object player)
1228{
1229
1230 if(!player || !this_interactive() ||
1231 (this_interactive()!=player && !IS_ARCH(this_interactive())) )
1232 return "";
1233
1234 string *tips = QueryTipObjects(player);
1235
1236 tips = map(tips, #'_getTip);
1237 tips -= ({0,""});
1238
1239 return implode(tips, "\n");
1240}
1241
1242public status playerMayGetTip(object player)
1243{
1244 int numElegible;
1245 int numReceived;
1246 int lvl;
1247 int i;
1248 string tips;
1249
1250 if(!ektipAllowed() || !player || !query_once_interactive(player))
1251 return 0;
1252
1253 if(!player || !query_once_interactive(player))
1254 return 0;
1255
Arathornd5c9c022020-01-08 22:04:28 +01001256 lvl = ({int})player->QueryProp(P_LEVEL);
MG Mud User88f12472016-06-24 23:31:02 +02001257 numElegible=0;
1258 i=sizeof(EKTIPS_LEVEL_LIMITS)-1;
1259
1260 if(lvl>EKTIPS_LEVEL_LIMITS[i])
1261 numElegible+=(lvl-EKTIPS_LEVEL_LIMITS[i]);
1262
1263 for(i;i>=0;i--){
1264 if(lvl>=EKTIPS_LEVEL_LIMITS[i]) numElegible++;
1265 }
1266
Arathornd5c9c022020-01-08 22:04:28 +01001267 tips = ({string})MASTER->query_ektips(getuid(player)) || "";
MG Mud User88f12472016-06-24 23:31:02 +02001268 // inaktive EKs ausfiltern.
1269 tips = and_bits(tips, active_eks);
1270 // und Gesamtzahl an Tips zaehlen. Hier werden erledigte Tips explizit nicht
1271 // ausgefiltert!
1272 numReceived=count_bits(tips);
1273
1274 return numElegible>numReceived;
1275}
1276
1277public string giveTipForPlayer(object player)
1278{
1279 string* tmp;
1280 mapping free;
1281 string tip,pl,ektip;
1282 int index;
1283
1284 if(!ektipAllowed() || !player ||
1285 !query_once_interactive(player) || !playerMayGetTip(player))
1286 return "";
1287
1288 pl=getuid(player);
1289 free=getFreeEKsForPlayer(player);
1290
1291 if(!mappingp(free) || sizeof(free)==0)
1292 return "";
1293
1294 tmp=m_indices(free);
1295
Arathornd5c9c022020-01-08 22:04:28 +01001296 ektip = ({string})MASTER->query_ektips(pl) || "";
MG Mud User88f12472016-06-24 23:31:02 +02001297
1298 foreach(int i: EKTIPS_MAX_RETRY) {
1299 index=random(sizeof(tmp));
1300 tip=getTip(tmp[index]);
1301 if (stringp(tip) && sizeof(tip)) {
1302 ektip=set_bit(ektip,npcs[tmp[index],NPC_NUMBER]);
bugfixd94d0932020-04-08 11:27:13 +02001303 ({int})MASTER->update_ektips(pl,ektip);
MG Mud User88f12472016-06-24 23:31:02 +02001304 break; //fertig
1305 }
1306 }
1307
1308 return tip;
1309}
1310
1311// checkt NPCs auf Existenz und Ladbarkeit
1312public void CheckNPCs(int num) {
1313 string fn;
1314 object ekob;
1315 if (!num) rm(SCORECHECKFILE);
1316 while (num <= lastNum && get_eval_cost() > 1480000) {
1317 if (!by_num[num,1] || !by_num[num,0]) {
1318 num++;
1319 continue;
1320 }
1321 fn = by_num[num,0] + ".c";
1322 if (file_size(fn) <= 0) {
1323 // File nicht existent
1324 write_file(SCORECHECKFILE, sprintf(
1325 "EK %.4d ist nicht existent (%s)\n",num,fn));
1326 }
1327 else if (catch(ekob=load_object(fn)) || !objectp(ekob) ) {
1328 // NPC offenbar nicht ladbar.
1329 write_file(SCORECHECKFILE, sprintf(
1330 "EK %.4d ist nicht ladbar (%s)\n",num,fn));
1331 }
1332 num++;
1333 }
1334 ZDEBUG(sprintf("%d NPC checked",num));
1335 if (num <= lastNum)
1336 call_out(#'CheckNPCs,4,num);
1337 else
1338 ZDEBUG("Finished!");
1339}
1340
1341// liquidiert einen EK endgueltig. An diesem Punkt wird davon augegangen, dass
1342// kein Spieler den EK mehr gesetzt hat!
1343private void LiquidateEK(int bit) {
1344
1345 if (!intp(bit) || bit < 0) return;
1346
1347 string obname = by_num[bit, BYNUM_KEY];
1348 int score = by_num[bit, BYNUM_SCORE];
1349
1350 if (member(npcs, obname) && (npcs[obname, NPC_NUMBER] == bit)) {
1351 m_delete(by_num, bit);
1352 m_delete(npcs, obname);
1353 if (member(unconfirmed_scores,bit))
1354 m_delete(unconfirmed_scores,bit);
1355 active_eks = clear_bit(active_eks,bit);
1356 removeTip(obname);
1357 free_num += ({bit});
1358 write_file(SCOREAUTOLOG,sprintf(
1359 "LIQUIDATEEK: %s %5d Score %3d %s\n",
1360 strftime("%d%m%Y-%T",time()), bit, score, obname));
1361 }
1362}
1363
1364private void check_player(string pl) {
1365 int changed, changed2;
1366
1367 // EKs pruefen
Arathornd5c9c022020-01-08 22:04:28 +01001368 string eks = ({string})master()->query_ek(pl) || "";
MG Mud User88f12472016-06-24 23:31:02 +02001369 string *opfer=allocate( (sizeof(eks)*6)+1, "");
1370 int p=-1;
1371 while ((p=next_bit(eks,p)) != -1) {
1372 if (!member(by_num, p)) {
1373 write_file(SCORECHECKFILE, sprintf(
1374 "UNKNOWNEK %s %5d in %s gefunden.\n",
1375 strftime("%d%m%Y-%T",time()), p, pl));
1376 }
1377 // wenn das aktuelle Bit geloescht werden soll, also in to_be_removed
1378 // steht...
1379 if (member(to_be_removed,p) != -1) {
1380 eks = clear_bit(eks,p);
1381 changed=1;
1382 write_file(EKCLEANLOG,sprintf(
1383 "CLEARBIT: %s %O %5d %s\n",
1384 strftime("%d%m%Y-%T",time()), pl, p,
1385 by_num[p,BYNUM_KEY] || "NoName"));
1386 }
1387 else {
1388 // sonst statistikpflege
1389 npcs[by_num[p,BYNUM_KEY],NPC_COUNT]++;
1390 // loggen, welche NPC der Spieler hat
1391 opfer[p]=to_string(p);
1392 }
1393 }
1394 // und noch die Ek-Tips...
Arathornd5c9c022020-01-08 22:04:28 +01001395 string ektips = ({string})master()->query_ektips(pl) || "";
MG Mud User88f12472016-06-24 23:31:02 +02001396 p = -1;
1397 while ((p=next_bit(ektips,p)) != -1) {
1398 if (!member(by_num, p)) {
1399 write_file(EKCLEANLOG, sprintf(
1400 "UNKNOWNEK %s %5d in %s (EKTips) gefunden - clearing.\n",
1401 strftime("%d%m%Y-%T",time()), p, pl));
1402 ektips = clear_bit(ektips, p); // hier direkt loeschen.
1403 changed2 = 1;
1404 }
1405 // wenn das aktuelle Bit geloescht werden soll, also in to_be_removed
1406 // steht...
1407 else if (member(to_be_removed,p) != -1) {
1408 ektips = clear_bit(ektips,p);
1409 changed2=1;
1410 write_file(EKCLEANLOG,sprintf(
1411 "CLEAREKTIP: %s %O %5d %s\n",
1412 strftime("%d%m%Y-%T",time()), pl, p,
1413 by_num[p,BYNUM_KEY] || "NoName"));
1414 }
1415 }
1416
1417 if (changed) {
bugfixd94d0932020-04-08 11:27:13 +02001418 ({int})master()->update_ek(pl, eks);
MG Mud User88f12472016-06-24 23:31:02 +02001419 }
1420 if (changed2) {
bugfixd94d0932020-04-08 11:27:13 +02001421 ({int})master()->update_ektips(pl, ektips);
MG Mud User88f12472016-06-24 23:31:02 +02001422 }
1423 opfer-=({""});
1424 write_file(WERKILLTWEN,sprintf("%s\n%=-78s\n\n",pl,CountUp(opfer)||""));
1425}
1426
1427public void check_all_player(mapping allplayer) {
1428
1429 if (extern_call() && !allowed())
1430 return;
1431
1432 if (!mappingp(allplayer)) {
1433 foreach(string key: npcs) {
Arathornd5c9c022020-01-08 22:04:28 +01001434 npcs[key,NPC_COUNT]=0;
MG Mud User88f12472016-06-24 23:31:02 +02001435 }
Arathornd5c9c022020-01-08 22:04:28 +01001436 allplayer = ({mapping})master()->get_all_players();
MG Mud User88f12472016-06-24 23:31:02 +02001437 rm(WERKILLTWEN);
1438 call_out(#'check_all_player,2,allplayer);
1439 return;
1440 }
1441
1442 // offenbar fertig mit allen Spielern, jetzt noch den Rest erledigen.
1443 if (!sizeof(allplayer)) {
1444 foreach(int bit: to_be_removed) {
1445 LiquidateEK(bit);
1446 }
1447 to_be_removed=({});
1448 save_object(SCORESAVEFILE);
1449 ZDEBUG("Spielerchecks und EK-Liquidation fertig.\n");
1450 return;
1451 }
1452
1453 string dir=m_indices(allplayer)[0];
1454 string *pls=allplayer[dir];
1455 foreach(string pl: pls) {
1456 if (get_eval_cost() < 1250000)
1457 break; // spaeter weitermachen.
1458 catch(check_player(pl) ; publish);
1459 pls-=({pl});
1460 }
1461 allplayer[dir] = pls;
1462
1463 if (!sizeof(allplayer[dir]))
1464 m_delete(allplayer,dir);
1465
1466 call_out(#'check_all_player,2,allplayer);
1467}
1468