blob: 004e3417970747a6be9afa013cfddc6e6c5f2d5b [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;
MG Mud User88f12472016-06-24 23:31:02 +0200765 string pls, ek;
766
767
768 if (!pointerp(info = StaticQueryNPCbyNumber(bit)))
769 return -1;
770
771 if ((!po=previous_object())
772 || load_name(po) != info[SCORE_KEY])
773 return -2;
774
775 pls=getuid(pl);
776
777 // wenn unbestaetigt, Spieler fuer spaeter merken
778 if (member(unconfirmed_scores, bit)) {
779 if (member(unconfirmed_scores[bit], pls) == -1)
780 unconfirmed_scores[bit] += ({pls});
781 }
782 else {
783 // sonst wird das Bit direkt im Spieler gesetzt.
bugfixd94d0932020-04-08 11:27:13 +0200784 ek = (({string})MASTER->query_ek(pls) || "");
MG Mud User88f12472016-06-24 23:31:02 +0200785 if (test_bit(ek, bit))
786 return -3;
787 ek = set_bit(ek, bit);
bugfixd94d0932020-04-08 11:27:13 +0200788 ({int})MASTER->update_ek(pls, ek);
MG Mud User88f12472016-06-24 23:31:02 +0200789 // Vergabestatistik hochzaehlen.
790 npcs[by_num[bit,BYNUM_KEY],NPC_COUNT]++;
791 }
792
793 if (member(users_ek, pls))
794 m_delete(users_ek, pls);
795
796 EK_GIVENLOG(sprintf("%s: %s", info[SCORE_KEY], pls));
797
798 return info[SCORE_SCORE];
799}
800
801public int HasKill(mixed pl, mixed npc)
802{
803 string fn, *pls;
804
805 if (!objectp(pl) && !stringp(pl) &&
806 !objectp(npc) && !stringp(npc) && !intp(npc))
807 return 0;
808 if (!stringp(pl))
809 pl=getuid(pl);
810
811 if (intp(npc))
812 npc=by_num[npc,BYNUM_KEY];
813 fn=load_name(npc);
814
815 if (!member(npcs, fn)) return 0;
816
817 int bit = npcs[fn, NPC_NUMBER];
818
819 if (pointerp(pls=unconfirmed_scores[bit]) &&
820 member(pls,pl) != -1)
821 return 1;
822
bugfixd94d0932020-04-08 11:27:13 +0200823 string eks = (({string})MASTER->query_ek(pl) || "");
MG Mud User88f12472016-06-24 23:31:02 +0200824
825 return test_bit(eks, bit);
826}
827
828private void WriteDumpFile(string *keys) {
829 int maxn;
830
831 if (!pointerp(keys)) return;
832
833 rm(SCOREDUMPFILE);
834
835 write_file(SCOREDUMPFILE,sprintf("%5s %5s %4s %s\n",
836 "Nr.", "Cnt", "Sc", "Objekt"));
837 foreach(string key: keys) {
838 write_file(SCOREDUMPFILE,sprintf("%5d %5d %4d %O\n",
839 npcs[key,NPC_NUMBER], npcs[key,NPC_COUNT],
840 npcs[key,NPC_SCORE], key));
841 maxn += npcs[key,NPC_SCORE];
842 }
843 write_file(SCOREDUMPFILE,sprintf(
844 "========================================================\n"
845 "NPCs gesamt: %d Punkte\n\n",maxn));
846}
847
848public varargs int DumpNPCs(int sortkey) {
849
850 if (extern_call() && !allowed()) return SCORE_NO_PERMISSION;
851 if (!intp(sortkey)) return SCORE_INVALID_ARG;
852
853 rm(SCOREDUMPFILE);
854
855 // sortieren
856 string *keys=sort_array(m_indices(npcs), function int (string a, string b) {
857 return(npcs[a,sortkey] < npcs[b,sortkey]); } );
858 call_out(#'WriteDumpFile, 2, keys);
859
860 return 1;
861}
862
863public int SetScoreBit(string pl, int bit)
864{
865 string ek;
866
867 if (!allowed())
868 return SCORE_NO_PERMISSION;
869
bugfixd94d0932020-04-08 11:27:13 +0200870 ek = (({string})MASTER->query_ek(pl) || "");
MG Mud User88f12472016-06-24 23:31:02 +0200871 ek = set_bit(ek, bit);
bugfixd94d0932020-04-08 11:27:13 +0200872 ({int})MASTER->update_ek(pl, ek);
MG Mud User88f12472016-06-24 23:31:02 +0200873
874 // Vergabestatistik hochzaehlen.
875 npcs[by_num[bit,BYNUM_KEY],NPC_COUNT]++;
876
877 if (member(users_ek, pl))
878 m_delete(users_ek, pl);
879
880 write_file(SCORELOGFILE,sprintf("SETBIT: %s %s %5d Sc: %.3d %s (%s, %O)\n",
881 strftime("%d%m%Y-%T",time()),pl, bit,
882 by_num[bit,BYNUM_SCORE], by_num[bit,BYNUM_KEY],
883 geteuid(previous_object()), this_interactive()));
884 return 1;
885}
886
887public int ClearScoreBit(string pl, int bit)
888{
889 string ek;
890
891 if (!allowed())
892 return SCORE_NO_PERMISSION;
893
bugfixd94d0932020-04-08 11:27:13 +0200894 ek = (({string})MASTER->query_ek(pl) || "");
MG Mud User88f12472016-06-24 23:31:02 +0200895 ek = clear_bit(ek, bit);
bugfixd94d0932020-04-08 11:27:13 +0200896 ({int})MASTER->update_ek(pl, ek);
MG Mud User88f12472016-06-24 23:31:02 +0200897
898 // Vergabestatistik runterzaehlen.
899 npcs[by_num[bit,BYNUM_KEY],NPC_COUNT]--;
900
901 if (member(users_ek, pl))
902 m_delete(users_ek, pl);
903
904 write_file(SCORELOGFILE,sprintf(
905 "CLEARBIT: %s %s %5d Sc: %.3d %s (%s, %O)\n",
906 strftime("%d%m%Y-%T",time()),pl,bit,
907 by_num[bit,BYNUM_SCORE],by_num[bit,BYNUM_KEY],
908 geteuid(previous_object()), this_interactive()));
909 return 1;
910}
911
912private status ektipAllowed()
913{
914 status poOK;
915 string poName;
916 status ret;
917
918 poName=load_name(previous_object());
919 poOK=previous_object() &&
920 ((previous_object()==find_object(EKTIPGIVER)) || (poName==EKTIPLIST) );
921
922 ret=allowed() ||
923 (this_player() && this_interactive() && previous_object() &&
924 this_interactive()==this_player() && poOK);
925 return ret;
926}
927
928// liefert alle EKs, die aktiv sind und die der Spieler noch nicht hat in
929// einem Mapping entsprechend npcs zurueck.
930public mapping getFreeEKsForPlayer(object player)
931{
932 if(!ektipAllowed() || !objectp(player) || !query_once_interactive(player)){
933 return ([]);
934 }
935 // alle EKs, die der Spieler hat
Arathornd5c9c022020-01-08 22:04:28 +0100936 string eks = ({string})master()->query_ek(getuid(player));
MG Mud User88f12472016-06-24 23:31:02 +0200937 // als Tips kommen alle in Frage, die er nicht hat, vor dem Invertieren muss
938 // aber sichergestellt werden, dass eks min. so lang ist wie active_eks, da
939 // die Invertierung ja z.B. nur EKs 0-1700 beruecksichtigt, wenn 1700 der
940 // hoechste EK im Spieler ist und dann als Tips alle EKs ueber
941 // 1700 verlorengingen.
942 // hier wird das letzte Bit von active_eks ermittelt und das darauf folgende
943 // Bit im Spieler-EK-String gesetzt und wieder geloescht, woraufhin der
944 // EK-String min. so lang wie active_eks ist. (es ist egal, wenn er
945 // laenger ist, auch egal, wenn man ein Bit ueberschreibt, das faellt alles
946 // gleich beim and_bits() raus.
947 int lb = last_bit(active_eks) + 1;
948 eks = clear_bit(set_bit(eks, lb), lb);
949 // jetzt invertieren
950 string non_eks = invert_bits(eks);
951 // jetzt vorhande EK-Tips ausfiltern. Im Prinzip gleiches Spiel wie oben.
Arathornd5c9c022020-01-08 22:04:28 +0100952 string ektips = ({string})master()->query_ektips(getuid(player));
MG Mud User88f12472016-06-24 23:31:02 +0200953 // jetzt alle nicht als Tip vergebenen NPC ermitteln, vor dem Invertieren
954 // wieder Laenge angleichen...
955 ektips = invert_bits(clear_bit(set_bit(ektips, lb), lb));
956 // verunden liefert EKs, die der Spieler nicht hat und nicht als Tip hat
957 ektips = and_bits(ektips, non_eks);
958
959 // und nun die inaktive EKs ausfiltern, nochmal verunden
960 ektips = and_bits(ektips, active_eks);
961
962 // mal Platz reservieren, entsprechend der Menge an Bits
963 mapping freeKills = m_allocate( count_bits(ektips), 2);
964 // durch alle jetzt gesetzten Bits laufen, d.h. alle EKs, die der Spieler
965 // nicht hat und ein Mapping a la npcs erstellen.
966 int p=-1;
967 while ( (p=next_bit(ektips, p)) != -1) {
968 freeKills += ([ by_num[p,0]: p; by_num[p,1] ]);
969 }
970
971 return freeKills;
972}
973
974public int addTip(mixed key,string tip)
975{
976 string fn;
977
978 if (!allowed())
979 return SCORE_NO_PERMISSION;
980
981 if (!tip || (!objectp(key) && !stringp(key)))
982 return SCORE_INVALID_ARG;
983
984 fn=load_name(key);
985
986 if (!member(npcs, fn)) return SCORE_INVALID_ARG;
987 tipList+=([fn:tip]);
988 save_object(SCORESAVEFILE);
989
990 return 1;
991}
992
993public int changeTip(mixed key,string tip)
994{
995 return addTip(key,tip);
996}
997
998public int removeTip(mixed key)
999{
1000 string fn;
1001
1002 if (!allowed())
1003 return SCORE_NO_PERMISSION;
1004
1005 if ((!objectp(key) && !stringp(key)))
1006 return SCORE_INVALID_ARG;
1007
1008 fn=load_name(key);
1009
1010 if (!member(tipList, fn)) return SCORE_INVALID_ARG;
1011
1012 m_delete(tipList,fn);
1013 save_object(SCORESAVEFILE);
1014
1015 return 1;
1016}
1017
1018private string getTipFromList(mixed key)
1019{
1020 string fn;
1021
1022 if (!ektipAllowed())
1023 return "";
1024
1025 if ((!objectp(key) && !stringp(key)))
1026 return "";
1027
1028 fn=load_name(key);
1029
1030 if (!member(tipList, fn)) return "";
1031
1032 return tipList[fn];
1033}
1034
1035private string _getTip(mixed key)
1036{
1037 string fn;
1038 string tip;
1039 string* path;
1040
1041 if ((!objectp(key) && !stringp(key)))
1042 return "";
1043
1044 fn=load_name(key);
1045
1046 if(!member(npcs,fn)) return "";
1047
1048 tip=getTipFromList(fn);
1049 if(!tip || tip==""){
1050 path=explode(fn,"/")-({""});
1051 if(sizeof(path)<3) return "";
1052 if(path[0]=="players") {
1053 string tiptext;
1054 if ( path[1] == "ketos" )
1055 tiptext = "Ketos im Gebirge";
1056 else if ( path[1] == "boing" && path[2] == "friedhof" )
1057 tiptext = "Boing im eisigen Polar";
1058 else
1059 tiptext = capitalize(path[1]);
1060 return "Schau Dich doch mal bei "+tiptext+" um.";
1061 }
1062
1063 if(path[0]=="d")
1064 {
1065 tip+="Schau Dich doch mal ";
1066
1067 if(file_size("/players/"+path[2])==-2)
1068 {
1069 tip+="bei "+capitalize(path[2]+" ");
1070 }
1071 if ( path[1]=="polar" && file_size("/players/"+path[3])==-2 )
1072 {
1073 tip+="bei "+capitalize(path[3])+" ";
1074 }
1075
1076 if(path[1]=="anfaenger")
1077 tip+="in den Anfaengergebieten ";
1078 if(path[1]=="fernwest")
1079 tip+="in Fernwest ";
1080 if(path[1]=="dschungel")
1081 tip+="im Dschungel ";
1082 if(path[1]=="schattenwelt")
1083 tip+="in der Welt der Schatten ";
1084 if(path[1]=="unterwelt")
1085 tip+="in der Unterwelt ";
1086 if(path[1]=="gebirge")
1087 tip+="im Gebirge ";
1088 if(path[1]=="seher")
1089 tip+="bei den Sehergebieten ";
1090 if(path[1]=="vland")
1091 tip+="auf dem Verlorenen Land ";
1092 if(path[1]=="ebene")
1093 tip+="in der Ebene ";
1094 if(path[1]=="inseln")
1095 tip+="auf den Inseln ";
1096 if(path[1]=="wald")
1097 tip+="im Wald ";
1098 if(path[1]=="erzmagier")
1099 tip+="bei den Erzmagiern ";
1100 if(path[1]=="polar")
1101 {
1102 if (path[2]=="files.chaos")
1103 tip+="in den Raeumen der Chaosgilde ";
1104 tip+="im eisigen Polar ";
1105 }
1106 if(path[1]=="wueste")
1107 tip+="in der Wueste ";
1108 tip+="um.";
1109 }
1110 else if ( path[0]=="gilden" )
1111 {
1112 tip+="Schau Dich doch mal";
1113 switch( path[1] )
1114 {
1115 case "mon.elementar":
1116 tip+=" unter den Anfuehrern der Elementargilde";
1117 break;
1118 case "files.dunkelelfen":
1119 tip+=" unter den Anfuehrern der Dunkelelfengilde";
1120 break;
1121 case "files.klerus":
1122 tip+=" beim Klerus";
1123 break;
1124 case "files.werwoelfe":
1125 tip+=" unter den Anfuehrern der Werwoelfe";
1126 break;
1127 case "files.chaos":
1128 tip+=" unter den Anfuehrern der Chaosgilde";
1129 break;
1130 default:
1131 tip+=" in einer der Gilden";
1132 break;
1133 }
1134 tip+=" um.";
1135 }
1136 else if ( path[0] == "p" )
1137 {
1138 tip+="Schau Dich doch mal ";
1139 switch( path[1] )
1140 {
1141 case "zauberer":
1142 tip+="in der Zauberergilde zu Taramis";
1143 break;
1144 case "kaempfer":
1145 tip+="bei den Angehoerigen des Koru-Tschakar-Struvs";
1146 break;
1147 case "katzenkrieger":
1148 tip+="bei der Gilde der Katzenkrieger";
1149 break;
1150 case "tanjian":
1151 tip+="unter den Meistern der Tanjiangilde";
1152 break;
1153 }
1154 tip+=" um.";
1155 }
1156 }
1157 return tip;
1158}
1159
1160// return valid tips from database or existing
1161public string getTip(mixed key)
1162{
MG Mud User88f12472016-06-24 23:31:02 +02001163 if (!ektipAllowed())
1164 return "";
1165
1166 return _getTip(key);
1167}
1168
1169// liefert ein Array mit allen Objekten zurueck, auf die bitstr verweist, also
1170// eine Liste aller Objekte, die als Tip vergeben wurden.
1171private string* makeTiplistFromBitString(string bitstr)
1172{
1173 string * ret= allocate(count_bits(bitstr));
1174 // ueber alle gesetzten bits laufen und Array zusammensammeln
1175 int i;
1176 int p=-1;
1177 while ((p=next_bit(bitstr,p)) != -1) {
1178 ret[i] = by_num[p, 0];
1179 i++;
1180 }
1181 // zur Sicherheit
1182 ret -= ({0});
1183
1184 return ret;
1185}
1186
1187// gibt die Objektnamen der EK-Tips vom jeweiligen Spieler zurueck.
1188public string *QueryTipObjects(mixed player) {
1189
1190 if (extern_call() && !allowed())
1191 return 0;
1192 if (objectp(player) && query_once_interactive(player))
1193 player=getuid(player);
1194 if (!stringp(player))
1195 return 0;
1196
Arathornd5c9c022020-01-08 22:04:28 +01001197 string tipstr = ({string})master()->query_ektips(player);
MG Mud User88f12472016-06-24 23:31:02 +02001198 // jetzt EK-Tips ausfiltern, die erledigt sind. Dazu EK-Liste holen...
Arathornd5c9c022020-01-08 22:04:28 +01001199 string eks = ({string})master()->query_ek(player);
MG Mud User88f12472016-06-24 23:31:02 +02001200 // als Tips kommen alle in Frage, die er nicht hat, vor dem Invertieren muss
1201 // aber sichergestellt werden, dass eks min. so lang ist wie tipstr, da
1202 // die Invertierung ja z.B. nur EKs 0-1700 beruecksichtigt, wenn 1700 der
1203 // hoechste EK im Spieler ist und dann alle Tips ueber
1204 // 1700 verlorengingen.
1205 // hier wird das letzte Bit von tipstr ermittelt und das darauf folgende
1206 // Bit im Spieler-EK-String gesetzt und wieder geloescht, woraufhin der
1207 // EK-String min. so lang wie der Tipstring ist. (es ist egal, wenn er
1208 // laenger ist, auch egal, wenn man ein Bit ueberschreibt, das faellt alles
1209 // gleich beim and_bits() raus.
1210 int lb = last_bit(tipstr) + 1;
1211 eks = clear_bit(set_bit(eks, lb), lb);
1212 // jetzt invertieren
1213 string non_eks = invert_bits(eks);
1214 // jetzt verunden und man hat die Tips, die noch nicht gehauen wurden.
1215 tipstr = and_bits(tipstr, non_eks);
1216 // noch inaktive EKs ausfiltern...
1217 tipstr = and_bits(tipstr, active_eks);
1218
1219 return makeTiplistFromBitString(tipstr);
1220}
1221
1222public string allTipsForPlayer(object player)
1223{
1224
1225 if(!player || !this_interactive() ||
1226 (this_interactive()!=player && !IS_ARCH(this_interactive())) )
1227 return "";
1228
1229 string *tips = QueryTipObjects(player);
1230
1231 tips = map(tips, #'_getTip);
1232 tips -= ({0,""});
1233
1234 return implode(tips, "\n");
1235}
1236
1237public status playerMayGetTip(object player)
1238{
1239 int numElegible;
1240 int numReceived;
1241 int lvl;
1242 int i;
1243 string tips;
1244
1245 if(!ektipAllowed() || !player || !query_once_interactive(player))
1246 return 0;
1247
1248 if(!player || !query_once_interactive(player))
1249 return 0;
1250
Arathornd5c9c022020-01-08 22:04:28 +01001251 lvl = ({int})player->QueryProp(P_LEVEL);
MG Mud User88f12472016-06-24 23:31:02 +02001252 numElegible=0;
1253 i=sizeof(EKTIPS_LEVEL_LIMITS)-1;
1254
1255 if(lvl>EKTIPS_LEVEL_LIMITS[i])
1256 numElegible+=(lvl-EKTIPS_LEVEL_LIMITS[i]);
1257
1258 for(i;i>=0;i--){
1259 if(lvl>=EKTIPS_LEVEL_LIMITS[i]) numElegible++;
1260 }
1261
Arathornd5c9c022020-01-08 22:04:28 +01001262 tips = ({string})MASTER->query_ektips(getuid(player)) || "";
MG Mud User88f12472016-06-24 23:31:02 +02001263 // inaktive EKs ausfiltern.
1264 tips = and_bits(tips, active_eks);
1265 // und Gesamtzahl an Tips zaehlen. Hier werden erledigte Tips explizit nicht
1266 // ausgefiltert!
1267 numReceived=count_bits(tips);
1268
1269 return numElegible>numReceived;
1270}
1271
1272public string giveTipForPlayer(object player)
1273{
1274 string* tmp;
1275 mapping free;
1276 string tip,pl,ektip;
1277 int index;
1278
1279 if(!ektipAllowed() || !player ||
1280 !query_once_interactive(player) || !playerMayGetTip(player))
1281 return "";
1282
1283 pl=getuid(player);
1284 free=getFreeEKsForPlayer(player);
1285
1286 if(!mappingp(free) || sizeof(free)==0)
1287 return "";
1288
1289 tmp=m_indices(free);
1290
Arathornd5c9c022020-01-08 22:04:28 +01001291 ektip = ({string})MASTER->query_ektips(pl) || "";
MG Mud User88f12472016-06-24 23:31:02 +02001292
1293 foreach(int i: EKTIPS_MAX_RETRY) {
1294 index=random(sizeof(tmp));
1295 tip=getTip(tmp[index]);
1296 if (stringp(tip) && sizeof(tip)) {
1297 ektip=set_bit(ektip,npcs[tmp[index],NPC_NUMBER]);
bugfixd94d0932020-04-08 11:27:13 +02001298 ({int})MASTER->update_ektips(pl,ektip);
MG Mud User88f12472016-06-24 23:31:02 +02001299 break; //fertig
1300 }
1301 }
1302
1303 return tip;
1304}
1305
1306// checkt NPCs auf Existenz und Ladbarkeit
1307public void CheckNPCs(int num) {
1308 string fn;
1309 object ekob;
1310 if (!num) rm(SCORECHECKFILE);
1311 while (num <= lastNum && get_eval_cost() > 1480000) {
1312 if (!by_num[num,1] || !by_num[num,0]) {
1313 num++;
1314 continue;
1315 }
1316 fn = by_num[num,0] + ".c";
1317 if (file_size(fn) <= 0) {
1318 // File nicht existent
1319 write_file(SCORECHECKFILE, sprintf(
1320 "EK %.4d ist nicht existent (%s)\n",num,fn));
1321 }
1322 else if (catch(ekob=load_object(fn)) || !objectp(ekob) ) {
1323 // NPC offenbar nicht ladbar.
1324 write_file(SCORECHECKFILE, sprintf(
1325 "EK %.4d ist nicht ladbar (%s)\n",num,fn));
1326 }
1327 num++;
1328 }
1329 ZDEBUG(sprintf("%d NPC checked",num));
1330 if (num <= lastNum)
1331 call_out(#'CheckNPCs,4,num);
1332 else
1333 ZDEBUG("Finished!");
1334}
1335
1336// liquidiert einen EK endgueltig. An diesem Punkt wird davon augegangen, dass
1337// kein Spieler den EK mehr gesetzt hat!
1338private void LiquidateEK(int bit) {
1339
1340 if (!intp(bit) || bit < 0) return;
1341
1342 string obname = by_num[bit, BYNUM_KEY];
1343 int score = by_num[bit, BYNUM_SCORE];
1344
1345 if (member(npcs, obname) && (npcs[obname, NPC_NUMBER] == bit)) {
1346 m_delete(by_num, bit);
1347 m_delete(npcs, obname);
1348 if (member(unconfirmed_scores,bit))
1349 m_delete(unconfirmed_scores,bit);
1350 active_eks = clear_bit(active_eks,bit);
1351 removeTip(obname);
1352 free_num += ({bit});
1353 write_file(SCOREAUTOLOG,sprintf(
1354 "LIQUIDATEEK: %s %5d Score %3d %s\n",
1355 strftime("%d%m%Y-%T",time()), bit, score, obname));
1356 }
1357}
1358
1359private void check_player(string pl) {
1360 int changed, changed2;
1361
1362 // EKs pruefen
Arathornd5c9c022020-01-08 22:04:28 +01001363 string eks = ({string})master()->query_ek(pl) || "";
MG Mud User88f12472016-06-24 23:31:02 +02001364 string *opfer=allocate( (sizeof(eks)*6)+1, "");
1365 int p=-1;
1366 while ((p=next_bit(eks,p)) != -1) {
1367 if (!member(by_num, p)) {
1368 write_file(SCORECHECKFILE, sprintf(
1369 "UNKNOWNEK %s %5d in %s gefunden.\n",
1370 strftime("%d%m%Y-%T",time()), p, pl));
1371 }
1372 // wenn das aktuelle Bit geloescht werden soll, also in to_be_removed
1373 // steht...
1374 if (member(to_be_removed,p) != -1) {
1375 eks = clear_bit(eks,p);
1376 changed=1;
1377 write_file(EKCLEANLOG,sprintf(
1378 "CLEARBIT: %s %O %5d %s\n",
1379 strftime("%d%m%Y-%T",time()), pl, p,
1380 by_num[p,BYNUM_KEY] || "NoName"));
1381 }
1382 else {
1383 // sonst statistikpflege
1384 npcs[by_num[p,BYNUM_KEY],NPC_COUNT]++;
1385 // loggen, welche NPC der Spieler hat
1386 opfer[p]=to_string(p);
1387 }
1388 }
1389 // und noch die Ek-Tips...
Arathornd5c9c022020-01-08 22:04:28 +01001390 string ektips = ({string})master()->query_ektips(pl) || "";
MG Mud User88f12472016-06-24 23:31:02 +02001391 p = -1;
1392 while ((p=next_bit(ektips,p)) != -1) {
1393 if (!member(by_num, p)) {
1394 write_file(EKCLEANLOG, sprintf(
1395 "UNKNOWNEK %s %5d in %s (EKTips) gefunden - clearing.\n",
1396 strftime("%d%m%Y-%T",time()), p, pl));
1397 ektips = clear_bit(ektips, p); // hier direkt loeschen.
1398 changed2 = 1;
1399 }
1400 // wenn das aktuelle Bit geloescht werden soll, also in to_be_removed
1401 // steht...
1402 else if (member(to_be_removed,p) != -1) {
1403 ektips = clear_bit(ektips,p);
1404 changed2=1;
1405 write_file(EKCLEANLOG,sprintf(
1406 "CLEAREKTIP: %s %O %5d %s\n",
1407 strftime("%d%m%Y-%T",time()), pl, p,
1408 by_num[p,BYNUM_KEY] || "NoName"));
1409 }
1410 }
1411
1412 if (changed) {
bugfixd94d0932020-04-08 11:27:13 +02001413 ({int})master()->update_ek(pl, eks);
MG Mud User88f12472016-06-24 23:31:02 +02001414 }
1415 if (changed2) {
bugfixd94d0932020-04-08 11:27:13 +02001416 ({int})master()->update_ektips(pl, ektips);
MG Mud User88f12472016-06-24 23:31:02 +02001417 }
1418 opfer-=({""});
1419 write_file(WERKILLTWEN,sprintf("%s\n%=-78s\n\n",pl,CountUp(opfer)||""));
1420}
1421
1422public void check_all_player(mapping allplayer) {
1423
1424 if (extern_call() && !allowed())
1425 return;
1426
1427 if (!mappingp(allplayer)) {
1428 foreach(string key: npcs) {
Arathornd5c9c022020-01-08 22:04:28 +01001429 npcs[key,NPC_COUNT]=0;
MG Mud User88f12472016-06-24 23:31:02 +02001430 }
Arathornd5c9c022020-01-08 22:04:28 +01001431 allplayer = ({mapping})master()->get_all_players();
MG Mud User88f12472016-06-24 23:31:02 +02001432 rm(WERKILLTWEN);
1433 call_out(#'check_all_player,2,allplayer);
1434 return;
1435 }
1436
1437 // offenbar fertig mit allen Spielern, jetzt noch den Rest erledigen.
1438 if (!sizeof(allplayer)) {
1439 foreach(int bit: to_be_removed) {
1440 LiquidateEK(bit);
1441 }
1442 to_be_removed=({});
1443 save_object(SCORESAVEFILE);
1444 ZDEBUG("Spielerchecks und EK-Liquidation fertig.\n");
1445 return;
1446 }
1447
1448 string dir=m_indices(allplayer)[0];
1449 string *pls=allplayer[dir];
1450 foreach(string pl: pls) {
1451 if (get_eval_cost() < 1250000)
1452 break; // spaeter weitermachen.
1453 catch(check_player(pl) ; publish);
1454 pls-=({pl});
1455 }
1456 allplayer[dir] = pls;
1457
1458 if (!sizeof(allplayer[dir]))
1459 m_delete(allplayer,dir);
1460
1461 call_out(#'check_all_player,2,allplayer);
1462}
1463