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