blob: 9616281a9e0dc9dfc7212fb9a2107b880f33bf9b [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{
Zesstraf86ed742019-04-25 18:37:27 +0200265 string commandverb;
MG Mud User88f12472016-06-24 23:31:02 +0200266 string *tmp,um,*hits;
267 int num, l, pos, cont;
268 int display_as_aliascommand;
269
Zesstraf86ed742019-04-25 18:37:27 +0200270 // unbearbeitetes Kommando ohne Verb ermitteln (auch ohne Trim an Anfang und
271 // Ende)
272 if (unmodified && unmodified!="")
MG Mud User88f12472016-06-24 23:31:02 +0200273 um=implode(old_explode(unmodified," ")[1..]," ");
Zesstraf86ed742019-04-25 18:37:27 +0200274
MG Mud User88f12472016-06-24 23:31:02 +0200275 if (um=="") um=0;
Zesstraf86ed742019-04-25 18:37:27 +0200276 if( !(str = um||_unparsed_args()) || str=="*")
277 return query_aliases(0);
MG Mud User88f12472016-06-24 23:31:02 +0200278
279 if (str=="-a" || strstr(str, "-a ")==0 ) {
280 str=str[2..];
281 if (str && str!="" && str[0]==' ') str=str[1..];
282 if (!str || str=="" || str=="*") return query_aliases(1);
283 display_as_aliascommand=1;
284 }
285
Zesstraf86ed742019-04-25 18:37:27 +0200286 pos=member(str,' ');
287 if (pos < 0) // Nur 1 Arg, Alias abfragen
MG Mud User88f12472016-06-24 23:31:02 +0200288 {
Zesstraf86ed742019-04-25 18:37:27 +0200289 if ((tmp=aliases[str])) // genau eins angegebenen
MG Mud User88f12472016-06-24 23:31:02 +0200290 printf(ALIFORMAT+"\n",str,present_alias(tmp));
Zesstraf86ed742019-04-25 18:37:27 +0200291 else if (str[<1]=='*') // * am Ende, alle ausgeben, die passend anfangen
292 {
293 str=str[0..<2];
294 hits=filter(m_indices(aliases), #'_starts_with, str);
295 if (!sizeof(hits))
MG Mud User88f12472016-06-24 23:31:02 +0200296 {
Zesstraf86ed742019-04-25 18:37:27 +0200297 printf("Du hast kein Alias, das mit \"%s\" anfaengt.\n", str);
298 return 1;
MG Mud User88f12472016-06-24 23:31:02 +0200299 }
Zesstraf86ed742019-04-25 18:37:27 +0200300 hits=sort_array(hits, #'>);
301 for (l=sizeof(hits); l--;)
302 hits[l]=sprintf(ALIFORMAT, hits[l], present_alias(aliases[hits[l]]));
303 More("Folgende Aliase beginnen mit \""+str+"\":\n"+implode(hits,"\n"));
304 }
305 else // Nix gefunden
MG Mud User88f12472016-06-24 23:31:02 +0200306 printf("Du hast kein Alias \"%s\" definiert.\n",str);
307 return 1;
308 }
Zesstraf86ed742019-04-25 18:37:27 +0200309
MG Mud User88f12472016-06-24 23:31:02 +0200310 if (!pos)
311 {
Zesstraf86ed742019-04-25 18:37:27 +0200312 write("Fehler: Leerzeichen am Alias-Anfang\n");
MG Mud User88f12472016-06-24 23:31:02 +0200313 return 1;
314 }
Zesstraf86ed742019-04-25 18:37:27 +0200315 // Kommandoverb alles bis zum ersten " ".
316 commandverb=str[0..pos-1];
317 if (commandverb=="unalias")
MG Mud User88f12472016-06-24 23:31:02 +0200318 {
319 write
320 ("Es nicht moeglich, den Befehl unalias zu ueberladen (waer dumm :))\n");
321 return 1;
322 }
Zesstraf86ed742019-04-25 18:37:27 +0200323 if (commandverb=="*")
MG Mud User88f12472016-06-24 23:31:02 +0200324 {
325 write
326 ("Es nicht moeglich, den Befehl \"*\" zu ueberladen.\n");
327 return 1;
328 }
329
330 str=str[pos+1..],tmp=({});
331 while (l=sizeof(str)) {
332 pos=0,cont=1;
333 while (cont) {
334 if (pos<l) {
335 if(str[pos]=='\\') {
336 str=str[0..pos-1]+str[pos+1..];
337 l--;
338 } else {
339 if (str[pos]=='&' || str[pos]=='$') {
340 cont=0;
341 if (pos>0) {
342 tmp+=({str[0..pos-1]});
343 }
344 if (pos==l-1) {
345 printf("Fehler: %c am Zeilenende\n",str[pos]);
346 return 1;
347 }
348 if ((num=str[++pos])=='*') {
349 num=1;
350 pos--;
351 } else {
352 num-='0';
353 }
354 if (num<0 || num>9) {
355 printf("Fehler: Nach %c muss Ziffer oder * folgen\n",
356 str[pos-1]);
357 return 1;
358 }
359 if ((str=str[pos+1..])!=""&&str[0]=='*') {
360 str=str[1..];
361 num=-num;
362 }
363 tmp+=({num});
364 }
365 }
366 pos++;
367 } else {
368 cont=0;
369 if (str!="") tmp+=({str});
370 str="";
371 }
372 }
373 }
Zesstraf86ed742019-04-25 18:37:27 +0200374 if ((!aliases[commandverb]) && (sizeof(aliases)>2000))
MG Mud User88f12472016-06-24 23:31:02 +0200375 printf("Du hast schon genuegend Aliase definiert!\n");
376 else
377 {
Zesstraf86ed742019-04-25 18:37:27 +0200378 aliases[commandverb]=tmp;
379 printf("Neues Alias: %s\t= %s\n",commandverb, present_alias(tmp));
MG Mud User88f12472016-06-24 23:31:02 +0200380 }
381 return 1;
382}
383
384static int unalias(string str) {
Zesstraf86ed742019-04-25 18:37:27 +0200385 int i, familymode;
MG Mud User88f12472016-06-24 23:31:02 +0200386 string *als,um;
387
388 if (unmodified&&unmodified!="")
389 um=implode(old_explode(unmodified," ")[1..]," ");
390 if (um=="") um=0;
Zesstraf86ed742019-04-25 18:37:27 +0200391 if ( !(str=um || _unparsed_args()))
392 return 0;
393
MG Mud User88f12472016-06-24 23:31:02 +0200394 if (str == "*.*" || str == "*") {
395 write(break_string(
396 "Versuchs mal mit 'unalias .*', wenn Du wirklich alle Alias entfernen "
397 "willst.",78));
398 return 1;
399 }
Zesstraf86ed742019-04-25 18:37:27 +0200400 if (!member(aliases,str))
401 {
MG Mud User88f12472016-06-24 23:31:02 +0200402 als=regexp(m_indices(aliases),("^"+str+"$"));
Zesstraf86ed742019-04-25 18:37:27 +0200403 if (!(i=sizeof(als)))
404 {
MG Mud User88f12472016-06-24 23:31:02 +0200405 write("So ein Alias hast Du nicht definiert.\n");
406 return 1;
407 }
408 for (--i;i>=0;i--)
409 m_delete(aliases,als[i]);
410 write(break_string(("Du entfernst folgende Aliase: "+
411 implode(als," ")+".\n"),75));
412 return 1;
413 }
414 m_delete(aliases,str);
415 write("Du entfernst das Alias \""+str+"\".\n");
416 return 1;
417}
418
419varargs string _unparsed_args(int level)
420{
421 return unparsed_args[level];
422}
423
424#define ARTIKEL ({"das","der","die","des","dem","den","ein","eine","einer",\
425 "eines"})
426
427#define TRENNER ({"in","aus","ueber","auf","unter","mit","durch","fuer",\
428 "von","vom","im","aufs","ein","weg","zurueck"})
429
430static string _single_spaces(string str)
431{
432 return regreplace(str, " *", " ", 1);
433}
434
435static mixed _return_args(string str)
436{
437 string *t,*t2,verb,s2;
438 int i,l,j,l2;
439
440 t=explode(trim(str,TRIM_BOTH)," ");
441 verb=t[0];
442 t = t[1..];
443 if (!sizeof(t))
444 {
445 unparsed_args[0]=unparsed_args[1]=unparsed_args[2]=0;
446 return str=verb;
447 }
448 else
449 str = unparsed_args[0] = implode(t, " ");
450
451 str=unparsed_args[1]=lower_case(_single_spaces(str));
452 t=regexplode(str,"\\<im\\>|\\<ins\\>");
453 for (i=1;i<sizeof(t);i+=2) t[i]="in";
454 t=regexplode(implode(t,""),"[\\,\\!\\:][\\,\\!\\:]*");
455 l=sizeof(t);
456 for(i=1;i<l;i+=2) t[i]="";
457 t=old_explode(implode(t,"")," ")-({""});
458 for (i=sizeof(t)-2;i>=0;i--)
459 {
460 if (member(ARTIKEL,t[i])>=0)
461 t=t[0..i-1]+t[i+1..];
462 }
463 unparsed_args[2]=implode(t," ");
464 t=regexplode((str=implode(t," ")),"[0-9][0-9]*\\.");
465 if ((l=sizeof(t))>2)
466 {
467 i=1;
468 while (i<l-1)
469 {
470 t[i]=" "+t[i][0..<2]+" ";
471 if ((l2=sizeof(t2=old_explode(t[i+1]," ")))<2)
472 t[i+1]+=t[i];
473 else
474 {
475 for (j=1;j<l2;j++)
476 {
477 if (member(TRENNER,t2[j])>=0)
478 {
479 t2[j-1]+=t[i];
480 l2=0;
481 }
482 }
483 if (!l2)
484 t[i+1]=implode(t2," ");
485 else
486 t[i+1]+=t[i];
487 }
488 t[i]="";
489 i+=2;
490 }
491 str=_single_spaces(verb+" "+implode(t," "));
492 if (str[<1]==' ') str=str[0..<2];
493 } else str=verb+(str==""?"":" "+str);
494 if (show_processing>2)
495 printf("-> {%s}\n",str);
496 return str;
497}
498
499static void decay_average()
500{
501 if (absolute_hb_count()-last_chg>14)
502 {
503 last_chg=absolute_hb_count()-last_chg;
504 if (last_chg>3000)
505 last_chg=absolute_hb_count(),cmds_per_time=0;
506 else
507 {
508 while (last_chg>14)
509 cmds_per_time=cmds_per_time*9/10, last_chg-=15;
510 last_chg=absolute_hb_count()-last_chg;
511 }
512 }
513}
514
515private void DelayPreparedSpells() {
516 mixed ps;
517
518 if (pointerp(ps=QueryProp(P_PREPARED_SPELL))
519 && sizeof(ps)>=1 && intp(ps[0])) {
520 ps[0]++;
521 SetProp(P_PREPARED_SPELL,ps);
522 write("Die Ausfuehrung Deines vorbereiteten Spruches wird verzoegert.\n");
523 } else if (ps) {
524 SetProp(P_PREPARED_SPELL,0);
525 }
526}
527
528static mixed bb;
529#ifndef BBMASTER
530#define BBMASTER "/secure/bbmaster"
531#endif
532
533/** Interpretiert Aliase und History-Kommandos
534 Eigentlich muesste hier noch die Umwandlung der Sonderzeichen
535 verschiedener Zeichensaetze mit convert_charset gemacht werden,
536 aber noch gibt es keine Moeglichkeit, den vom Spieler genutzten
537 Zeichensatz zu identifizieren.
538 \param[in] str string - Kommando des Spielers
539 \return interpretiertes Alias bzw. korrektes Kommando aus der History
540*/
541private string parsecommand(string str)
542{
543 if (str[0]=='\\')
544 {
545 // Kommando soll nicht interpretiert werden
546 return str[1..];
547 }
548 else if (str[0]=='&')
549 {
550 // Kommando aus der History
551 string cmd = str[1..];
552 int cmd_size = sizeof(cmd);
553 int cmd_found = 0;
554 if (cmd_size)
555 {
556 // Test ob &<text> etwas findet
557 for (int i=0;i<hist_size-1 && !cmd_found;i++)
558 {
559 int idx = (hist_size-i+hist_now-1)%hist_size;
560 if (history[idx][0..cmd_size-1]==cmd)
561 {
562 str = history[idx];
563 cmd_found = 1;
564 }
565 if (cmd_found)
566 {
567 if (show_processing)
568 printf("[%s]\n",str);
569 }
570 }
571 }
572 if (!cmd_found)
573 {
574 // Test, ob &<nr> klappt
575 int nummer;
576 if (str=="&&")
577 str = "&-0";
578 if (sscanf(str,"&%d",nummer))
579 {
580 if (nummer<0 || (!nummer && str[1]=='-'))
581 {
582 if (nummer<-(hist_size-1))
583 nummer=-1;
584 else
585 nummer=(hist_now+nummer-1+hist_size)%hist_size;
586 }
587 else
588 {
589 if (nummer>hist_now || hist_now-nummer>hist_size)
590 nummer=-1;
591 else
592 nummer=nummer%hist_size;
593 }
594 if (nummer<0
595 || ( (cmd=history[nummer]) =="\n\n") )
596 notify_fail("Der Befehl ist nicht in der History!\n");
597 else
598 {
599 str = cmd;
600 if (show_processing)
601 printf("[%s]\n",str);
602 }
603 }
604 }
605 }
606 switch (str)
607 {
608 case "n": return "norden";
609 case "s": return "sueden";
610 case "w": return "westen";
611 case "o": return "osten";
612 case "nw": return "nordwesten";
613 case "sw": return "suedwesten";
614 case "so": return "suedosten";
615 case "no": return "nordosten";
616 case "ob": return "oben";
617 case "u": return "unten";
618 }
619 // Test auf Alias
620 string output = "";
621 string* input = explode(str," ");
622 int input_size = sizeof(input);
623 mixed alias = aliases[input[0]];
624 if (!alias)
625 return str;
626 foreach (mixed a:alias)
627 {
628 if (!intp(a))
629 output += a;
630 else
631 {
632 if (a >= 0)
633 {
634 if (input_size > a)
635 output += input[a];
636 }
637 else
638 {
639 a = -a;
640 if (input_size > a)
641 output += implode(input[a..]," ");
642 }
643 }
644 }
645 output = _single_spaces(output);
646 str = trim(output,TRIM_RIGHT);
647 if (show_processing>1)
648 printf("[%s]\n",str);
649 return str;
650}
651
652/** Behandelt alle Sonderfaelle der Eingabe des Spielers
653 Alle Befehle des Spielers, die nicht durch Objekte behandelt
654 werden sollen, werden hier erkannt und ausgefuehrt.
655 Dazu gehoert auch die Interpretation von Aliases und History-
656 befehlen.
657 \param[in] str string: Kommando des Spielers
658 \return auszufuehrendes Kommando
659 oder 0 fuer ein nicht interpretierbares Kommando
660 oder 1 fuer ein bereits durchgefuehrtes Kommando
661*/
662mixed modify_command(string str)
663{
664
665 if (extern_call() && previous_object() &&
666 (previous_object()!=this_object() || process_call()) )
667 {
668 return 0;
669 }
670
671 // Leerzeichen an den Enden abschneiden.
672 str = trim(str, TRIM_BOTH);
673
674 if (bb)
675 BBMASTER->BBWrite(trim(str,TRIM_RIGHT,"\n"), 0);
676
677 decay_average();
678 cmds_per_time+=10000;
679
680 unparsed_args[0]=unparsed_args[1]=unparsed_args[2]=unmodified="";
681
682 if (!sizeof(str)) return "";
683
684 // Kommando wird geparst
685 unmodified=parsecommand(str);
686
687 // Environment schonmal merken.
688 last_command_env=environment();
689
690 if (unmodified == "")
691 return "";
692 // Kommando in History merken, auch wenn es im Kommandoblock abgebrochen
693 // wird.
694 AddHistory(unmodified);
695
696 // pruefen, ob Kommandoblock gesetzt ist.
697 // (Fuer Magier mit mschau ein wird das ignoriert.)
698 // BTW: Es wird absichtlich nicht das Ergebnis der Closure zurueckgegeben,
699 // sonst wuerde man beliebigen Objekten nicht nur das Abbrechen, sondern
700 // auch das Aendern von Kommandos ermoeglichen.
701 if (disablecommands && !IS_LEARNING(ME) )
702 {
703 if (disablecommands[B_TIME] >= time()
704 && objectp(disablecommands[B_OBJECT]))
705 {
706 // disablecommands valid
707 // hart-kodierte Ausnameliste pruefen
708 if ( member(({"mrufe","mschau","bug","idee","typo","detail"}),
709 explode(str," ")[0]) == -1)
710 {
711 if (closurep(disablecommands[B_VALUE]))
712 {
713 if (funcall(disablecommands[B_VALUE],_return_args(unmodified)))
714 {
715 // Non-zero Closure-Ergebnis, Abbruch. Die gerufene Funktion ist
716 // fuer eine geeignete Meldung an den Spieler verantwortlich.
717 return 1;
718 }
719 }
720 // wenn Text, dann auch pruefen, ob das Kommandoverb in den Ausnahmen
721 // steht. (query_verb() geht leider hier noch nicht.)
722 else if (stringp(disablecommands[B_VALUE])
723 && member(disablecommands[B_EXCEPTIONS],
724 explode(str," ")[0]) == -1)
725 {
726 // meldung ausgeben...
727 tell_object(PL, disablecommands[B_VALUE]);
728 // und Ende...
729 return 1;
730 }
731 }
732 }
733 else disablecommands=0;
734 }
735
736 // Verfolger direkt ins Env reinholen.
737 if (remove_call_out("TakeFollowers")>=0)
738 catch(TakeFollowers();publish);
739
740 DelayPreparedSpells();
741
742 // Historyeintrag korrigieren
743 if (unmodified[0]=='^')
744 {
745 string *oldnew,pre,post;
746 if (sizeof(oldnew=explode(unmodified,"^"))>2)
747 {
748 int hist_idx = (hist_now-1)%hist_size;
749 sscanf(history[hist_idx],"%s"+oldnew[1]+"%s", pre, post);
750 unmodified = pre+oldnew[2]+post;
751 if (show_processing)
752 write("["+unmodified+"]\n");
753 // korrigiertes Kommando natuerlich auch in die History.
754 AddHistory(unmodified);
755 }
756 }
757
758 if( bb )
759 BBMASTER->BBWrite(" -> " + unmodified, 1);
760
761 if (show_processing>1)
762 printf("[%s]\n",unmodified);
763
764 mixed ret = _return_args(unmodified);
765
766 // wenn Spieler eingewilligt hat und die SyntaxDB geladen ist, Befehl
767 // dorthin melden.
768 if (syntaxdb)
769 {
770 if (!objectp(syntaxdb[0]))
771 syntaxdb[0] = find_object("/secure/syntaxdb");
772 if (syntaxdb[0])
773 catch(syntaxdb[0]->start_cmd(unmodified);nolog);
774 }
775 return ret;
776}
777
778static int do_list(string str)
779{
780 string *cmdlist;
781 int i;
782
783 if (!QueryProp(P_WANTS_TO_LEARN))
784 return 0;
785 cmdlist=old_explode(_unparsed_args()||"",";")-({ "" });
786 for (i=0;i<sizeof(cmdlist);i++)
787 {
788 cmdlist[i]=implode(old_explode(cmdlist[i]," ")-({}), " ");
789 if (show_processing)
790 write("["+cmdlist[i]+"]\n");
791 command(cmdlist[i]);
792 }
793 return 1;
794}
795
796//falls die aliasliste kaputt ist ...
797
798int unalias_all()
799{
800 if (IS_ELDER(this_interactive())) aliases=([]);
801 return 1;
802}
803
804object _query_last_command_env()
805{
806 return last_command_env;
807}
808
809int _query_show_alias_processing()
810{
811 return show_processing;
812}
813
814int _query_histmin()
815{
816 return histmin;
817}
818
819varargs void AddAction(mixed fun, mixed cmd, int flag, int lvl)
820{
821 int i;
822 mixed *cmds;
823
824 log_file( "ARCH/ADD_ACTION", sprintf(
825 "%s:\n TO: %O TP: %O PO: %O\n fun: %O cmd: %O flag: %O lvl: %O",
826 dtime(time()), this_object(), this_player(), previous_object(),
827 fun, cmd, flag, lvl));
828
829 if (!(cmds=Query(P_LOCALCMDS))) cmds=({});
830
831 if (!pointerp(cmd)) cmd=({cmd});
832
833 for (i = sizeof(cmd)-1; i>=0; i--)
834 cmds += ({({ cmd[i] , fun, flag, lvl})});
835
836 Set(P_LOCALCMDS, cmds);
837}
838
839static int auswerten(mixed cmd, string str)
840{
841 if (closurep(cmd))
842 return funcall(cmd,str);
843 if (stringp(cmd))
844 return call_other(this_object(),cmd,str);
845 return 0;
846}
847
848static varargs int __auswerten(string str, string intern)
849{
850 string verb;
851 mixed *cmd, cmds;
852 int i,ret,lvl,l,vl;
853
854 if (!intern)
855 verb=query_verb();
856 else
857 verb=intern;
858 lvl=query_wiz_level(ME);
859 vl=sizeof(verb);
860 cmds=QueryProp(P_LOCALCMDS);
861
862 for(i=sizeof(cmds)-1;i>=0;i--)
863 {
864 cmd=cmds[i],l=sizeof(cmd[0]);
865 if (cmd[0]==verb[0..l-1] && cmd[3]<=lvl && (cmd[2]||vl==l) &&
866 (ret=auswerten(cmd[1],str)))
867 return ret;
868 }
869 // An dieser Stelle gibt es hier und vermutlich nirgendwo anders etwas, was
870 // dieses Kommando als gueltig betrachtet. Wir informieren ggf. die
871 // Syntax-DB. (Achtung: wenn jemand ne add_action() im Spielerobjekt
872 // einbaut, die vor dieser eingetragen wird, ist die Annahme ggf. falsch.)
873 // wenn Spieler eingewilligt hat und die SyntaxDB geladen ist, Befehl
874 // dorthin melden.
875 if (syntaxdb)
876 {
877 if (!objectp(syntaxdb[0]))
878 syntaxdb[0] = find_object("/secure/syntaxdb");
879 if (syntaxdb[0])
880 catch(syntaxdb[0]->cmd_unsuccessful();nolog);
881 }
882
883 return 0;
884}
885
886public void syntax_log_ep(int type)
887{
888 // wenn Spieler eingewilligt hat und die SyntaxDB geladen ist, Befehl
889 // dorthin melden.
890 if (syntaxdb && syntaxdb[0])
891 {
892 catch(syntaxdb[0]->LogEP(type);nolog);
893 }
894}
895
896static mixed _query_localcmds()
897{
898 mixed *l;
899
900 l=Query(P_LOCALCMDS);
901 if (!pointerp(l))
902 l=({});
903 return ({
904 ({"ali","alias",0,0}),
905 ({"alias","alias",0,0}),
906 ({"unali","unalias",1,0}),
907 ({"histmin","histmin",0,0}),
908 ({"histlen","histlen",0,0}),
909 ({"hist","show_hist",0,0}),
910 ({"history","show_hist",0,0}),
911 ({"do","do_list",0,LEARNER_LVL}),
912 ({"ersetzungsanzeige","replacedisplay",0,0}),
913 ({"syntaxsammlung","collect_cmds",0,0}),
914 ({"fehlermeldung","set_errormessage",0,SEER_LVL}),
915 })+l;
916}
917
918static int collect_cmds(string cmd)
919{
920 if (!stringp(cmd))
921 {
922 _notify("Mit diesem Befehl kannst Du mithelfen, Syntaxen im MG zu "
923 "verbessern. Wenn Du einverstanden bist, speichern wir "
924 "anonym (d.h. ohne Deinen Charnamen), welche Deiner Befehle "
925 "erfolgreich und nicht erfolgreich waren. Uebliche "
926 "Kommunikationsbefehle werden dabei nicht gespeichert.",
927 0);
928 _notify("Mit 'syntaxsammlung ja' kannst Du die Speicherung einschalten, "
929 "mit 'syntaxsammlung nein' kannst Du sie ausschalten.",0);
930 _notify("Deine Befehle werden zur Zeit"
931 + (QueryProp("_syntaxdb") ? " " : " NICHT ")
932 + "gespeichert.", 0);
933 }
934 else if (cmd == "ja")
935 {
936 SetProp("_syntaxdb", 1);
937 _notify("Ab jetzt werden Deine Befehle gespeichert. Vielen Dank!", 0);
938 }
939 else
940 {
941 SetProp("_syntaxdb", 0);
942 _notify("Ab jetzt werden Deine Befehle NICHT gespeichert.", 0);
943 }
944 return 1;
945}
946
947int _query_command_average()
948{
949 decay_average();
950 return cmds_per_time;
951}
952
953nomask void __set_bb(int flag) {
954 if( previous_object()!=find_object(BBMASTER) || process_call() )
955 return;
956 bb=flag;
957}
958
959
960nomask public void countCmds( int type, string key )
961{
962 string tmp;
963
964 if ( this_player() != this_interactive()
965 || this_interactive() != this_object()
966 || member( cmd_types, type ) < 0 )
967 return;
968
969 tmp = sprintf( "%d\n%s", type, key );
970
971 commands -= ({ tmp });
972 commands += ({ tmp });
973 commands = commands[0..max_commands-1];
974}
975
976
977nomask public string *getCmds()
978{
979 string *tmp;
980
981 if ( previous_object() != find_object(BBMASTER) )
982 return ({});
983
984 tmp = commands;
985 commands = ({});
986
987 return tmp;
988}
989
990/*
991 * Force the monster to do a command. The force_us() function isn't
992 * always good, because it checks the level of the caller, and this function
993 * can be called by a room.
994 */
995int command_me(string cmd)
996{
997 if (IS_LEARNER(ME))
998 {
999 if (!this_interactive() || !previous_object())
1000 return 0;
1001 if( geteuid(ME)!=geteuid(this_interactive())
1002 || geteuid(ME)!=geteuid(previous_object()) )
1003 {
1004 if( query_wiz_level(ME)<query_wiz_level(previous_object()))
1005 tell_object(ME,previous_object()->name()+" zwingt Dich zu: "
1006 + cmd + ".\n");
1007 else
1008 {
1009 tell_object(ME,previous_object()->name()
1010 + " versucht, Dich zu " + cmd + " zu zwingen.\n" );
1011 return 0;
1012 }
1013 }
1014 }
1015 return command(cmd);
1016}
1017
1018
1019static mixed _query_p_lib_disablecommands() {
1020 // abgelaufen oder Objekt zerstoert? Weg damit.
1021 if (pointerp(disablecommands)
1022 && (disablecommands[B_TIME] < time()
1023 || !objectp(disablecommands[B_OBJECT])) )
1024 return(disablecommands = 0);
1025
1026 // sonst Kopie zurueck (copy(0) geht)
1027 return(copy(disablecommands));
1028}
1029
1030static mixed _set_p_lib_disablecommands(mixed data) {
1031
1032 // setzendes Objekt ermitteln, da diese Funktion nur per SetProp() gerufen
1033 // werden sollte (!), ist das PO(1);
1034 object origin = previous_object(1);
1035 // wenn nicht existent, direkt abbruch
1036 if (!objectp(origin))
1037 return _query_p_lib_disablecommands();
1038
1039 // Prop loeschen? Explizit loeschen darf jeder, allerdings nicht direkt
1040 // ungeprueft ueberschreiben.
1041 if (!data) {
1042 return (disablecommands = 0 );
1043 }
1044 // mal direkt buggen bei falschen Datentyp, damits auffaellt.
1045 if (!pointerp(data) || sizeof(data) < 2 || !intp(data[0])
1046 || (!stringp(data[1]) && !closurep(data[1]))
1047 || (sizeof(data) >= 3 && !pointerp(data[2])) )
1048 raise_error(sprintf(
1049 "Wrong data type for P_DISABLE_COMMANDS. Expected Array with "
1050 "2 or 3 elements (int, string|closure, [string*]), got %.25O\n",
1051 data));
1052
1053 // Wenn abgelaufen oder gleiches Objekt wie letztes Mal: eintragen.
1054 if (!disablecommands || (disablecommands[B_TIME] < time()
1055 || !objectp(disablecommands[B_OBJECT])
1056 || disablecommands[B_OBJECT] == origin) ) {
1057 // Loggen nur, wenn eine Closure eingetragen wird. Reduziert den
1058 // Logscroll und Strings haben deutlich weniger Missbrauchspotential.
1059 if (closurep(data[1])) {
1060 CBLOG(sprintf("[%s] CB gesetzt von %O, gueltig bis %s, Daten: %O\n",
1061 strftime("%Y%m%d-%H:%M:%S"),origin,
1062 strftime("%Y%m%d-%H:%M:%S",data[0]),
1063 (stringp(data[1]) ? regreplace(data[1],"\n","\\n",0)
1064 : data[1])));
1065 }
1066 if (sizeof(data)+1 <= B_EXCEPTIONS)
1067 disablecommands = ({ origin, data[0], data[1], ({}) });
1068 else
1069 disablecommands = ({ origin, data[0], data[1], data[2] });
1070 return(copy(disablecommands));
1071 }
1072
1073 return(_query_p_lib_disablecommands());
1074}
1075
1076static mixed _set__syntaxdb(mixed v)
1077{
1078 Set("_syntaxdb", v, F_VALUE);
1079 if (v)
1080 syntaxdb = ({find_object("/secure/syntaxdb")});
1081 else
1082 syntaxdb = 0;
1083 return QueryProp("_syntaxdb");
1084}
1085