blob: a183c617a8c98a8052b0fed7388c423dc8a24ef9 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// explorationmaster.c -- Verwaltung der ExplorationPoints (FP)
4//
5// $Id: explorationmaster.c 9569 2016-06-05 21:28:23Z Zesstra $
6//
7#pragma strict_types,rtt_checks
8#pragma no_clone,no_shadow,no_inherit
9//#pragma pedantic
10//#pragma range_check
11#pragma warn_deprecated
12
13#define BY_EP "/secure/ARCH/EPSTAT_BY_EP"
14#define BY_PL "/secure/ARCH/EPSTAT_BY_PL"
15#define BY_PN "/secure/ARCH/EPSTAT_BY_PN"
16#define AVGLOG "/secure/ARCH/EPSTAT_AVG"
17
18inherit "/std/util/pl_iterator";
19
20#include <config.h>
21#include <wizlevels.h>
22#include <userinfo.h>
23#include <exploration.h>
24#include <properties.h>
25#include <new_skills.h>
26
27#define DOMAIN_INFO 1
28
29#include <living/comm.h>
30#define ZDEBUG(x) if (find_player("zesstra")) \
31 find_player("zesstra")->ReceiveMsg(x,MT_DEBUG,0,object_name()+":",this_object())
32//#define ZDEBUG(x)
33
34// Struktur: ([ string filename : string* action, int number, int type ])
35private mapping obs;
36private int epcount; // Anzahl FP
37private int epavg; // Durchschnittliche FP/Spieler
38// Bitstrings fuer normale (alloc) und Bonus-FP (bonus)
39private string alloc,bonus;
40
41private nosave string vc_ob;
42private nosave string output;
43
44private nosave int changed;
45private nosave int dumping;
46private nosave mapping dumpMap, lastfound, querytime;
47
48private nosave int stat_done;
49
50protected void create()
51{
52 seteuid(getuid(this_object()));
53 if (!restore_object(EPSAVEFILE)) {
54 obs = ([]);
55 epcount = epavg = 0;
56 alloc = " ";
57 bonus = " ";
58 save_object(EPSAVEFILE);
59 }
60 if (!bonus) bonus=" ";
61 lastfound=([]);
62 querytime=([]);
63}
64
65public varargs int remove(int silent)
66{
67 save_object(EPSAVEFILE);
68 destruct(this_object());
69 return 1;
70}
71
72// Statistik erstellen
73private int SetAverage(int newavg)
74{
75 if (epavg != newavg)
76 {
77 epavg = newavg;
78 write_file(AVGLOG, sprintf("%s : %d\n",dtime(time()),newavg));
79 save_object(EPSAVEFILE);
80 }
81 return epavg;
82}
83
84private void check_player(string pl, mixed extra)
85{
86 int *fp_found = extra[0];
87 mapping plstat = extra[1];
88 mapping pnstat = extra[2];
89 object pldata = extra[3];
90 if (!pldata)
91 {
Zesstra073ec8b2020-01-05 21:56:21 +010092 pldata=clone_object("/secure/playerdata");
MG Mud User88f12472016-06-24 23:31:02 +020093 extra[3]=pldata;
94 }
Zesstra073ec8b2020-01-05 21:56:21 +010095 //ZDEBUG(sprintf("check %s...",pl));
MG Mud User88f12472016-06-24 23:31:02 +020096 // Letzte Loginzeit ermitteln, wenn laenger als 90 Tage her und nicht
97 // eingeloggt, wird der Spieler uebersprungen.
Zesstrab5c40012017-06-20 22:09:39 +020098 // Der playerdata hat als UID unsere eigene, weil wir ihn geclont haben.
99 // Damit darf er alle Spielersavefiles lesen...
MG Mud User88f12472016-06-24 23:31:02 +0200100 pldata->ReleasePlayer();
Zesstradb1a2192019-12-03 19:36:49 +0100101 pldata->LoadPlayer(pl);
MG Mud User88f12472016-06-24 23:31:02 +0200102 // Testspieler ausnehmen, Spieler, die 90 Tage nicht da waren.
Zesstradb1a2192019-12-03 19:36:49 +0100103 if ( (({int})pldata->QueryProp(P_LAST_LOGIN) < time() - 7776000
MG Mud User88f12472016-06-24 23:31:02 +0200104 && !find_player(pl))
Zesstradb1a2192019-12-03 19:36:49 +0100105 || ({<int|string>})pldata->QueryProp(P_TESTPLAYER))
MG Mud User88f12472016-06-24 23:31:02 +0200106 return;
107 // Wenn kein SPieler/Seher, sondern Magier: auch ueberspringen
Zesstradb1a2192019-12-03 19:36:49 +0100108 mixed* uinfo = ({mixed*})master()->get_userinfo(pl);
MG Mud User88f12472016-06-24 23:31:02 +0200109 if (uinfo[USER_LEVEL+1] >= LEARNER_LVL)
110 return;
111
Zesstradb1a2192019-12-03 19:36:49 +0100112 string eps=({string})master()->query_ep(pl) || "";
MG Mud User88f12472016-06-24 23:31:02 +0200113 int p=-1;
114 int count, avgcount;
115 while ((p=next_bit(eps,p)) != -1)
116 {
117 if (p<sizeof(fp_found))
118 {
119 ++count;
120 ++fp_found[p];
121 // es tragen nur normale EPs zum Durchschnitt bei
122 if (!test_bit(bonus,p))
123 ++avgcount;
124 }
125 }
126
127 // Spieler mit weniger als MIN_EP ignorieren.
128 if (avgcount >= MIN_EP)
129 {
130 extra[4] += avgcount; // Summe der gefundenen FP
131 ++extra[5]; // Anzahl beruecksichtigter Spieler
132 }
133 plstat += ([ pl : count ]);
134 if (!member(pnstat, count))
135 pnstat += ([ count : ({ pl }) ]);
136 else
137 pnstat[count] = ({ pl })+pnstat[count];
Zesstra073ec8b2020-01-05 21:56:21 +0100138 //ZDEBUG(sprintf("done\n"));
MG Mud User88f12472016-06-24 23:31:02 +0200139}
140
141// Mit allen Spielern fertig, aufraeumen.
142#define BAR "************************************************************"
143private void plcheck_finished(mixed extra)
144{
145 ZDEBUG("plcheck_finished entry\n");
146 if (get_eval_cost() < 1000000)
147 {
148 call_out(#'plcheck_finished, 4+random(6), extra);
149 return;
150 }
151
152 int *fp_found = extra[0];
153 mapping plstat = extra[1];
154 mapping pnstat = extra[2];
155 object pldata = extra[3];
156 int avgcount = extra[4];
157 int avgspieler = extra[5];
158
159 if (objectp(pldata))
160 pldata->remove(1);
161
162 if (file_size(BY_EP) >= 0)
163 rm(BY_EP);
164 if (file_size(BY_PL) >= 0)
165 rm(BY_PL);
166 if (file_size(BY_PN) >= 0)
167 rm(BY_PN);
168
169 // Neuen Durchschnittswert fuer gefundene FP setzen.
170 if (avgspieler)
171 {
172 SetAverage(to_int(avgcount/avgspieler));
173 }
174
175 // Histogramm ueber alle FP schreiben: wie oft wurde jeder gefunden?
176 int maxval = max(fp_found);
177 int fp_index;
178 foreach(int found : fp_found)
179 {
180 write_file(BY_EP, sprintf("%5d:%5d %s\n", fp_index, found,
181 BAR[0..(60*found)/maxval]));
182 ++fp_index;
183 }
184 // sortierte Liste der Spieler (sortiert nach gefundenen FP) erzeugen
185 foreach(int fp : sort_array(m_indices(pnstat),#'<) )
186 {
187 foreach(string pl : pnstat[fp])
188 write_file(BY_PN, sprintf("%-14s: %5d\n", pl, fp));
189 }
190 // alphabetisch sortierte Liste der Spieler erzeugen
191 foreach(string pl : sort_array(m_indices(plstat),#'>) )
192 {
193 write_file(BY_PL, sprintf("%-14s: %5d\n", pl, plstat[pl]));
194 }
195}
196#undef BAR
197
198private void make_stat()
199{
200 stat_done = time();
201 // Leider laesst sich epcount nicht nutzen Beim Loeschen von FP wird
202 // <epcount> dekrementiert, so dass hier nicht mehr alle FPs ausgewertet
203 // werden. Daher muss stattdessen bis zur Gesamtgroesse von <obs> gezaehlt
204 // werden.
205 int* fp_found = allocate(sizeof(obs));
206 mapping plstat = m_allocate(500);
207 mapping pnstat = m_allocate(500);
208 start_player_check(#'check_player, #'plcheck_finished, 1200000,
209 ({fp_found,plstat,pnstat,
210 0, 0, 0}) );
211}
212
213void reset()
214{
215 if (changed && !dumping)
216 {
217 catch(rm(DUMPFILE);publish);
218 dumping = 1;
219 call_out("dumpEPObjects", 0, sort_array(m_indices(obs),#'> /*'*/));
220 changed = 0;
221 }
222 // nur einmal am tag statistiken erstellen
223 if (time()%86400 < 4000
224 && stat_done < time()-80000)
225 make_stat();
226}
227
228private string strArr(string *s)
229{
230 string ret;
231 int i;
232
233 ret = ("\""+s[<1]+"\"");
234 for (i=sizeof(s)-2; i>=0; i--)
235 ret += (", \""+s[i]+"\"");
236
237 return ret;
238}
239
240static void dumpEPObjects(string *doit)
241{
242 string *toGo, id;
243 int i,j;
244
245 if (!mappingp(dumpMap))
246 dumpMap = ([]);
247
248 toGo = 0;
249 if (sizeof(doit) > 200) {
250 toGo = doit[200..];
251 doit = doit[0..199];
252 }
253
254 j = sizeof(doit);
255
256 for (i=0; i<j; i++) {
Zesstradb1a2192019-12-03 19:36:49 +0100257 id = ({string})master()->creator_file(doit[i]);
MG Mud User88f12472016-06-24 23:31:02 +0200258 if (member(dumpMap, id))
259 dumpMap[id] += ({ doit[i] });
260 else
261 dumpMap += ([ id : ({ doit[i] }) ]);
262 }
263 if (toGo)
264 call_out("dumpEPObjects", 1, toGo);
265 else {
266 int step;
267
268 step = 0;
269 id = "";
270 toGo = sort_array(m_indices(dumpMap),#'> /*'*/ );
271 for (i=0, j=sizeof(toGo); i<j; i++) {
272 int k,l;
273 doit = dumpMap[toGo[i]];
274 id += sprintf("### %s %s\n", toGo[i], "#########################"[sizeof(toGo[i])..]);
275 for (k=0, l=sizeof(doit); k<l; k++) {
276 id += sprintf("%s %4d %s %s.c ({ %s })\n",
277 EP_TYPES[obs[doit[k], MPOS_TYPE]],
278 obs[doit[k], MPOS_NUM],
279 test_bit(bonus,obs[doit[k], MPOS_NUM])?"b":"n",
280 doit[k],
281 strArr(obs[doit[k]]));
282 if (!(++step % 50)) {
283 write_file(DUMPFILE, id);
284 id = "";
285 }
286 }
287 id += "\n";
288 }
289 write_file(DUMPFILE,id);
290 if (dumping == 2)
291 write("Fertig! Anfrage bitte wiederholen.\n");
292 dumping = 0;
293 changed = 0;
294 dumpMap = 0;
295 }
296}
297
298private string validOb(mixed ob)
299{
300 string fn, fpart;
301
302 if (!objectp(ob))
303 return 0;
304
305 fn = old_explode(object_name(ob),"#")[0];
306 fpart = old_explode(fn,"/")[<1];
307 /*
308 if (query_once_interactive(ob))
309 return 0;
310
311 if ((file_size(fn+".c") <= 0) &&
312 this_player() &&
313 (strstr(fpart, getuid(this_player())) >= 0))
314 return 0;
315 */
316 return fn;
317}
318
319private int allowed()
320{
321 if (previous_object() && geteuid(previous_object())==ROOTID)
322 return 1;
323 if ( !process_call() && previous_object() && this_interactive()
324 && ARCH_SECURITY )
325 return 1;
326
327 return 0;
328}
329
330nomask varargs int AddEPObject(object ob, mixed keys, int type, int bonusflag)
331{
332 string fn;
333
334 if (!allowed())
335 return EPERR_NOT_ARCH;
336
337 if (!(fn = validOb(ob)))
338 return EPERR_INVALID_OB;
339
340 if (stringp(keys))
341 keys = ({ keys });
342
343 if (member(obs, fn)) {
344 if (type == obs[fn, MPOS_TYPE])
345 obs[fn] = keys;
346 else
347 obs += ([ fn : keys; obs[fn, MPOS_NUM]; type ]);
348 if (bonusflag) bonus = set_bit(bonus, obs[fn, MPOS_NUM]);
349 else bonus = clear_bit(bonus, obs[fn, MPOS_NUM]);
350 }
351 else {
352 int nr, i;
353
354 nr = 0;
355 while (test_bit(alloc,nr))
356 nr++;
357
358 obs += ([ fn : keys; nr; type ]);
359 ++epcount;
360 alloc = set_bit(alloc,nr);
361 if (bonusflag) bonus = set_bit(bonus,nr);
362 else bonus = clear_bit(bonus,nr);
363 }
364
365 changed = 1;
366 save_object(EPSAVEFILE);
367 return epcount;
368}
369
370nomask int RemoveEPObject(string|object ob)
371{
372 string fn;
373
374 if (!allowed())
375 return EPERR_NOT_ARCH;
376
377 if (objectp(ob))
378 ob=object_name(ob);
379
380 fn = explode(ob,"#")[0];
381 if (!member(obs,fn))
382 return EPERR_NO_ENTRY;
383
384 alloc = clear_bit(alloc, obs[fn, MPOS_NUM]);
385 bonus = clear_bit(bonus, obs[fn, MPOS_NUM]);
386
387 m_delete(obs, fn);
388
389 changed = 1;
390 --epcount;
391 save_object(EPSAVEFILE);
392 return epcount;
393}
394
395nomask int ChangeEPObject(object ob, int what, mixed new)
396{
397 string fn, fn2;
398 mapping entry;
399
400 if (!allowed())
401 return EPERR_NOT_ARCH;
402
403 if (!(fn = validOb(ob)))
404 return EPERR_INVALID_OB;
405
406 if (!member(obs,fn))
407 return EPERR_NO_ENTRY;
408
409 switch(what) {
410 case CHANGE_OB:
411 if (!(fn2 = validOb(new)))
412 return EPERR_INVALID_ARG;
413 entry = ([ fn2: obs[fn]; obs[fn,MPOS_NUM]; obs[fn,MPOS_TYPE] ]);
414 obs = m_copy_delete(obs, fn);
415 obs += entry;
416 break;
417 case CHANGE_BONUS:
418 if (!(intp(new)))
419 return EPERR_INVALID_ARG;
420 if (new) bonus=set_bit(bonus,obs[fn,MPOS_NUM]);
421 else bonus=clear_bit(bonus,obs[fn,MPOS_NUM]);
422 break;
423 case CHANGE_KEY:
424 if (!stringp(new) && !pointerp(new))
425 return EPERR_INVALID_ARG;
426
427 if (stringp(new))
428 new = ({ new });
429
430 obs[fn] = new;
431 break;
432 case CHANGE_TYPE:
433 if (!intp(new) || new < 0 || new > EP_MAX)
434 return EPERR_INVALID_ARG;
435
436 obs[fn, MPOS_TYPE] = new;
437 break;
438 default:
439 return EPERR_INVALID_ARG;
440 }
441 changed = 1;
442 save_object(EPSAVEFILE);
443 return 1;
444}
445
446nomask mixed QueryEPObject(object ob)
447{
448 string fn;
449
450 if (!allowed())
451 return EPERR_NOT_ARCH;
452
453 if (!(fn = validOb(ob)))
454 return EPERR_INVALID_OB;
455
456 if (!member(obs, fn))
457 return 0;
458
459 return ({ obs[fn], obs[fn,MPOS_NUM], obs[fn, MPOS_TYPE], test_bit(bonus,obs[fn, MPOS_NUM]) });
460}
461
462private string getMatch(string m)
463{
464 string *res;
465 int i;
466
467 res = regexp(sort_array(m_indices(obs), #'> /*'*/), m);
468 for (i=sizeof(res)-1; i>=0; i--)
469 res[i] = sprintf("%s %s %s.c ({ %s })",
470 EP_TYPES[obs[res[i], MPOS_TYPE]],
471 test_bit(bonus,obs[res[i], MPOS_NUM])?"b":"n",
472 res[i],
473 strArr(obs[res[i]]));
474 return implode(res, "\n");
475}
476
477private string getMatchArch(string m)
478{
479 string *res;
480 int i;
481
482 res = regexp(sort_array(m_indices(obs), #'> /*'*/), m);
483 for (i=sizeof(res)-1; i>=0; i--)
484 res[i] = sprintf("%s %4d %s %s.c ({ %s })",
485 EP_TYPES[obs[res[i], MPOS_TYPE]],
486 obs[res[i], MPOS_NUM],
487 test_bit(bonus,obs[res[i], MPOS_NUM])?"b":"n",
488 res[i],
489 strArr(obs[res[i]]));
490 return implode(res, "\n");
491}
492
493/*
494 * Anleitung fuer >=EMs:
495 * ShowEPObjects() zeigt das gesamte Dumpfile an
496 * ShowEPObjects("dump") erzeugt ein neues Dumpfile
497 * ShowEPObjects("string") liefert alle EPs, die "/string/"
498 * im Filenamen enthalten
499 * ShowEPObjects("string1","string2") liefert alle EPs, die
500 * "/string1/string2/" im Filenamen enthalten
501 * Anleitung fuer RMs:
502 * ShowEPObjects() liefert all Deine EPs
503 * ShowEPObjects("domainname") liefert Deine EPs in Deiner Domanin
504 * ShowEPObjects("domainname","magiername") liefert die EPs des
505 * Mitarbeiters in Deiner Region
506 *
507 * Anleitung fuer <RMs:
508 * ShowEPObjects() liefert Deine EPs
509 *
510 * Alle EPObjects einer ganzen Domain kann man nicht mehr auf einmal
511 * ziehen und sollte man auch nicht. Alle Zugriffe auf diese Funktion
512 * werden geloggt. Auch die der EMs!
513 *
514 * Rikus
515 */
516
517nomask varargs void ShowEPObjects(string what, string magname)
518{
519 if (allowed()) {
520 if (what == "dump") {
521 if (!dumping) {
522 dumping = 2;
523 catch(rm(DUMPFILE);publish);
524 call_out("dumpEPObjects", 0, sort_array(m_indices(obs),#'>/*'*/));
525 }
526 printf("Liste wird erstellt und in '%s' abgelegt!\n", DUMPFILE);
527 return;
528 }
529 if (!what || what == "") {
530 this_interactive()->More(DUMPFILE, 1);
531 log_file("ARCH/EPZugriffe", ctime(time())+": "+
532 capitalize(getuid(this_interactive()))+" schaute sich das DUMPFILE an.\n");
533 return;
534 }
535 what="/"+what+"/";
536 if (magname) what+=magname+"/";
537 }
538 else
539#ifdef DOMAIN_INFO
540 if (IS_LORD(this_interactive())) {
541 if (!what || what == "")
542 what = "/"+getuid(this_interactive())+"/";
543 else {
544 if (!master()->domain_master(getuid(this_interactive()), what)) {
545 write("Sorry, Du kannst nur Objekte in Deiner eigenen Region abfragen!\n");
546 return;
547 }
548 if (!magname || magname=="")
549 magname = getuid(this_interactive());
550// if (!master()->domain_member(magname, what)) {
551// write(capitalize(magname)+" ist gar kein Mitarbeiter in Deiner Region!\n");
552// return;
553// }
554 what = "/d/"+what+"/"+magname+"/";
555 }
556 }
557 else
558#endif
559 {
560 if (!what || what == "")
561 what = getuid(this_interactive());
562 else if (what != getuid(this_interactive())) {
563 write("Sorry, Du kannst nur Deine eigenen Objekte abfragen!\n");
564 return;
565 }
566 what="/"+what+"/";
567 }
568 if (allowed())
569 this_interactive()->More(getMatchArch(what));
570 else
571 this_interactive()->More(getMatch(what));
572 log_file("ARCH/EPZugriffe", ctime(time())+": "+
573 capitalize(getuid(this_interactive()))+" schaute sich "+what+" an.\n");
574 return;
575}
576
577nomask void PrepareVCQuery(string file)
578{
579 string path, *parts;
580
581 vc_ob = 0;
582
583 if (!previous_object() || !stringp(file))
584 return;
585
586 parts = explode(object_name(previous_object()),"/");
587
588 if (parts[<1] == "virtual_compiler") {
589 path = implode(parts[0..<2]+({ file }), "/");
590 if (file_size(path+".c") < 0)
591 vc_ob = path;
592 }
593}
594
595nomask mixed *QueryExplore()
596{
597 string fn;
598
599 if (!previous_object())
600 return 0;
601
602 if (!member(obs, fn = old_explode(object_name(previous_object()),"#")[0]))
603 if (!vc_ob || !member(obs, fn = vc_ob)) {
604 vc_ob = 0;
605 return 0;
606 }
607
608 vc_ob = 0;
609 return ({ obs[fn, MPOS_TYPE], obs[fn] });
610}
611
612nomask int QueryMaxEP()
613{
614 return (epcount||1);
615}
616
617nomask int QueryAverage()
618{
619 return (epavg||1);
620}
621
622static int check_arch(object u)
623{
624 return query_wiz_level(u)>=ARCH_LVL;
625}
626
627private int check_to_fast(string name, string fn, int gesetzt)
628{
629 if (gesetzt) return 1; // Rikus, sonst arger scroll :)
630
631 log_file(FP_LOG,sprintf("%s : %s : %s\n",name,fn,dtime(time())),500000);
632
633 if ( !member( lastfound, name ) )
634 lastfound += ([ name: time(); 1; ({ fn + "#*#" + dtime(time()) + "#*#" +gesetzt }) ]);
635 else if ( time() <= lastfound[name, 0] + LF_TIME ){
636 lastfound[name, 1]++;
637 lastfound[name, 2] = ({ fn + "#*#" + dtime(time()) + "#*#" +gesetzt })
638 + lastfound[name,2];
639 }
640 else {
641 lastfound[name, 1] = 1;
642 lastfound[name, 2] = ({ fn + "#*#" + dtime(time()) + "#*#" +gesetzt });
643 }
644
645 lastfound[name, 0] = time();
646
647 if ( lastfound[name, 1] >= LF_WARN ){
648 object *u;
649 int i;
650 string *tmp;
651
652// u = filter( users(), "check_arch" );
653// map( u, #'tell_object/*'*/, "**** FP-Liste/Script " +
654// capitalize(name) + " (" + dtime(time()) + ") ****\n" );
655
656 for ( i = sizeof(lastfound[name, 2]); i--; ){
657 tmp = explode( lastfound[name, 2][i], "#*#" );
658 log_file( LF_LOG, sprintf( "%s : %s : %s : %s\n",
659 tmp[1], name, tmp[2], tmp[0] ), 500000 );
660 }
661
662 lastfound[name, 2] = ({});
663 }
664 return 1;
665}
666
667nomask int GiveExplorationPoint(string key)
668{
669 string fn;
670 string ep;
671 int gesetzt;
672
673 if (!previous_object() || !this_interactive() || !this_player() ||
674 this_player() != this_interactive() ||
675 this_player()->QueryProp(P_KILLS) ||
676 this_player()->QueryGuest() )
677 return 0;
678
679 fn = old_explode(object_name(previous_object()), "#")[0];
680
681 if (!member(obs, fn))
682 return 0;
683
684 if (member(obs[fn],key) < 0)
685 return 0;
686
687 ep = (MASTER->query_ep(getuid(this_interactive())) || "");
688
689 gesetzt=test_bit(ep,obs[fn,1]);
690 check_to_fast(getuid(this_player()),fn,gesetzt);
691 if (gesetzt) return 0;
692
693 catch(ep = set_bit(ep, obs[fn,1]));
694
Zesstradb1a2192019-12-03 19:36:49 +0100695 return ({int})MASTER->update_ep(getuid(this_interactive()),ep);
MG Mud User88f12472016-06-24 23:31:02 +0200696}
697
698nomask int GiveExplorationPointObject(string key, object ob)
699{
700 string fn;
701 string ep;
702 int gesetzt;
703
704 if (!objectp(ob) || ob->QueryProp(P_KILLS ))
705 return 0;
706
707 fn = old_explode(object_name(previous_object()), "#")[0];
708
709 if (!member(obs, fn))
710 return 0;
711
712 if (member(obs[fn],key) < 0)
713 return 0;
714
715 ep = (MASTER->query_ep(getuid(ob)) || "");
716
717 gesetzt=test_bit(ep,obs[fn,1]);
718 check_to_fast(getuid(this_player()),fn,gesetzt);
719 if (gesetzt) return 0;
720
721 catch(ep = set_bit(ep, obs[fn,1]));
722
Zesstradb1a2192019-12-03 19:36:49 +0100723 return ({int})MASTER->update_ep(getuid(ob),ep);
MG Mud User88f12472016-06-24 23:31:02 +0200724}
725
726
727private int QueryRealExplorationPoints(string pl)
728{
729 return count_bits(MASTER->query_ep(pl) || " ");
730}
731
732nomask int QueryExplorationPoints(mixed pl)
733{
734 mixed val;
735 int ep;
736
737 if (!stringp(pl)) pl=getuid(pl);
738 ep=QueryRealExplorationPoints(pl);
739
740 if (allowed() || !ep) return ep;
741
742 val=querytime[pl];
743
744 if (!pointerp(val) || sizeof(val)<2)
745 val=({0,time()});
746
747 if (time()>=val[1]) {
748 val = ({ ep + random(6)-3, time()+300+random(300) });
749 if (val[0]<0) val[0]=0;
750 querytime+=([pl:val]);
751 }
752 return val[0];
753}
754
755private int remove_fp(int num, string pl)
756{
757 int i,j,k,t,maxEP;
758 string ep;
759 ep = (MASTER->query_ep(pl) || "");
760
761 maxEP = QueryMaxEP();
762 for( i=0; i<num; i++)
763 {
764 t = random(maxEP);
765 for( j=0; j<maxEP; j++ ) {
766 if( test_bit(ep, k=(t+j)%maxEP ) ) break;
767 }
768 if( j==maxEP ) break;
769 ep = clear_bit(ep, k);
770 }
771 MASTER->update_ep(pl,ep);
772 return i;
773}
774
775/* */
776
777// quoted from /sys/mail.h
778#define MSG_FROM 0
779#define MSG_SENDER 1
780#define MSG_RECIPIENT 2
781#define MSG_CC 3
782#define MSG_BCC 4
783#define MSG_SUBJECT 5
784#define MSG_DATE 6
785#define MSG_ID 7
786#define MSG_BODY 8
787
788nomask int RemoveFP(int num, string pl, string grund)
789{
790 int i;
791 string text;
792 mixed* mail;
793
794 if (!allowed()) return -1;
795 if( num<0 ) return -3;
796 if (!grund || grund=="") grund="<unbekannt>";
797 if (!pl) return -2;
798 i=remove_fp(num, pl);
799 log_file("ARCH/fp_strafen", ctime(time())+": "
800 +this_interactive()->Name(WER)+" loescht "+pl+" "+i
801 +" FPs\nGrund:"+grund+"\n");
802 if( i>0 ) {
803 text =
804 "Hallo "+capitalize(pl)+",\n\n"+
805 break_string(
806 this_interactive()->Name(WER)+" hat soeben veranlasst, dass Dir "+i
807 +" FPs abgezogen wurden.\nGrund:"+grund+"\n", 78 );
808
809 mail = allocate(9);
810 mail[MSG_FROM] = getuid(this_interactive());
811 mail[MSG_SENDER] = MUDNAME;
812 mail[MSG_RECIPIENT] = pl;
813 mail[MSG_CC]=0;
814 mail[MSG_BCC]=0;
815 mail[MSG_SUBJECT]="FP-Reduktion";
816 mail[MSG_DATE]=dtime(time());
817 mail[MSG_ID]=MUDNAME":"+time();
818 mail[MSG_BODY]=text;
819
820 "/secure/mailer"->DeliverMail(mail,1);
821 }
822 return i;
823}
824/* */
825
826private int add_fp(int num, string pl)
827{
828 int i,j,k,t,maxEP;
829 string ep;
830 ep = (MASTER->query_ep(pl) || "");
831
832 maxEP = QueryMaxEP();
833 for( i=0; i<num; i++)
834 {
835 t = random(maxEP);
836 for( j=0; j<maxEP; j++ ) {
837 if( !test_bit(ep, k=(t+j)%maxEP ) ) break;
838 }
839 if( j==maxEP ) break;
840 ep = set_bit(ep, k);
841 }
842 MASTER->update_ep(pl,ep);
843 return i;
844}
845
846nomask int AddFP(int num, string pl)
847{
848 int i;
849 if (!allowed()) return -1;
850 if ( num<0 ) return -3;
851 if (!pl) return -2;
852 i=add_fp(num, pl);
853 log_file("ARCH/fp_strafen", ctime(time())+": "
854 +this_interactive()->Name(WER)+" gibt "+pl+" "+i
855 +" FPs\n");
856
857 return i;
858}
859
860nomask int SetFP(int num, string pl)
861{
862 int maxEP;
863 string ep;
864 if (!allowed()) return -1;
865 if ( num<0 ) return -3;
866 if (!pl) return -2;
867 ep = (MASTER->query_ep(pl) || "");
868
869 maxEP = QueryMaxEP();
870 if (num<0 || num>=maxEP) return -4;
871 ep = set_bit(ep, num);
872 MASTER->update_ep(pl,ep);
873 return num;
874}
875
876nomask int ClearFP(int num, string pl)
877{
878 int maxEP;
879 string ep;
880 if (!allowed()) return -1;
881 if ( num<0 ) return -3;
882 if (!pl) return -2;
883 ep = (MASTER->query_ep(pl) || "");
884
885 maxEP = QueryMaxEP();
886 if (num<0 || num>=maxEP) return -4;
887 ep = clear_bit(ep, num);
888 MASTER->update_ep(pl,ep);
889 return num;
890}
891
892private void printep( int nr, string key, int kind, string* det )
893{
894 output+=sprintf("%4d %s %s.c %s ({ %s })\n",nr,test_bit(bonus,nr)?"b":"n",key,EP_TYPES[kind],
895 strArr(det));
896}
897
898nomask varargs int ShowPlayerEPs(string pl,string pattern)
899{
900 string ep,teststring;
901 if (!allowed()) return -1;
902 ep = (MASTER->query_ep(pl) || "");
903
904 output="";
905 if (!pattern || pattern=="")
906 teststring="%s";
907 else teststring="%s"+pattern+"%s";
Arathorn282a0552018-11-26 00:08:35 +0100908 walk_mapping( obs, function void (string fn, string* v1, int v2, int v3)
909 {
910 string unused;
911 if ( test_bit(ep, v2) && sscanf(fn, teststring, unused, unused) )
912 printep(v2, fn, v3, v1);
913 });
MG Mud User88f12472016-06-24 23:31:02 +0200914 this_interactive()->More(output);
915 return 1;
916}
917
918// Hier kommen Funktionen fuer die Levelpunkte
919// Funktion fuer Levelpunkte steht im LEPMASTER!
920
921nomask int QueryLEP(int lep) {
922 raise_error("Bitte QueryLEP() im LEPMASTER abfragen, nicht im "
923 "EPMASTER!");
924 return(-1); //never reached
925}
926
927string QueryForschung()
928{
929 int my;
930 string ret;
931
932 if ((my=QueryRealExplorationPoints(getuid(previous_object()))) < MIN_EP)
933 return "Du kennst Dich im "MUDNAME" so gut wie gar nicht aus.\n";
934
935 my *= 100;
936 int absolute = my/QueryMaxEP();
937 int relative = my/QueryAverage();
938
Zesstrad94ae712019-08-29 23:15:58 +0200939 ret = "Verglichen mit Deinen Mitspielern kennst Du Dich im "MUDNAME" ";
MG Mud User88f12472016-06-24 23:31:02 +0200940 switch(relative) {
941 case 0..10:
942 ret += "kaum";
943 break;
944 case 11..40:
945 ret += "aeusserst schlecht";
946 break;
947 case 41..56:
948 ret += "sehr schlecht";
949 break;
950 case 57..72:
951 ret += "schlecht";
952 break;
953 case 73..93:
954 ret += "unterdurchschnittlich";
955 break;
956 case 94..109:
957 ret += "durchschnittlich gut";
958 break;
959 case 110..125:
960 ret += "besser als der Durchschnitt";
961 break;
962 case 126..145:
963 ret += "recht gut";
964 break;
965 case 146..170:
966 ret += "ziemlich gut";
967 break;
968 case 171..210:
969 ret += "gut";
970 break;
971 case 211..300:
972 ret += "sehr gut";
973 break;
974 case 301..400:
975 ret += "ausserordentlich gut";
976 break;
977 case 401..500:
978 ret += "unheimlich gut";
979 break;
980 default:
981 ret += "einfach hervorragend";
982 break;
983 }
984 ret += " aus.\nAbsolut gesehen ";
985
986 switch(absolute) {
987 case 0..5:
988 ret += "kennst Du nur wenig vom "MUDNAME".";
989 break;
990 case 6..10:
991 ret += "solltest Du Dich vielleicht noch genauer umsehen.";
992 break;
993 case 11..17:
994 ret += "bist Du durchaus schon herumgekommen.";
995 break;
996 case 18..25:
997 ret += "hast Du schon einiges gesehen.";
998 break;
999 case 26..35:
1000 ret += "bist Du schon weit herumgekommen.";
1001 break;
1002 case 36..50:
1003 ret += "koenntest Du eigentlich einen Reisefuehrer herausbringen.";
1004 break;
1005 case 51..75:
1006 ret += "hast Du schon sehr viel gesehen.";
1007 break;
1008 default:
1009 ret += "besitzt Du eine hervorragende Ortskenntnis.";
1010 }
1011 return break_string(ret, 78, 0, 1);
1012}
1013
1014// Nicht jeder Magier muss den EPMASTER entsorgen koennen.
1015string NotifyDestruct(object caller) {
1016 if( (caller!=this_object() && !ARCH_SECURITY) || process_call() )
1017 {
1018 return "Du darfst den Exploration Master nicht zerstoeren!\n";
1019 }
1020 return 0;
1021}
1022