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