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