blob: 230445dfa16316f211db8a7a9d44d124130cf86d [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// player/commands.c -- alias, history and player command handling
4//
5// $Id: command.c 9576 2016-06-18 15:00:01Z Zesstra $
6#pragma strong_types
7#pragma save_types
8//#pragma range_check
9#pragma no_clone
10#pragma pedantic
11
12#define NEED_PROTOTYPES
13#include <player/command.h>
14#include <player/comm.h>
15#include <thing/properties.h>
16#include <living/moving.h>
17#include <player.h>
18#undef NEED_PROTOTYPES
19
20#include <properties.h>
21#include <language.h>
22#include <new_skills.h>
23#include <config.h>
24#include <defines.h>
25#include <wizlevels.h>
26#include <logging.h>
27#include <strings.h>
28
29#define CBLOG(x) log_file(SHELLLOG("DISABLECOMMANDS"),x,200000)
30
31#define HIST_SIZE 40
32#define EPMASTER "/secure/explorationmaster"
33
34private mapping aliases;
35private string *commands;
36private int hist_size, show_processing, histmin;
37private string default_notify_fail;
38private nosave string *history, *unparsed_args, unmodified;
39private nosave int hist_now;
40private nosave object last_command_env;
41private nosave int cmds_per_time, last_chg, max_commands, *cmd_types;
42// Datenstruktur: ({Setzer, Ablaufzeit, String/Closure})
43private nosave mixed disablecommands;
44private nosave object* syntaxdb;
45
46nomask void __set_bb(int flag);
47
48static varargs int __auswerten(string str, string intern);
49varargs int SoulComm(string str, string _verb);
50varargs mixed More(string str, int fflag, string returnto);
51static int _starts_with(string str, string start);
52static void reallocate_histbuf();
53
54private void AddHistory(string str)
55{
56 if (!stringp(str) || str=="" || str[0]=='&' || str[0]=='^' ||
57 str=="hist")
58 return;
59 if (!hist_size) return;
60 if (!pointerp(history) || sizeof(history)!=hist_size)
61 reallocate_histbuf();
62 if (sizeof(str)>=histmin && history[(hist_size+hist_now-1)%hist_size]!=str)
63 history[(hist_now++)%hist_size]=str;
64}
65
66static void create()
67{
68 last_chg=0;
69 histmin=hist_now=0;
70 Set(P_LOCALCMDS,({}));
71 Set(P_LOCALCMDS,PROTECTED,F_MODE_AS);
72 Set("_syntaxdb", SECURED|SAVE, F_MODE_AS);
73
74 show_processing=1;
75 unparsed_args=({0,0,0});
76 hist_size=HIST_SIZE;
77}
78
79static int replacedisplay(string str)
80{
81 if (!str || str=="" || !sscanf(str,"%d",show_processing))
82 printf("Unzulaessige Eingabe!\n%s 0|1|2\n",query_verb());
83 printf("Ersetzungsanzeige auf Level %d.\nLevel 0: Nichts anzeigen\n"+
84 "Level 1: Nur History-Ersetzungen anzeigen\n"+
85 "Level 2: History- und Alias-Ersetzungen anzeigen\n",show_processing);
86 if (show_processing>2&&!IS_WIZARD(ME)) show_processing=2;
87 return 1;
88}
89
90static int histmin(string str)
91{
92 int len;
93
94 if (!str||!sscanf(str,"%d",len)||len<0)
95 {
96 write("Benutzung: histmin ZAHL\nLegt die Mindestlaenge fest, die eine \
97Befehlszeile haben muss, um in den\nHistory-Puffer zu gelangen. Derzeit \
98eingestellt auf "+(string)histmin+" Zeichen.\n");
99 return 1;
100 }
101 histmin=len;
102 write("Mindestlaenge auf "+(string)len+" eingestellt.\n");
103 return 1;
104}
105
106static void reallocate_histbuf()
107{
108 int i;
109
110 history=allocate(hist_size);
111 hist_now=0;
112 for (i=0;i<hist_size;i++)
113 if (!stringp(history[i]))
114 history[i]="\n\n";
115}
116
117static int histlen(string str)
118{
119 int d;
120 if (!str||!sscanf(str,"%d",d)||d<0||d>40)
121 {
122 write("Benutzung: histlen ZAHL\nZAHL muss zwischen 0 und 40 liegen.\n");
123 printf("Deine History-Buffer-Laenge liegt bei %d Befehlen.\n",hist_size);
124 return 1;
125 }
126 hist_size=d;
127 printf("Deine History-Buffer-Laenge liegt jetzt bei %d Befehlen.\n",
128 hist_size);
129 reallocate_histbuf();
130 return 1;
131}
132
133static void initialize()
134{
135 if (!pointerp(history)||sizeof(history)!=hist_size)
136 reallocate_histbuf();
137 add_action("__auswerten","",1);
138 max_commands = EPMASTER->QueryCommands();
139 cmd_types = EPMASTER->QueryCmdTypes() || ({});
140
141 if ( !mappingp(aliases) )
142 aliases = ([]);
143
144 if ( !pointerp(commands) )
145 commands = ({});
146
147 if (QueryProp("_syntaxdb"))
148 syntaxdb = ({find_object("/secure/syntaxdb")});
149/* else if (QueryProp(P_TESTPLAYER))
150 {
151 SetProp("_syntaxdb", 1);
152 call_out(#'_notify, 2,
153 "\nDa Du als Testspieler markiert bist, wurde bei Dir "
154 "die Syntaxsammlung eingeschaltet. Du kannst dies "
155 "wieder ausschalten. (hilfe syntaxsammlung) "
156 "Es waere schoen, wenn Du es beim Testen von "
157 "Gebieten einschaltest.", 0);
158 }*/
159}
160
161static mixed _set_default_notify_fail(string s)
162{
163 if (stringp(s)&&s!="")
164 {
165 if (s[<1]!='\n') s+="\n";
166 return default_notify_fail=s;
167 }
168 else if (!s||s=="")
169 return (default_notify_fail=0);
170}
171
172static mixed _query_default_notify_fail()
173{
174 return default_notify_fail;
175}
176
177static int set_errormessage(string s)
178{
179 if (!(s=_unparsed_args()))
180 {
181 _set_default_notify_fail(0);
182 write("Standard-Fehlermeldung auf \"Wie bitte?\" gesetzt.\n");
183 } else
184 {
185 write(break_string(sprintf("Standard-Fehlermeldung auf %s gesetzt.\n",
186 s),78));
187 _set_default_notify_fail(s);
188 }
189 return 1;
190}
191
192void reconnect()
193{
194 if (!mappingp(aliases)) aliases=([]);
195
196 if ( !pointerp(commands) )
197 commands = ({});
198
199 max_commands = EPMASTER->QueryCommands();
200 cmd_types = EPMASTER->QueryCmdTypes() || ({});
201}
202
203static int show_hist()
204{
205 int i;
206 string comm;
207
208 tell_object( ME, "Die History-Liste enthaelt folgende Kommandos:\n" );
209
210 for( i = 0; i < hist_size; i++ )
211 if ((comm=history[(hist_now+i)% hist_size])!= "\n\n")
212 tell_object( ME, " &"+(hist_now+i-hist_size)+"/-"+ (hist_size-i-1)
213 +"\t= "+comm+"\n");
214 return 1;
215}
216
217static string present_alias(mixed *ali)
218{
219 int j,k;
220 string s,s2;
221
222 for (s="",j=sizeof(ali)-1;j>=0;j--)
223 if (intp(ali[j]))
224 if ((k=ali[j])<0)
225 s="$"+(k==-1?"":(string)-k)+"*"+s;
226 else
227 s="$"+(string)k+s;
228 else
229 {
230 s2=implode(explode(ali[j],"\\"),"\\\\");
231 s=implode(explode(s2,"$"),"\\$")+s;
232 }
233 return s;
234}
235
236#define ALIFORMAT ({" %s\t= %s", "alias %s %s"})[display_as_aliascommand]
237// Ich weiss, den Variablennamen im define zu haben ist unfein, aber das
238// macht es im Code dann angenehm uebersichtlich. -HrT
239
240static int query_aliases(int display_as_aliascommand)
241{
242 int i;
243 string *a,*ali;
244
245 if(i=sizeof(ali=sort_array(m_indices(aliases),#'<))) //')))
246 {
247 for(a=({}),i--; i>=0; i--)
248 a+=({sprintf(ALIFORMAT, ali[i], present_alias( aliases[ali[i]] ) ) });
249 More("Du hast folgende Aliase definiert:\n"+implode(a,"\n"));
250 }
251 else
252 write("Du hast keine Aliase definiert.\n");
253 return 1;
254}
255
256static int
257_starts_with(string str, string start)
258{
259 return (sizeof(start)>sizeof(str) ? 0
260 : str[0..sizeof(start)-1]==start);
261}
262
263static int alias(string str)
264{
265 string command;
266 string *tmp,um,*hits;
267 int num, l, pos, cont;
268 int display_as_aliascommand;
269
270 if (unmodified&&unmodified!="")
271 um=implode(old_explode(unmodified," ")[1..]," ");
272 if (um=="") um=0;
273 if(!(str=um||_unparsed_args()) || str=="*") return query_aliases(0);
274
275 if (str=="-a" || strstr(str, "-a ")==0 ) {
276 str=str[2..];
277 if (str && str!="" && str[0]==' ') str=str[1..];
278 if (!str || str=="" || str=="*") return query_aliases(1);
279 display_as_aliascommand=1;
280 }
281
282 if ((pos=member(str,' '))<0) // 1 Arg only
283 {
284 if ((tmp=aliases[str]))
285 printf(ALIFORMAT+"\n",str,present_alias(tmp));
286 else
287 if (str[<1]=='*')
288 {
289 str=str[0..<2];
290 hits=filter(m_indices(aliases), #'_starts_with, str);
291 if (!sizeof(hits))
292 {
293 printf("Du hast kein Alias, das mit \"%s\" anfaengt.\n", str);
294 return 1;
295 }
296 hits=sort_array(hits, #'>);
297 for (l=sizeof(hits); l--;)
298 hits[l]=sprintf(ALIFORMAT, hits[l], present_alias(aliases[hits[l]]));
299 More("Folgende Aliase beginnen mit \""+str+"\":\n"+implode(hits,"\n"));
300 }
301 else
302 printf("Du hast kein Alias \"%s\" definiert.\n",str);
303 return 1;
304 }
305 if (!pos)
306 {
307 write("Fehler: Blanc am Alias-Anfang\n");
308 return 1;
309 }
310 if ((command=str[0..pos-1])=="unalias")
311 {
312 write
313 ("Es nicht moeglich, den Befehl unalias zu ueberladen (waer dumm :))\n");
314 return 1;
315 }
316 if ((command=str[0..pos-1])=="*")
317 {
318 write
319 ("Es nicht moeglich, den Befehl \"*\" zu ueberladen.\n");
320 return 1;
321 }
322
323 str=str[pos+1..],tmp=({});
324 while (l=sizeof(str)) {
325 pos=0,cont=1;
326 while (cont) {
327 if (pos<l) {
328 if(str[pos]=='\\') {
329 str=str[0..pos-1]+str[pos+1..];
330 l--;
331 } else {
332 if (str[pos]=='&' || str[pos]=='$') {
333 cont=0;
334 if (pos>0) {
335 tmp+=({str[0..pos-1]});
336 }
337 if (pos==l-1) {
338 printf("Fehler: %c am Zeilenende\n",str[pos]);
339 return 1;
340 }
341 if ((num=str[++pos])=='*') {
342 num=1;
343 pos--;
344 } else {
345 num-='0';
346 }
347 if (num<0 || num>9) {
348 printf("Fehler: Nach %c muss Ziffer oder * folgen\n",
349 str[pos-1]);
350 return 1;
351 }
352 if ((str=str[pos+1..])!=""&&str[0]=='*') {
353 str=str[1..];
354 num=-num;
355 }
356 tmp+=({num});
357 }
358 }
359 pos++;
360 } else {
361 cont=0;
362 if (str!="") tmp+=({str});
363 str="";
364 }
365 }
366 }
367 if ((!aliases[command]) && (sizeof(aliases)>2000))
368 printf("Du hast schon genuegend Aliase definiert!\n");
369 else
370 {
371 aliases[command]=tmp;
372 printf("Neues Alias: %s\t= %s\n",command,present_alias(tmp));
373 }
374 return 1;
375}
376
377static int unalias(string str) {
378 int i;
379 string *als,um;
380
381 if (unmodified&&unmodified!="")
382 um=implode(old_explode(unmodified," ")[1..]," ");
383 if (um=="") um=0;
384 if ( !(str=um || _unparsed_args())) return 0;
385 if (str == "*.*" || str == "*") {
386 write(break_string(
387 "Versuchs mal mit 'unalias .*', wenn Du wirklich alle Alias entfernen "
388 "willst.",78));
389 return 1;
390 }
391 if (!member(aliases,str)) {
392 als=regexp(m_indices(aliases),("^"+str+"$"));
393 if (!(i=sizeof(als))) {
394 write("So ein Alias hast Du nicht definiert.\n");
395 return 1;
396 }
397 for (--i;i>=0;i--)
398 m_delete(aliases,als[i]);
399 write(break_string(("Du entfernst folgende Aliase: "+
400 implode(als," ")+".\n"),75));
401 return 1;
402 }
403 m_delete(aliases,str);
404 write("Du entfernst das Alias \""+str+"\".\n");
405 return 1;
406}
407
408varargs string _unparsed_args(int level)
409{
410 return unparsed_args[level];
411}
412
413#define ARTIKEL ({"das","der","die","des","dem","den","ein","eine","einer",\
414 "eines"})
415
416#define TRENNER ({"in","aus","ueber","auf","unter","mit","durch","fuer",\
417 "von","vom","im","aufs","ein","weg","zurueck"})
418
419static string _single_spaces(string str)
420{
421 return regreplace(str, " *", " ", 1);
422}
423
424static mixed _return_args(string str)
425{
426 string *t,*t2,verb,s2;
427 int i,l,j,l2;
428
429 t=explode(trim(str,TRIM_BOTH)," ");
430 verb=t[0];
431 t = t[1..];
432 if (!sizeof(t))
433 {
434 unparsed_args[0]=unparsed_args[1]=unparsed_args[2]=0;
435 return str=verb;
436 }
437 else
438 str = unparsed_args[0] = implode(t, " ");
439
440 str=unparsed_args[1]=lower_case(_single_spaces(str));
441 t=regexplode(str,"\\<im\\>|\\<ins\\>");
442 for (i=1;i<sizeof(t);i+=2) t[i]="in";
443 t=regexplode(implode(t,""),"[\\,\\!\\:][\\,\\!\\:]*");
444 l=sizeof(t);
445 for(i=1;i<l;i+=2) t[i]="";
446 t=old_explode(implode(t,"")," ")-({""});
447 for (i=sizeof(t)-2;i>=0;i--)
448 {
449 if (member(ARTIKEL,t[i])>=0)
450 t=t[0..i-1]+t[i+1..];
451 }
452 unparsed_args[2]=implode(t," ");
453 t=regexplode((str=implode(t," ")),"[0-9][0-9]*\\.");
454 if ((l=sizeof(t))>2)
455 {
456 i=1;
457 while (i<l-1)
458 {
459 t[i]=" "+t[i][0..<2]+" ";
460 if ((l2=sizeof(t2=old_explode(t[i+1]," ")))<2)
461 t[i+1]+=t[i];
462 else
463 {
464 for (j=1;j<l2;j++)
465 {
466 if (member(TRENNER,t2[j])>=0)
467 {
468 t2[j-1]+=t[i];
469 l2=0;
470 }
471 }
472 if (!l2)
473 t[i+1]=implode(t2," ");
474 else
475 t[i+1]+=t[i];
476 }
477 t[i]="";
478 i+=2;
479 }
480 str=_single_spaces(verb+" "+implode(t," "));
481 if (str[<1]==' ') str=str[0..<2];
482 } else str=verb+(str==""?"":" "+str);
483 if (show_processing>2)
484 printf("-> {%s}\n",str);
485 return str;
486}
487
488static void decay_average()
489{
490 if (absolute_hb_count()-last_chg>14)
491 {
492 last_chg=absolute_hb_count()-last_chg;
493 if (last_chg>3000)
494 last_chg=absolute_hb_count(),cmds_per_time=0;
495 else
496 {
497 while (last_chg>14)
498 cmds_per_time=cmds_per_time*9/10, last_chg-=15;
499 last_chg=absolute_hb_count()-last_chg;
500 }
501 }
502}
503
504private void DelayPreparedSpells() {
505 mixed ps;
506
507 if (pointerp(ps=QueryProp(P_PREPARED_SPELL))
508 && sizeof(ps)>=1 && intp(ps[0])) {
509 ps[0]++;
510 SetProp(P_PREPARED_SPELL,ps);
511 write("Die Ausfuehrung Deines vorbereiteten Spruches wird verzoegert.\n");
512 } else if (ps) {
513 SetProp(P_PREPARED_SPELL,0);
514 }
515}
516
517static mixed bb;
518#ifndef BBMASTER
519#define BBMASTER "/secure/bbmaster"
520#endif
521
522/** Interpretiert Aliase und History-Kommandos
523 Eigentlich muesste hier noch die Umwandlung der Sonderzeichen
524 verschiedener Zeichensaetze mit convert_charset gemacht werden,
525 aber noch gibt es keine Moeglichkeit, den vom Spieler genutzten
526 Zeichensatz zu identifizieren.
527 \param[in] str string - Kommando des Spielers
528 \return interpretiertes Alias bzw. korrektes Kommando aus der History
529*/
530private string parsecommand(string str)
531{
532 if (str[0]=='\\')
533 {
534 // Kommando soll nicht interpretiert werden
535 return str[1..];
536 }
537 else if (str[0]=='&')
538 {
539 // Kommando aus der History
540 string cmd = str[1..];
541 int cmd_size = sizeof(cmd);
542 int cmd_found = 0;
543 if (cmd_size)
544 {
545 // Test ob &<text> etwas findet
546 for (int i=0;i<hist_size-1 && !cmd_found;i++)
547 {
548 int idx = (hist_size-i+hist_now-1)%hist_size;
549 if (history[idx][0..cmd_size-1]==cmd)
550 {
551 str = history[idx];
552 cmd_found = 1;
553 }
554 if (cmd_found)
555 {
556 if (show_processing)
557 printf("[%s]\n",str);
558 }
559 }
560 }
561 if (!cmd_found)
562 {
563 // Test, ob &<nr> klappt
564 int nummer;
565 if (str=="&&")
566 str = "&-0";
567 if (sscanf(str,"&%d",nummer))
568 {
569 if (nummer<0 || (!nummer && str[1]=='-'))
570 {
571 if (nummer<-(hist_size-1))
572 nummer=-1;
573 else
574 nummer=(hist_now+nummer-1+hist_size)%hist_size;
575 }
576 else
577 {
578 if (nummer>hist_now || hist_now-nummer>hist_size)
579 nummer=-1;
580 else
581 nummer=nummer%hist_size;
582 }
583 if (nummer<0
584 || ( (cmd=history[nummer]) =="\n\n") )
585 notify_fail("Der Befehl ist nicht in der History!\n");
586 else
587 {
588 str = cmd;
589 if (show_processing)
590 printf("[%s]\n",str);
591 }
592 }
593 }
594 }
595 switch (str)
596 {
597 case "n": return "norden";
598 case "s": return "sueden";
599 case "w": return "westen";
600 case "o": return "osten";
601 case "nw": return "nordwesten";
602 case "sw": return "suedwesten";
603 case "so": return "suedosten";
604 case "no": return "nordosten";
605 case "ob": return "oben";
606 case "u": return "unten";
607 }
608 // Test auf Alias
609 string output = "";
610 string* input = explode(str," ");
611 int input_size = sizeof(input);
612 mixed alias = aliases[input[0]];
613 if (!alias)
614 return str;
615 foreach (mixed a:alias)
616 {
617 if (!intp(a))
618 output += a;
619 else
620 {
621 if (a >= 0)
622 {
623 if (input_size > a)
624 output += input[a];
625 }
626 else
627 {
628 a = -a;
629 if (input_size > a)
630 output += implode(input[a..]," ");
631 }
632 }
633 }
634 output = _single_spaces(output);
635 str = trim(output,TRIM_RIGHT);
636 if (show_processing>1)
637 printf("[%s]\n",str);
638 return str;
639}
640
641/** Behandelt alle Sonderfaelle der Eingabe des Spielers
642 Alle Befehle des Spielers, die nicht durch Objekte behandelt
643 werden sollen, werden hier erkannt und ausgefuehrt.
644 Dazu gehoert auch die Interpretation von Aliases und History-
645 befehlen.
646 \param[in] str string: Kommando des Spielers
647 \return auszufuehrendes Kommando
648 oder 0 fuer ein nicht interpretierbares Kommando
649 oder 1 fuer ein bereits durchgefuehrtes Kommando
650*/
651mixed modify_command(string str)
652{
653
654 if (extern_call() && previous_object() &&
655 (previous_object()!=this_object() || process_call()) )
656 {
657 return 0;
658 }
659
660 // Leerzeichen an den Enden abschneiden.
661 str = trim(str, TRIM_BOTH);
662
663 if (bb)
664 BBMASTER->BBWrite(trim(str,TRIM_RIGHT,"\n"), 0);
665
666 decay_average();
667 cmds_per_time+=10000;
668
669 unparsed_args[0]=unparsed_args[1]=unparsed_args[2]=unmodified="";
670
671 if (!sizeof(str)) return "";
672
673 // Kommando wird geparst
674 unmodified=parsecommand(str);
675
676 // Environment schonmal merken.
677 last_command_env=environment();
678
679 if (unmodified == "")
680 return "";
681 // Kommando in History merken, auch wenn es im Kommandoblock abgebrochen
682 // wird.
683 AddHistory(unmodified);
684
685 // pruefen, ob Kommandoblock gesetzt ist.
686 // (Fuer Magier mit mschau ein wird das ignoriert.)
687 // BTW: Es wird absichtlich nicht das Ergebnis der Closure zurueckgegeben,
688 // sonst wuerde man beliebigen Objekten nicht nur das Abbrechen, sondern
689 // auch das Aendern von Kommandos ermoeglichen.
690 if (disablecommands && !IS_LEARNING(ME) )
691 {
692 if (disablecommands[B_TIME] >= time()
693 && objectp(disablecommands[B_OBJECT]))
694 {
695 // disablecommands valid
696 // hart-kodierte Ausnameliste pruefen
697 if ( member(({"mrufe","mschau","bug","idee","typo","detail"}),
698 explode(str," ")[0]) == -1)
699 {
700 if (closurep(disablecommands[B_VALUE]))
701 {
702 if (funcall(disablecommands[B_VALUE],_return_args(unmodified)))
703 {
704 // Non-zero Closure-Ergebnis, Abbruch. Die gerufene Funktion ist
705 // fuer eine geeignete Meldung an den Spieler verantwortlich.
706 return 1;
707 }
708 }
709 // wenn Text, dann auch pruefen, ob das Kommandoverb in den Ausnahmen
710 // steht. (query_verb() geht leider hier noch nicht.)
711 else if (stringp(disablecommands[B_VALUE])
712 && member(disablecommands[B_EXCEPTIONS],
713 explode(str," ")[0]) == -1)
714 {
715 // meldung ausgeben...
716 tell_object(PL, disablecommands[B_VALUE]);
717 // und Ende...
718 return 1;
719 }
720 }
721 }
722 else disablecommands=0;
723 }
724
725 // Verfolger direkt ins Env reinholen.
726 if (remove_call_out("TakeFollowers")>=0)
727 catch(TakeFollowers();publish);
728
729 DelayPreparedSpells();
730
731 // Historyeintrag korrigieren
732 if (unmodified[0]=='^')
733 {
734 string *oldnew,pre,post;
735 if (sizeof(oldnew=explode(unmodified,"^"))>2)
736 {
737 int hist_idx = (hist_now-1)%hist_size;
738 sscanf(history[hist_idx],"%s"+oldnew[1]+"%s", pre, post);
739 unmodified = pre+oldnew[2]+post;
740 if (show_processing)
741 write("["+unmodified+"]\n");
742 // korrigiertes Kommando natuerlich auch in die History.
743 AddHistory(unmodified);
744 }
745 }
746
747 if( bb )
748 BBMASTER->BBWrite(" -> " + unmodified, 1);
749
750 if (show_processing>1)
751 printf("[%s]\n",unmodified);
752
753 mixed ret = _return_args(unmodified);
754
755 // wenn Spieler eingewilligt hat und die SyntaxDB geladen ist, Befehl
756 // dorthin melden.
757 if (syntaxdb)
758 {
759 if (!objectp(syntaxdb[0]))
760 syntaxdb[0] = find_object("/secure/syntaxdb");
761 if (syntaxdb[0])
762 catch(syntaxdb[0]->start_cmd(unmodified);nolog);
763 }
764 return ret;
765}
766
767static int do_list(string str)
768{
769 string *cmdlist;
770 int i;
771
772 if (!QueryProp(P_WANTS_TO_LEARN))
773 return 0;
774 cmdlist=old_explode(_unparsed_args()||"",";")-({ "" });
775 for (i=0;i<sizeof(cmdlist);i++)
776 {
777 cmdlist[i]=implode(old_explode(cmdlist[i]," ")-({}), " ");
778 if (show_processing)
779 write("["+cmdlist[i]+"]\n");
780 command(cmdlist[i]);
781 }
782 return 1;
783}
784
785//falls die aliasliste kaputt ist ...
786
787int unalias_all()
788{
789 if (IS_ELDER(this_interactive())) aliases=([]);
790 return 1;
791}
792
793object _query_last_command_env()
794{
795 return last_command_env;
796}
797
798int _query_show_alias_processing()
799{
800 return show_processing;
801}
802
803int _query_histmin()
804{
805 return histmin;
806}
807
808varargs void AddAction(mixed fun, mixed cmd, int flag, int lvl)
809{
810 int i;
811 mixed *cmds;
812
813 log_file( "ARCH/ADD_ACTION", sprintf(
814 "%s:\n TO: %O TP: %O PO: %O\n fun: %O cmd: %O flag: %O lvl: %O",
815 dtime(time()), this_object(), this_player(), previous_object(),
816 fun, cmd, flag, lvl));
817
818 if (!(cmds=Query(P_LOCALCMDS))) cmds=({});
819
820 if (!pointerp(cmd)) cmd=({cmd});
821
822 for (i = sizeof(cmd)-1; i>=0; i--)
823 cmds += ({({ cmd[i] , fun, flag, lvl})});
824
825 Set(P_LOCALCMDS, cmds);
826}
827
828static int auswerten(mixed cmd, string str)
829{
830 if (closurep(cmd))
831 return funcall(cmd,str);
832 if (stringp(cmd))
833 return call_other(this_object(),cmd,str);
834 return 0;
835}
836
837static varargs int __auswerten(string str, string intern)
838{
839 string verb;
840 mixed *cmd, cmds;
841 int i,ret,lvl,l,vl;
842
843 if (!intern)
844 verb=query_verb();
845 else
846 verb=intern;
847 lvl=query_wiz_level(ME);
848 vl=sizeof(verb);
849 cmds=QueryProp(P_LOCALCMDS);
850
851 for(i=sizeof(cmds)-1;i>=0;i--)
852 {
853 cmd=cmds[i],l=sizeof(cmd[0]);
854 if (cmd[0]==verb[0..l-1] && cmd[3]<=lvl && (cmd[2]||vl==l) &&
855 (ret=auswerten(cmd[1],str)))
856 return ret;
857 }
858 // An dieser Stelle gibt es hier und vermutlich nirgendwo anders etwas, was
859 // dieses Kommando als gueltig betrachtet. Wir informieren ggf. die
860 // Syntax-DB. (Achtung: wenn jemand ne add_action() im Spielerobjekt
861 // einbaut, die vor dieser eingetragen wird, ist die Annahme ggf. falsch.)
862 // wenn Spieler eingewilligt hat und die SyntaxDB geladen ist, Befehl
863 // dorthin melden.
864 if (syntaxdb)
865 {
866 if (!objectp(syntaxdb[0]))
867 syntaxdb[0] = find_object("/secure/syntaxdb");
868 if (syntaxdb[0])
869 catch(syntaxdb[0]->cmd_unsuccessful();nolog);
870 }
871
872 return 0;
873}
874
875public void syntax_log_ep(int type)
876{
877 // wenn Spieler eingewilligt hat und die SyntaxDB geladen ist, Befehl
878 // dorthin melden.
879 if (syntaxdb && syntaxdb[0])
880 {
881 catch(syntaxdb[0]->LogEP(type);nolog);
882 }
883}
884
885static mixed _query_localcmds()
886{
887 mixed *l;
888
889 l=Query(P_LOCALCMDS);
890 if (!pointerp(l))
891 l=({});
892 return ({
893 ({"ali","alias",0,0}),
894 ({"alias","alias",0,0}),
895 ({"unali","unalias",1,0}),
896 ({"histmin","histmin",0,0}),
897 ({"histlen","histlen",0,0}),
898 ({"hist","show_hist",0,0}),
899 ({"history","show_hist",0,0}),
900 ({"do","do_list",0,LEARNER_LVL}),
901 ({"ersetzungsanzeige","replacedisplay",0,0}),
902 ({"syntaxsammlung","collect_cmds",0,0}),
903 ({"fehlermeldung","set_errormessage",0,SEER_LVL}),
904 })+l;
905}
906
907static int collect_cmds(string cmd)
908{
909 if (!stringp(cmd))
910 {
911 _notify("Mit diesem Befehl kannst Du mithelfen, Syntaxen im MG zu "
912 "verbessern. Wenn Du einverstanden bist, speichern wir "
913 "anonym (d.h. ohne Deinen Charnamen), welche Deiner Befehle "
914 "erfolgreich und nicht erfolgreich waren. Uebliche "
915 "Kommunikationsbefehle werden dabei nicht gespeichert.",
916 0);
917 _notify("Mit 'syntaxsammlung ja' kannst Du die Speicherung einschalten, "
918 "mit 'syntaxsammlung nein' kannst Du sie ausschalten.",0);
919 _notify("Deine Befehle werden zur Zeit"
920 + (QueryProp("_syntaxdb") ? " " : " NICHT ")
921 + "gespeichert.", 0);
922 }
923 else if (cmd == "ja")
924 {
925 SetProp("_syntaxdb", 1);
926 _notify("Ab jetzt werden Deine Befehle gespeichert. Vielen Dank!", 0);
927 }
928 else
929 {
930 SetProp("_syntaxdb", 0);
931 _notify("Ab jetzt werden Deine Befehle NICHT gespeichert.", 0);
932 }
933 return 1;
934}
935
936int _query_command_average()
937{
938 decay_average();
939 return cmds_per_time;
940}
941
942nomask void __set_bb(int flag) {
943 if( previous_object()!=find_object(BBMASTER) || process_call() )
944 return;
945 bb=flag;
946}
947
948
949nomask public void countCmds( int type, string key )
950{
951 string tmp;
952
953 if ( this_player() != this_interactive()
954 || this_interactive() != this_object()
955 || member( cmd_types, type ) < 0 )
956 return;
957
958 tmp = sprintf( "%d\n%s", type, key );
959
960 commands -= ({ tmp });
961 commands += ({ tmp });
962 commands = commands[0..max_commands-1];
963}
964
965
966nomask public string *getCmds()
967{
968 string *tmp;
969
970 if ( previous_object() != find_object(BBMASTER) )
971 return ({});
972
973 tmp = commands;
974 commands = ({});
975
976 return tmp;
977}
978
979/*
980 * Force the monster to do a command. The force_us() function isn't
981 * always good, because it checks the level of the caller, and this function
982 * can be called by a room.
983 */
984int command_me(string cmd)
985{
986 if (IS_LEARNER(ME))
987 {
988 if (!this_interactive() || !previous_object())
989 return 0;
990 if( geteuid(ME)!=geteuid(this_interactive())
991 || geteuid(ME)!=geteuid(previous_object()) )
992 {
993 if( query_wiz_level(ME)<query_wiz_level(previous_object()))
994 tell_object(ME,previous_object()->name()+" zwingt Dich zu: "
995 + cmd + ".\n");
996 else
997 {
998 tell_object(ME,previous_object()->name()
999 + " versucht, Dich zu " + cmd + " zu zwingen.\n" );
1000 return 0;
1001 }
1002 }
1003 }
1004 return command(cmd);
1005}
1006
1007
1008static mixed _query_p_lib_disablecommands() {
1009 // abgelaufen oder Objekt zerstoert? Weg damit.
1010 if (pointerp(disablecommands)
1011 && (disablecommands[B_TIME] < time()
1012 || !objectp(disablecommands[B_OBJECT])) )
1013 return(disablecommands = 0);
1014
1015 // sonst Kopie zurueck (copy(0) geht)
1016 return(copy(disablecommands));
1017}
1018
1019static mixed _set_p_lib_disablecommands(mixed data) {
1020
1021 // setzendes Objekt ermitteln, da diese Funktion nur per SetProp() gerufen
1022 // werden sollte (!), ist das PO(1);
1023 object origin = previous_object(1);
1024 // wenn nicht existent, direkt abbruch
1025 if (!objectp(origin))
1026 return _query_p_lib_disablecommands();
1027
1028 // Prop loeschen? Explizit loeschen darf jeder, allerdings nicht direkt
1029 // ungeprueft ueberschreiben.
1030 if (!data) {
1031 return (disablecommands = 0 );
1032 }
1033 // mal direkt buggen bei falschen Datentyp, damits auffaellt.
1034 if (!pointerp(data) || sizeof(data) < 2 || !intp(data[0])
1035 || (!stringp(data[1]) && !closurep(data[1]))
1036 || (sizeof(data) >= 3 && !pointerp(data[2])) )
1037 raise_error(sprintf(
1038 "Wrong data type for P_DISABLE_COMMANDS. Expected Array with "
1039 "2 or 3 elements (int, string|closure, [string*]), got %.25O\n",
1040 data));
1041
1042 // Wenn abgelaufen oder gleiches Objekt wie letztes Mal: eintragen.
1043 if (!disablecommands || (disablecommands[B_TIME] < time()
1044 || !objectp(disablecommands[B_OBJECT])
1045 || disablecommands[B_OBJECT] == origin) ) {
1046 // Loggen nur, wenn eine Closure eingetragen wird. Reduziert den
1047 // Logscroll und Strings haben deutlich weniger Missbrauchspotential.
1048 if (closurep(data[1])) {
1049 CBLOG(sprintf("[%s] CB gesetzt von %O, gueltig bis %s, Daten: %O\n",
1050 strftime("%Y%m%d-%H:%M:%S"),origin,
1051 strftime("%Y%m%d-%H:%M:%S",data[0]),
1052 (stringp(data[1]) ? regreplace(data[1],"\n","\\n",0)
1053 : data[1])));
1054 }
1055 if (sizeof(data)+1 <= B_EXCEPTIONS)
1056 disablecommands = ({ origin, data[0], data[1], ({}) });
1057 else
1058 disablecommands = ({ origin, data[0], data[1], data[2] });
1059 return(copy(disablecommands));
1060 }
1061
1062 return(_query_p_lib_disablecommands());
1063}
1064
1065static mixed _set__syntaxdb(mixed v)
1066{
1067 Set("_syntaxdb", v, F_VALUE);
1068 if (v)
1069 syntaxdb = ({find_object("/secure/syntaxdb")});
1070 else
1071 syntaxdb = 0;
1072 return QueryProp("_syntaxdb");
1073}
1074