blob: f684288213a7f0dcc7486aea2e412c813811ecdc [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001// MorgenGrauen MUDlib
2//
3// thing/commands.c -- thing description
4//
5// $Id: commands.c 9514 2016-02-23 20:33:09Z Gloinson $
6//
7// Aus Regenbogen MUDLib
8// aus Gueldenland/Wunderland MUDlib (Morgengrauen MUDlib)
9//
10// P_COMMANDS data-structure:
11//
12// AddCmd(verb,fun1,1);
13// AddCmd(verb+syn1a|syn1b&syn2a|syn2b|syn2c,fun2,
Zesstra16698ff2016-12-13 22:19:16 +010014// error1_notify|error2_notify^error2_write);
MG Mud User88f12472016-06-24 23:31:02 +020015// -->
Zesstra16698ff2016-12-13 22:19:16 +010016// ([verb:
17// ({fun1,fun2}); // funs
18// ({1,({error1_notify, error2_write^error2_say, 1})}); // flags
19// ({0,({({syn1a,syn1b}),({syn2a,syn2b,syn2c})})}); // rules
20// 0; // IDs
21// ({closure auf fun1, closure auf fun2}) ]) // Cache
MG Mud User88f12472016-06-24 23:31:02 +020022//
Zesstra16698ff2016-12-13 22:19:16 +010023// Funs: ({<fun1> ,...
24// 'fun' kann sein: Closure
25// String: Methodenname - wird etwas geprueft
26// Objekt: wenn keine Methode, this_object() fuer
27// intern, previous_object() fuer extern
28// 0 (erloschenes externes Objekt)
MG Mud User88f12472016-06-24 23:31:02 +020029// Rules: ({<Regelsatz fuer fun1>, ({<1. Synonymgruppe>,
Zesstra16698ff2016-12-13 22:19:16 +010030// <2. Synonymgruppe, ...}), ...})
MG Mud User88f12472016-06-24 23:31:02 +020031// Flags: ({<Flag fuer fun1>, ({<Fehlermeldung 1. Synonymgruppe>, ... ,
Zesstra16698ff2016-12-13 22:19:16 +010032// [, Index fuer write anstatt notify_fail]}),
33// ... })
MG Mud User88f12472016-06-24 23:31:02 +020034// IDs: 0 oder ({<ID fuer fun1>}) oder ({0, <ID fuer fun2>}) ...
Zesstra16698ff2016-12-13 22:19:16 +010035// Cache: ({<closure fuer fun1>, ...
36// Cache-Closures sind bei Export (QueryProp) immer genullt
MG Mud User88f12472016-06-24 23:31:02 +020037#pragma strict_types
38#pragma save_types
39#pragma range_check
40#pragma no_clone
41#pragma pedantic
42
43#include <moving.h>
44#include <thing/language.h>
45#include <exploration.h>
Zesstrabab5a9d2016-07-30 12:53:35 +020046#include <defines.h>
47#include <living/comm.h>
Zesstra16698ff2016-12-13 22:19:16 +010048#include <functionlist.h>
MG Mud User88f12472016-06-24 23:31:02 +020049
50#define NEED_PROTOTYPES
Zesstrabab5a9d2016-07-30 12:53:35 +020051#include <thing/properties.h>
MG Mud User88f12472016-06-24 23:31:02 +020052#include <thing/description.h>
53#include <thing/commands.h>
54#undef NEED_PROTOTYPES
55
Zesstra16698ff2016-12-13 22:19:16 +010056#define CMDS_WIDTH 5
57// Mapping-Indizes
58#define CMDIDX_FUN 0
59#define CMDIDX_FLAG 1
60#define CMDIDX_RULE 2
61#define CMDIDX_ID 3
62#define CMDIDX_CACHE 4
63
MG Mud User88f12472016-06-24 23:31:02 +020064#ifdef DBG
65#undef DBG
66#endif
67#define DBG(x) printf("Object %O tmpstr=%s\n", explode(object_name(this_object()),"#")[1], x);
68
69private nosave mapping added_cmds;
70
Zesstra2a4b5702016-08-16 19:08:47 +020071protected int _cmd_syntaxhelp(string str, mixed *args)
Zesstrabab5a9d2016-07-30 12:53:35 +020072{
73 mapping|closure restr;
74 mixed help = QueryProp(P_SYNTAX_HELP);
75 if (pointerp(help))
76 {
77 restr = help[1];
78 help = help[0];
79 }
80 // Restriktionen vor dem Anzeigen pruefen.
81 if (mappingp(restr))
82 {
83 string res = "/std/restriction_checker"->check_restrictions(PL,restr);
84 if (res)
85 {
86 PL->ReceiveMsg("Fuer " + name(WEN,1) + " darfst Du "
87 "die Syntaxhilfe (noch) nicht lesen:\n"
88 + res,
89 MT_NOTIFICATION|MSG_BS_LEAVE_LFS,
90 "syntaxhilfe",0,this_object());
91 return 1;
92 }
93 }
94 else if (closurep(restr))
95 {
96 string res = funcall(restr, ME);
97 if (res)
98 {
99 if (intp(res))
100 PL->ReceiveMsg("Fuer " + name(WEN,1) + " darfst Du "
101 "die Syntaxhilfe (noch) nicht lesen.",
102 MT_NOTIFICATION|MSG_BS_LEAVE_LFS,
103 "syntaxhilfe",0,this_object());
104 else if (stringp(res))
105 PL->ReceiveMsg(res,
106 MT_NOTIFICATION|MSG_BS_LEAVE_LFS,
107 "syntaxhilfe",0,this_object());
108 return 1;
109 }
110 }
111
112 if (stringp(help))
113 {
114 help = "Fuer " + name(WEN,1) + " gibt es folgende Syntaxhilfe:\n"
115 + help;
116 }
117 else if (closurep(help))
118 {
119 help = funcall(help, this_object());
120 }
121 else
122 {
123 // wenn das Objekt keine Syntaxhilfe hat, braucht es das Kommando auch
124 // nicht.
125 notify_fail("Fuer " + name(WEN,1)
126 + " gibt es keine Syntaxhilfe.\n");
127 RemoveCmd(0,0, "_cmd_syntaxhelp");
128 return 0;
129 }
130 if (stringp(help) && sizeof(help))
131 PL->ReceiveMsg(help, MT_NOTIFICATION|MSG_BS_LEAVE_LFS,
132 "syntaxhilfe",0,this_object());
133
134 return 1;
135}
136
MG Mud User88f12472016-06-24 23:31:02 +0200137protected void create()
138{
Zesstrabab5a9d2016-07-30 12:53:35 +0200139 AddCmd("syntaxhilfe&@ID", #'_cmd_syntaxhelp,
140 "Fuer WAS moechtest Du eine Syntaxhilfe?\n",
141 "_cmd_syntaxhelp");
MG Mud User88f12472016-06-24 23:31:02 +0200142}
143
144protected void create_super() {
145 set_next_reset(-1);
Zesstrabab5a9d2016-07-30 12:53:35 +0200146}
MG Mud User88f12472016-06-24 23:31:02 +0200147
Zesstra16698ff2016-12-13 22:19:16 +0100148private closure _check_stringmethod(mixed func, int ex, string resp) {
Christian Georg Becker903cc992017-06-24 15:54:36 +0200149 closure cl = symbol_function(func, this_object());
150 if(!cl)
151 {
Christian Georg Becker9ddeb092017-06-24 00:39:44 +0200152 catch(raise_error(sprintf(
Zesstra16698ff2016-12-13 22:19:16 +0100153 resp+"string-Methode '%s' fehlt oder ist private.\n", func));
Christian Georg Becker9ddeb092017-06-24 00:39:44 +0200154 publish);
Christian Georg Becker903cc992017-06-24 15:54:36 +0200155 return 0;
156 }
Zesstra16698ff2016-12-13 22:19:16 +0100157
158 // extern erstellte auf Sichtbarkeit pruefen und abbrechen/davor warnen
159 if(ex) {
160 mixed *fl = functionlist(this_object(), RETURN_FUNCTION_NAME|
161 RETURN_FUNCTION_FLAGS);
162 int index = member(fl, func)+1;
Christian Georg Becker903cc992017-06-24 15:54:36 +0200163 if(index<=0 || sizeof(fl)<=index) // sollte nicht passieren, s.o.
164 raise_error(sprintf(
165 resp+"string-Methode '%s' trotz function_exists() nicht in "
166 "functionlist. WTF?\n", func));
167 else if(fl[index]&TYPE_MOD_PROTECTED || fl[index]&TYPE_MOD_STATIC) {
Christian Georg Becker9ddeb092017-06-24 00:39:44 +0200168 catch(raise_error(sprintf(
Zesstra16698ff2016-12-13 22:19:16 +0100169 resp+"string-Methode '%s' ist protected/static. Extern "
170 "definiertes Kommando darf so eine Methode nicht aufrufen!\n",
171 func));
Christian Georg Becker9ddeb092017-06-24 00:39:44 +0200172 publish);
Christian Georg Becker903cc992017-06-24 15:54:36 +0200173 return 0;
174 }
Zesstra16698ff2016-12-13 22:19:16 +0100175 }
176
177 // ansonsten Methode erstmal cachen
Zesstra16698ff2016-12-13 22:19:16 +0100178 return cl;
179}
180
MG Mud User88f12472016-06-24 23:31:02 +0200181varargs void AddCmd(mixed cmd, mixed func, mixed flag, mixed cmdid) {
182 int i,j;
Zesstra16698ff2016-12-13 22:19:16 +0100183 closure cachedcl;
MG Mud User88f12472016-06-24 23:31:02 +0200184 mixed *rule;
185
186 // potentielle AddCmd mit Regel?
187 if(stringp(cmd)) {
188 // eine Regel? - aufsplitten
189 if((i=member(cmd,'&'))>0) {
190 // ... in Array mit Verknuepfungselementen
191 rule=explode(cmd[(i+1)..],"&");
192 j=sizeof(rule);
193 // ... in Array mit Arrays mit Alternativelementen:
194 // "p|q&r|s" -> ({ ({"p","q"}), ({"r","s"}} })
195 while(j--)
196 rule[j]=explode(rule[j],"|");
197
198 // Regeln von Kommandoverben abschneiden
199 cmd=cmd[0..(i-1)];
200 }
201 // Kommandoverben extrahieren
202 cmd=explode(cmd,"|");
203
204 // Satz von Regeln existiert: Aufsplitten von Fehlermeldungen
205 if(rule)
206 if(stringp(flag)) {
207 mixed *fail;
208 // in einfaches Array mit jeweiligen Fehlermeldungen
209 fail=explode(flag,"|");
210 j=0;
211 i=sizeof(fail);
212 while(j<i) {
213 // write - Fehlermeldung entdeckt - Position ggf. eintragen
214 if(member(fail[j],'^')>=0 && !intp(fail[<1]))
215 fail+=({j});
216 if(member(fail[j],'@')>=0) {
217 int s;
218 flag=regexplode(fail[j], "@WE[A-SU]*[0-9]");
219 s=sizeof(flag);
220 while((s-=2)>0) {
221 int tmpint;
222 tmpint=flag[s][<1]-'1';
223 if(tmpint<0 || tmpint>j)
224 raise_error(sprintf(
225 "AddCmd: error-message %d contains out-of-bounds @WExx-rule.\n",j+1));
226 }
227 }
228 j++;
229 }
230 // "Was?|Wie das?" -> ({"Was?","Wie das?"})
231 // "Was?|Wie das?^|Womit das?|Worauf das?^@WER1 macht was." ->
232 // ({"Was?",
233 // "Wie das?^Womit das?",
234 // "Worauf das?^@WER1 macht was.",1})
235 flag=sizeof(fail);
236 if(flag && flag<sizeof(rule))
237 raise_error(
238 "AddCmd: number of error-messages does not match number of rules.\n");
239 flag=fail; // ueberschreiben mit den parsefreundlichen Format
240 } else if(flag)
241 raise_error("AddCmd: rules exist but flags are not an error-string.\n");
242 } // end if(stringp(cmd)) ... kein Regelstring vorhanden
243
244 // kein Kommandoarray gewesen noch erzeugt?
245 if(!pointerp(cmd))
246 raise_error("AddCmd: missing string/pointer-parameter for command.\n");
247
Zesstra16698ff2016-12-13 22:19:16 +0100248 // String-Methode auf Sichtbarkeit pruefen, Cache gleich anpassen
249 switch(typeof(func)) {
250 case T_STRING:
251 cachedcl=_check_stringmethod(func, extern_call(), "AddCmd: ");
252 break;
253 case T_CLOSURE:
254 cachedcl=func;
255 break;
256 default:
257 if(extern_call()) func=previous_object();
258 else func=this_object();
259 break;
260 }
MG Mud User88f12472016-06-24 23:31:02 +0200261
Zesstra16698ff2016-12-13 22:19:16 +0100262 // jedes einzelne Verb mit seinen Regeln und Funktionen eintragen
MG Mud User88f12472016-06-24 23:31:02 +0200263 i=sizeof(cmd);
Zesstra16698ff2016-12-13 22:19:16 +0100264 if(!added_cmds) added_cmds=m_allocate(i, CMDS_WIDTH);
MG Mud User88f12472016-06-24 23:31:02 +0200265 while(i--) {
266 string str;
267 str=cmd[i];
MG Mud User88f12472016-06-24 23:31:02 +0200268 if(!member(added_cmds,str))
Zesstra16698ff2016-12-13 22:19:16 +0100269 added_cmds+=([str:allocate(0);allocate(0);allocate(0);0;allocate(0)]);
MG Mud User88f12472016-06-24 23:31:02 +0200270 // existierendes Verb ergaenzen
Zesstra16698ff2016-12-13 22:19:16 +0100271 added_cmds[str, CMDIDX_FUN]+=({func});
272 added_cmds[str, CMDIDX_FLAG]+=({flag});
273 added_cmds[str, CMDIDX_RULE]+=({rule});
274 added_cmds[str, CMDIDX_CACHE]+=({cachedcl});
MG Mud User88f12472016-06-24 23:31:02 +0200275 // ggf. id in das ID-Mapping eintragen
276 if(cmdid) {
277 mixed *tmp;
Zesstra16698ff2016-12-13 22:19:16 +0100278 j=sizeof((string*)added_cmds[str, CMDIDX_FUN]);
279 tmp=added_cmds[str, CMDIDX_ID]||allocate(j);
MG Mud User88f12472016-06-24 23:31:02 +0200280 if(sizeof(tmp)<j) tmp+=allocate(j-sizeof(tmp));
281 tmp[<1]=cmdid;
Zesstra16698ff2016-12-13 22:19:16 +0100282 added_cmds[str, CMDIDX_ID]=tmp;
MG Mud User88f12472016-06-24 23:31:02 +0200283 }
284 }
285}
286
287// Auswertung fuer ein Verb loeschen
288varargs int RemoveCmd(mixed cmd, int del_norule, mixed onlyid) {
289 int ret;
290
291 // Falls Magier das RemoveCmd falsch nutzen (z.B. analog zu AddCmd)
292 // wird das Regelsystem verwirrt. Da das del_norule nur int sein darf,
293 // gibt es hier eine gute Chance den Fehler abwaertskompatibel zu ent-
294 // decken. Damit Spieler den Fehler nicht mitbekommen, wird hier auf
295 // ein raise_error verzichtet, und statt dessen in ein Logfile ge-
296 // schrieben.
297 if (!intp(del_norule))
298 {
299 log_file("REMOVE_CMD",
300 sprintf("\n-- %s --\nIllegal RemoveCommand() in Object [%O]:\n %O\n",
301 dtime(time()), this_object(), cmd));
302 del_norule=0;
303 onlyid=0;
304 }
305
306 if(!added_cmds || (!cmd && !del_norule && !onlyid))
307 added_cmds=(mapping)0;
308 else {
309 int i, j;
Zesstra16698ff2016-12-13 22:19:16 +0100310 mixed *rule, *flag, *fun, *delrule, *ids, *cachecl;
MG Mud User88f12472016-06-24 23:31:02 +0200311
312 if(stringp(cmd)) {
313 // Regeln entdeckt - Zerlegen (wie AddCmd)
314 if((i=member(cmd,'&'))>0) {
315 delrule=explode(cmd[(i+1)..],"&");
316 j=sizeof(delrule);
317 while(j--)
318 delrule[j]=explode(delrule[j],"|");
319 cmd=cmd[0..(i-1)];
320 }
321 cmd=explode(cmd,"|");
322 } else if(del_norule || onlyid) cmd=m_indices(added_cmds);
323
324 if(!pointerp(cmd))
325 raise_error("RemoveCmd: missing string/pointer-parameter.\n");
326 i=sizeof(cmd);
327
328 while(i--) {
329 // keine Regeln da und Regeln loeschen erlaubt: alles loeschen
330 if(!delrule && !del_norule && !onlyid) m_delete(added_cmds,cmd[i]);
Zesstra16698ff2016-12-13 22:19:16 +0100331 else if(m_contains(&fun, &flag, &rule, &ids, &cachecl, added_cmds, cmd[i])) {
MG Mud User88f12472016-06-24 23:31:02 +0200332 j=sizeof(fun);
333 while(j--) {
334 int k;
335 // DBG(rule[j]);
Christian Georg Becker903cc992017-06-24 15:54:36 +0200336 // Regeln nicht löschen und Regel?
MG Mud User88f12472016-06-24 23:31:02 +0200337 if(!(del_norule && pointerp(rule[j])) &&
Christian Georg Becker903cc992017-06-24 15:54:36 +0200338 // nur bestimmte ID löschen und ID passt nicht?
MG Mud User88f12472016-06-24 23:31:02 +0200339 !(onlyid && (!pointerp(ids) || sizeof(ids)<=j || ids[j]!=onlyid)) &&
Christian Georg Becker903cc992017-06-24 15:54:36 +0200340 // Löschregel existiert und passt nicht auf Regel?
MG Mud User88f12472016-06-24 23:31:02 +0200341 !(delrule && (k=sizeof(rule[j]))!=sizeof(delrule))) {
Christian Georg Becker903cc992017-06-24 15:54:36 +0200342 // partielles Testen einer Löschregel ...
MG Mud User88f12472016-06-24 23:31:02 +0200343 if(delrule) {
344 while(k--)
345 if(!sizeof(rule[j][k]&delrule[k])) break;
346 if(k>=0) continue;
347 }
Christian Georg Becker903cc992017-06-24 15:54:36 +0200348 // alles korrekt: Löschen!
MG Mud User88f12472016-06-24 23:31:02 +0200349 // (Arraybereich durch leeres Array loeschen)
Zesstra16698ff2016-12-13 22:19:16 +0100350 flag[j..j] = allocate(0);
351 fun[j..j] = allocate(0);
352 rule[j..j] = allocate(0);
353 cachecl[j..j] = allocate(0);
MG Mud User88f12472016-06-24 23:31:02 +0200354 if(ids) {
355 ids[j..j] = allocate(0);
356 if(!sizeof(ids-allocate(1))) ids=(mixed*)0;
357 }
358 ret++;
359 }
360 } // end while(j--) {
361 }
362 // Funktions/Regelliste update oder ggf. Kommando voellig loeschen
363 if(sizeof(rule)) {
Zesstra16698ff2016-12-13 22:19:16 +0100364 added_cmds[cmd[i], CMDIDX_FUN]=fun;
365 added_cmds[cmd[i], CMDIDX_FLAG]=flag;
366 added_cmds[cmd[i], CMDIDX_RULE]=rule;
367 added_cmds[cmd[i], CMDIDX_ID]=ids;
368 added_cmds[cmd[i], CMDIDX_CACHE]=cachecl;
MG Mud User88f12472016-06-24 23:31:02 +0200369 } else m_delete(added_cmds,cmd[i]);
370 }
371 if(!sizeof(added_cmds)) added_cmds=(mapping)0;
372 }
373 return ret;
374}
375
376// Ausfuehren samt geparstem Inputstring und getriggerten Parserergebnissen
377static int _execute(mixed fun, string str, mixed *parsed) {
Zesstra16698ff2016-12-13 22:19:16 +0100378 switch(typeof(fun)) {
379 case T_CLOSURE:
380 return ((int)funcall(fun,str,&parsed));
381 case T_STRING:
382 int ret;
383 if(!call_resolved(&ret, this_object(), fun, str, &parsed))
384 raise_error(sprintf(
385 "AddCmd: String-Methode '%s' in Kommando %#O scheint zu fehlen "
386 "oder nicht zugreifbar zu sein.\n", fun, parsed));
387 return ret;
388 default:
389 break;
390 }
391 return 0;
MG Mud User88f12472016-06-24 23:31:02 +0200392}
393
394#define CHECK_PRESENT 1
395#define CHECK_ID 2
396#define CHECK_PUTGETNONE 4
397#define CHECK_PUTGETDROP 8
398#define CHECK_PUTGETTAKE 16
399#define CHECK_PUTGET (CHECK_PUTGETNONE|CHECK_PUTGETDROP|CHECK_PUTGETTAKE)
400
401// Wert fuer Fehlschlag, Fallback-Wert der benutzten while-- - Schleifen
402#define NOMATCHFOUND -1
403
404// Regeln fuer ein (nun unwichtiges) Verb triggern
405static int _process_command(string str, string *noparsestr,
Zesstra16698ff2016-12-13 22:19:16 +0100406 mixed fun, mixed flag, mixed rule,
407 mixed id, mixed cachedcl) {
MG Mud User88f12472016-06-24 23:31:02 +0200408 mixed *parsed, *objmatches;
409
410 // eine Regel ... auswerten ...
411 if(pointerp(rule)) {
412 int nrul;
413 parsed=objmatches=allocate(0);
414 int lastmatchpos=NOMATCHFOUND;
415
416 // Abgleichen der gesplitteten Eingabe mit Regeln:
417 // vorwaerts durch die Synonymgruppen
418 int rs=sizeof(rule);
419 while(nrul<rs) {
420 int matchpos;
421 string *synonym;
422 mixed matchstr;
423
424 matchpos=NOMATCHFOUND;
425 matchstr=0;
426
427 // Synonyme extrahieren
428 int nrsynonyms=sizeof(synonym=rule[nrul]);
429
430 // egal wie durch Synonyme bis Match - Abgleich mit Eingabe
431 while(nrsynonyms--) {
432 int tmppos = member(noparsestr,synonym[nrsynonyms]);
433 // ist Synonym im Eingabestring und kommt spaeter als vorheriges Synonym?
434 if(tmppos>=0 && tmppos>lastmatchpos) {
435 // Erfolg: merken der Position im Eingabestring und den matchenden String
436 matchpos=tmppos;
437 matchstr=noparsestr[tmppos];
438 break;
439 }
440 }
441
442 // kein Match durch Synonyme? Pruefe die @-Spezialvariablen.
443 if(matchpos == NOMATCHFOUND) {
444 int check_present;
445 // ist Abpruefen von ID/PRESENT in der Synonymgruppe verlangt
446 // bei present()/find_obs gleich Voraussetzung gueltiger TP mitprufen
447 if(member(synonym,"@ID")>=0) check_present=CHECK_ID;
448 if(this_player()) {
449 if(member(synonym,"@PRESENT")>=0) check_present|=CHECK_PRESENT;
450 else if(member(synonym,"@PUT_GET_NONE")>=0)
451 check_present|=CHECK_PUTGETNONE;
452 else if(member(synonym,"@PUT_GET_TAKE")>=0)
453 check_present|=CHECK_PUTGETDROP;
454 else if(member(synonym,"@PUT_GET_DROP")>=0)
455 check_present|=CHECK_PUTGETTAKE;
456 }
457
458 if(check_present) {
459 // wir fangen hinter dem letzten Match an
460 int q_start=lastmatchpos+1;
461 int r_end=sizeof(noparsestr)-1;
462
463 int range;
464 while((range=r_end-q_start)>=0) {
465 mixed tmpobj;
466
Christian Georg Becker903cc992017-06-24 15:54:36 +0200467 // wie weit wollen wir zurückgehen?
MG Mud User88f12472016-06-24 23:31:02 +0200468 if(range)
469 if(!(check_present&CHECK_PUTGET))
470 range=range>2?2:range; // 3 Fragmente fuer @ID/@PRESENT (Adverb/Nr)
471 else if(range>4)
472 range=4; // 5 Fragmente fuer @PUT_XXX
473
474 // und jetzt die Substrings pruefen
475 while(range>=0 && !matchstr) {
476 string tmpstr;
477
478 // zu pruefenden String aus den Teilen zusammensetzen
479 if(range) tmpstr=implode(noparsestr[q_start..(q_start+range)]," ");
480 else tmpstr=noparsestr[q_start];
481
482 //DBG(tmpstr);
483 if(check_present&CHECK_PRESENT && // PRESENT ?
484 ((tmpobj=present(tmpstr,this_player())) ||
485 (tmpobj=present(tmpstr,environment(this_player())))))
486 matchstr=tmpobj;
487 else if(check_present&CHECK_ID && id(tmpstr)) // ID ?
488 matchstr=this_object();
489 else if((check_present&CHECK_PUTGET) && // PUT_GET_??? ?
490 (tmpobj=(object*)
491 this_player()->find_obs(tmpstr,
492 ([CHECK_PUTGETNONE:PUT_GET_NONE,
493 CHECK_PUTGETDROP:PUT_GET_TAKE,
494 CHECK_PUTGETTAKE:PUT_GET_DROP])
495 [CHECK_PUTGET&check_present])) &&
496 sizeof(tmpobj)) {
497 if(sizeof(tmpobj)==1) matchstr=tmpobj[0];
498 else { // Arrays werden zwischengespeichert ...
499 objmatches+=({sizeof(parsed),tmpobj});
500 matchstr=tmpstr;
501 }
502 } else { // dieser Substring hat nicht gematcht
503 // ab weniger als 3 Teilen ist das Nichtmatching eines Substrings mit
504 // beendendem Numeral Kriterium fuer Abbruch ("objekt 2" soll matchen)
505 string numeralcheck;
506 if(range && range<=2 &&
507 sizeof(numeralcheck=noparsestr[q_start+range]) &&
508 to_string(to_int(numeralcheck))==numeralcheck)
509 break;
510
511 // Substringlaenge verkuerzen und weiter
512 range--;
513 }
514 }
515
516 // Match gefunden!
517 if(matchstr) {
518 matchpos=range+q_start;
519 // DBG(matchpos);
520 break;
521 }
522 q_start++;
523 } // end while
524 }
525 }
526
527 // Fehlermeldung fuer diese fehlgeschlagene Synonymgruppe setzen
528 if(matchpos == NOMATCHFOUND) {
529 // Fehlermeldungen und ein Eintrag an der Fehlerstelle?
530 if(pointerp(flag) && sizeof(flag)>nrul) {
531
532 matchstr=flag[nrul];
533
534 if(stringp(matchstr) && sizeof(matchstr)) {
535 if(member(matchstr,'@')>=0) {
536 matchstr=replace_personal(&matchstr,({this_player()})+parsed,1);
537 string stamm=((query_verb()[<1]^'e')?query_verb():query_verb()[0..<2]);
538 matchstr=regreplace(matchstr,"@VERB",capitalize(stamm),1);
539 matchstr=regreplace(matchstr,"@verb",stamm,1);
540 }
541
542 // ist Fehlermeldung ein WRITE?
543 // dann return 1 !
544 if(intp(flag[<1]) && flag[<1]<=nrul) {
545 if(member(matchstr,'^')>=0) {
546 matchstr=explode(matchstr,"^");
547 write(capitalize(break_string(matchstr[0],78,0,1)));
548 if(sizeof(matchstr[1]))
549 say(capitalize(break_string(matchstr[1],78,0,1)),({this_player()}) );
550 } else write(capitalize(break_string(matchstr,78,0,1)));
551 return 1;
552 } else notify_fail(capitalize(break_string(matchstr,78,0,1)));
553 }
554 }
555 return 0;
556 }
557
558 // Updaten der Hilfsvariablen
559 parsed+=({matchstr});
560 lastmatchpos=matchpos;
561 nrul++;
562 } // end while(nrul<rs) ... Erfolg ... ab zum naechsten Regelteil
563 }
564
565 // Arrays der @-Objectmatches in Parameter fuer Methode resubstituieren
566 int objsize;
567 if((objsize=sizeof(objmatches)))
568 while((objsize-=2)>=0)
569 parsed[objmatches[objsize]]=objmatches[objsize+1];
570
571 // erfolgreich Methode gefunden/eine Regel bis zum Ende durchgeparst:
Zesstra16698ff2016-12-13 22:19:16 +0100572 return(_execute(cachedcl||fun, str, &parsed));
MG Mud User88f12472016-06-24 23:31:02 +0200573}
574
575// Auswertung - add_action-Fun
576public int _cl(string str) {
577 int nindex;
578 string *keys;
579 mixed *flag;
580 // Verb existiert, Kommandos auch, Eintrag fuer Verb gefunden
581 if(mappingp(added_cmds) && query_verb()) {
Zesstra16698ff2016-12-13 22:19:16 +0100582 mixed *fun, *rule, *ids, *cachecl;
MG Mud User88f12472016-06-24 23:31:02 +0200583
584 // ist das Verb ein Key im Kommandomapping?
Zesstra16698ff2016-12-13 22:19:16 +0100585 if(m_contains(&fun, &flag, &rule, &ids, &cachecl, added_cmds, query_verb())) {
MG Mud User88f12472016-06-24 23:31:02 +0200586 string *noparsestr;
587 nindex=sizeof(fun);
588 noparsestr=explode((str||"")," ")-({""});
589 // dann matche ungeparsten Input gegen etwaige Regeln
590 // -- nicht aendern: neue Kommandos sollen alte "ueberschreiben" koennen
591 while(nindex--)
Zesstra@Morgengrauen4ba72dc2016-09-08 23:13:26 +0200592 {
593 mixed cmd_id = (pointerp(ids) && sizeof(ids)>nindex) ? ids[nindex] : 0;
594 if(_process_command(&str, noparsestr, fun[nindex], flag[nindex],
Zesstra16698ff2016-12-13 22:19:16 +0100595 rule[nindex], cmd_id, cachecl[nindex])) {
MG Mud User88f12472016-06-24 23:31:02 +0200596 GiveEP(EP_CMD, query_verb());
597 return 1;
598 }
Zesstra@Morgengrauen4ba72dc2016-09-08 23:13:26 +0200599 }
MG Mud User88f12472016-06-24 23:31:02 +0200600 }
601
602 // keine Regel passte, unscharfe Auswertung auf alle
603 // AddCmdverben ausdehnen
604 keys=m_indices(added_cmds);
605 nindex=sizeof(keys);
606 while(nindex--)
607 if(!strstr(query_verb(),keys[nindex]) &&
Zesstra16698ff2016-12-13 22:19:16 +0100608 member((flag=added_cmds[keys[nindex], CMDIDX_FLAG]),1)>=0) {
MG Mud User88f12472016-06-24 23:31:02 +0200609 int i,ret;
610 i=sizeof(flag);
611 // Reihenfolge nicht aendern !
612 while(i--)
613 if(flag[i]==1) {
Zesstra16698ff2016-12-13 22:19:16 +0100614 mixed *fuzzyfuns = added_cmds[keys[nindex], CMDIDX_FUN];
615 mixed *fuzzycache = added_cmds[keys[nindex], CMDIDX_CACHE];
616 if(!pointerp(fuzzyfuns) || sizeof(fuzzyfuns)<=i)
MG Mud User88f12472016-06-24 23:31:02 +0200617 catch(raise_error(
618 "AddCmd-Auswertung: CatchAll-Kommando \""+keys[nindex]+"\""
619 " wurde waehrend der Ausfuehrung veraendert oder geloescht. "
620 "Klartext: Das ist schlecht. Sucht ein RemoveCmd? ");
621 publish);
Zesstra16698ff2016-12-13 22:19:16 +0100622 else if(_execute(fuzzycache[i]||fuzzyfuns[i], str, 0)) {
MG Mud User88f12472016-06-24 23:31:02 +0200623 GiveEP(EP_CMD, query_verb());
624 return 1;
625 }
626 }
627 }
628 }
629 return 0;
630}
631
632void init() {
633 add_action("_cl","",1);
634}
635
636
Zesstra16698ff2016-12-13 22:19:16 +0100637private void _check_importentry(string ind, mixed *fun, mixed *flag,
638 mixed *rule, int|mixed id, mixed *cachedcl) {
Christian Georg Beckerb128c652017-01-08 18:03:08 +0200639 // Check der Stringmethoden: Fehler werden abgefangen, um generelles
640 // Kopieren zu ermoeglichen. Es koennen aber Kommandomethoden verloren gehen.
Zesstra16698ff2016-12-13 22:19:16 +0100641 cachedcl = map(fun, function int|closure(string clfun) {
642 closure ret;
643 catch(ret=_check_stringmethod(
644 clfun, 1,
645 "Warnung SetProp(P_COMMANDS): ");
646 publish);
647 return ret;
648 });
649 if(sizeof(fun)!=sizeof(flag) || sizeof(fun)!=sizeof(rule))
650 raise_error("SetProp(P_COMMANDS): corrupt commands-mapping.\n");
MG Mud User88f12472016-06-24 23:31:02 +0200651}
652
Zesstra16698ff2016-12-13 22:19:16 +0100653private void _cleanup_exportcl(string ind, mixed *fun, mixed *flag,
654 mixed *rule, int|mixed id,
Christian Georg Beckerb128c652017-01-08 18:03:08 +0200655 mixed *cachedcl) {
Zesstra16698ff2016-12-13 22:19:16 +0100656 cachedcl = allocate(sizeof(fun));
MG Mud User88f12472016-06-24 23:31:02 +0200657}
658
659static mapping _query_commands() {
Zesstra16698ff2016-12-13 22:19:16 +0100660 mapping ret;
661 if(mappingp(added_cmds)) {
662 ret = deep_copy(added_cmds);
Christian Georg Beckerb128c652017-01-08 18:03:08 +0200663 // Closures im Cache werden immer geloescht, da sonst versehentlich
664 // doch exportiert werden koennte
665 walk_mapping(ret, #'_cleanup_exportcl);
Zesstra16698ff2016-12-13 22:19:16 +0100666 }
667 return ret;
MG Mud User88f12472016-06-24 23:31:02 +0200668}
669
Zesstra16698ff2016-12-13 22:19:16 +0100670static mapping _set_commands(mapping commands) {
671 if(!commands) added_cmds=(mapping)0;
672 else if(mappingp(commands)) {
673 if(widthof(commands) != CMDS_WIDTH)
674 raise_error("SetProp(P_COMMANDS): corrupt commands-mapping.\n");
675 mapping tmp = deep_copy(commands);
676 walk_mapping(tmp, #'_check_importentry);
Zesstradd69a342017-01-08 19:46:37 +0100677 added_cmds = tmp;
Zesstra16698ff2016-12-13 22:19:16 +0100678 }
679 return _query_commands();
680}