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