| |
| ADDCMD() - BEISPIELE |
| ******************** |
| |
| |
| FUNKTION |
| ======== |
| |
| varargs void AddCmd(mixed cmd, mixed func, mixed flag); |
| |
| |
| BEMERKUNGEN |
| =========== |
| |
| Die hier aufgefuehrten Komplexbeispiele sind zum Verstaendnis gedacht, |
| daher fuehren sie oft Alternativen auf. Die letzte Variante ist dann |
| jeweils diejenige, welche am leichtesten das Problem loesen koennte. |
| Falls die einem zu komplex ist, hilft vielleicht die vorletzte. |
| |
| |
| BEISPIELE |
| ========= |
| |
| |
| Einfache Beispiele |
| ------------------ |
| |
| 1. SIMPEL: ganz simpel, beinahe wie add_action() |
| |
| AddCmd("befiehl","action_befehlen"); |
| // ... |
| int action_befehlen(string str) { |
| if(!str || !strlen(str)) |
| // Fehlermeldung, falls gar keine Funktion 1 dafuer zurueckgibt |
| notify_fail("Was willst du befehlen?!\n"); |
| else { |
| write("Du befiehlst \""+str+"\", und alle folgen!\n"); |
| say(TP->Name(WER)+" befiehlt \""+str+"\", und du folgst!\n"); |
| return 1; // ERFOLG - Abbruch der Kommandoauswertung |
| } |
| return 0; // MISSERFOLG - Fehlermeldung oben gesetzt |
| } |
| |
| 2. SIMPEL .. weitere Beispiele |
| |
| AddCmd(({"kletter","klettere"}),"action_klettern" ); |
| AddCmd(({"renn","renne"}),#'action_rennen); |
| |
| 3. REGELN: eine komplexere Regel |
| |
| AddCmd("loesch|loesche|ersticke&feuer|brand|flammen&decke|wolldecke", |
| "action_loeschen", |
| "Was willst du loeschen?|Womit willst du loeschen?"); |
| |
| 4. REGELN: mit Platzhaltern im Fehlerstring |
| |
| AddCmd("spring|springe|huepf|huepfe&von|vom&baum|ast|eiche", |
| #'action_huepfe, |
| "Willst du von etwas @verben?|Von wo willst du @verben?"); |
| |
| 5. SCHLECHT: eine unscharfe Regel - sie sollten eine Ausnahme sein |
| (!) |
| |
| AddCmd("kletter","fun_klettern",1); |
| |
| 6. FALSCH: sehr schlecht, kein Imperativ verwendet |
| |
| AddCmd("lese","eval_lesen"); |
| |
| ausserdem sollte man fuer Lese-Syntaxen AddReadDetail benutzen |
| |
| 7. SIMPLE REGEL |
| |
| static int action_jump(string str); // Prototype (wegen closure) |
| // ... |
| AddCmd("spring|springe|huepf|huepfe&von&baum|ast",#'action_jump, |
| "Willst Du von etwas @verben?|Wovon willst Du @verben?"); |
| // ... |
| static int action_jump(string str) { |
| write(break_string("Du springst vom Baum und kommst hart auf!",78)); |
| this_player()->move((XXXROOM+"boden"), M_GO, 0, |
| "springt unelegant vom Baum","faellt vom Baum"); |
| this_player()->Defend(random(100),({DT_BLUDGEON}),([SP_RECURSIVE:1]), |
| this_object()); |
| return 1; |
| } |
| |
| 8. SIMPLE REGEL OHNE METHODE |
| |
| mit Regeln kann man auch Aktivitaeten im Raum erlauben, ohne eine |
| Funktion aufrufen zu muessen: die letzte Regel ist fuer Spieler |
| unmoeglich zu erfuellen, die dazugehoerige Fehlermeldung wird mit |
| dem ^ (write-Flag) versehen und entsprechend an den Spieler (und |
| den Raum (hinter dem ^)) ausgegeben |
| |
| AddCmd("spring|springe&herunter|runter&\n\bimpossible",0, |
| "Wohin oder wovon willst Du springen?|" |
| "Du springst vom Baum und kommst hart auf.^" |
| "@WER1 springt vom Baum und kommt hart auf."); |
| |
| |
| Komplexbeispiel: Regeln mit Fehlermeldungen |
| ------------------------------------------- |
| |
| |
| Variante 1a, OHNE REGELN |
| ~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Wenn man keine Regeln verwendet, muss man die Eingabe selbst |
| auswerten. |
| |
| AddCmd(({"bohr","bohre"}),#'action_bohren); |
| // ... |
| private int action_bohren(string str) { |
| string *tmp; |
| notify_fail("Wo willst (etwas) Du bohren?\n"); |
| if(!str) return 0; // Tja, keine Argumente ... |
| tmp=explode(str," "); // nach " " in Argument-Array aufspalten |
| if((i=member(tmp,"loch"))>=0) { // aha, ab jetzt uebernehmen wir :) |
| if((j=member(tmp[(i+1)..],"in"))<0 && |
| (j=member(tmp[(i+1)..],"durch"))<0) |
| write("Willst Du das Loch in etwas bohren?\n"); |
| else if((i=member(tmp[(j+1)..],"boden"))<0 && |
| (i=member(tmp[(j+1)..],"erde"))<0) |
| write("In/Durch was willst du das Loch bohren?\n"); |
| else { |
| write("Du bohrst ein Loch in den Boden.\n"); |
| say(this_player()->Name(WER)+" bohrt ein Loch in den Boden.\n"); |
| } |
| return 1; // "bohre loch" war so eindeutig, dass nur diese |
| // Methode gemeint sein konnte, also brechen wir die |
| // weitere Auswertung auf jeden Fall ab (und geben |
| // eine write-Fehlermeldung) |
| } // end if(..."loch") |
| return 0; // "bohre" allein muss nicht diese Methode meinen, |
| // also nur obige notify_fail()-Meldung, falls |
| // sich nach dieser Methode gar keine sonst |
| // angesprochen fuehlt |
| } // end fun |
| |
| |
| Variante 1b, OHNE REGELN, Alternative |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| prinzipiell koennte die Methode action_bohren() auch so aussehen, |
| ist aber nicht ganz so flexibel: |
| |
| private int action_bohren(string str) { |
| string unused; |
| if(!str || (sprintf(str,"loch in erde%s", unused)!=1 && |
| sprintf(str,"loch durch erde%s", unused)!=1 && |
| sprintf(str,"loch in boden%s", unused)!=1 && |
| sprintf(str,"loch durch boden%s", unused)!=1)) |
| notify_fail("Willst Du in irgendwas ein Loch bohren?\n"); |
| else { |
| // ... |
| return 1; |
| } |
| return 0; |
| } |
| |
| |
| Variante 2, MIT REGEL |
| ~~~~~~~~~~~~~~~~~~~~~ |
| |
| das gleiche in etwa mal als einfache Regel |
| |
| AddCmd("bohr|bohre&loch&in|durch&erde|boden",#'action_bohren, |
| "Was willst du (wohin) bohren?|" |
| "Willst du das Loch in etwas bohren?|" |
| "Wohin willst du das Loch bohren?"); |
| // ... |
| private int action_bohren(string str, mixed *param) { |
| write("Du bohrst ein Loch in den Boden.\n"); |
| say(this_player()->Name(WER)+" bohrt ein Loch in den Boden.\n"); |
| // ... |
| return 1; |
| } |
| |
| |
| Variante 3, MIT REGEL UND FEHLERMELDUNG |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| und nun mit Fehlermeldungen mit Ersetzungen, so dass wir mehr auf |
| die Eingaben des Spielers eingehen |
| |
| AddCmd("bohr|bohre&loch&in|durch&erde|boden",#'action_bohren, |
| "Was willst du (wohin) @verben?|" |
| "Willst du das Loch in etwas @verben?|" |
| "@WER3 was willst du das Loch @verben?"); |
| // ... |
| private int action_bohren(string str, mixed *param) // ... |
| |
| |
| Variante 4, MIT REGEL, FEHLERMELDUNG UND RETURN 1 |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| In Variante 1 kam sinnvollerweise sehr frueh der Abbruch mit |
| "return 1;" und mit Ausgabe von write-Fehlermeldungen, das koennen |
| wir auch direkt und ohne eigene Methode. |
| |
| AddCmd("bohr|bohre&loch&in|durch&erde|boden",#'action_bohren, |
| "Was willst du (wohin) @verben?|" |
| "Willst du das Loch in etwas @verben?^|" |
| "@WER3 was willst du das Loch @verben?^"); |
| // ... |
| private int action_bohren(string str, mixed *param) // ... |
| |
| |
| Variante 5, MIT REGEL, FEHLERMELDUNG, RETURN 1, OHNE FUN |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| // und falls in action_bohren() nichts ausser Ausgaben passiert, koennen |
| // wir uns die auch ganz sparen indem wir eine nichterfuellbare Regel |
| // samt Fehlermeldung bauen |
| AddCmd("bohr|bohre&loch&in|durch&erde|boden&\nimpossible",0, |
| "Was willst du (wohin) @verben?|" |
| "Willst du das Loch in etwas @verben?^|" |
| "@WER3 was willst du das Loch @verben?^|" |
| "Du @verbst ein Loch @WER3 den Boden.^@WER1 @verbt " |
| "ein Loch @WER3 den Boden."); |
| |
| |
| Komplexbeispiel: Spezialregeln @PRESENT und @ID |
| ----------------------------------------------- |
| |
| |
| Variante 1, OHNE REGELN |
| ~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Oft agieren Kommandos auf Objekten im Raum, diese muessen dabei per |
| present() identifiziert werden: Beispiel ist ein Geldautomat |
| (Hinweis: dieses Beispiel dient der Illustration, die |
| Funktionalitaet an sich sollte man besser mit einem Container mit |
| PreventInsert() erzeugen.) |
| |
| AddCmd(({"stopf","stopfe"}),#'action_stopf); |
| // ... |
| private int action_stopf(string str) { |
| string was, unused; |
| if(str && (sprintf("%s in automat%s", was, unused)==2 || |
| sprintf("%s in geldautomat%s", was, unused)==2 || |
| sprintf("%s in bankomat%s", was, unused)==2) { |
| object o = present(was, this_player()); |
| if(o) { |
| if(o->QueryProp(...)) { |
| write(break_string( |
| "Du stopfst "+o->name(WEN,1)+" in den Automaten.",78)); |
| say(...); |
| } else { |
| write(break_string( |
| "Du versuchst "+o->name(WEN,1)+" in den Automaten zu stopfen, " |
| "aber "+o->QueryPronoun(WER)+" passt nicht hinein.",78)); |
| say(...); |
| } |
| } else { |
| write("Was willst du in den Automaten stopfen?\n"); |
| say(....); |
| } |
| return 1; |
| } |
| notify_fail("Was willst du wohin stecken?\n"); |
| return 0; |
| } |
| |
| |
| Variante 2, MIT REGEL |
| ~~~~~~~~~~~~~~~~~~~~~ |
| |
| einerseits koennen wir auf diese Weise das Finden von Objekten in |
| Inv und Env in die AddCmd()-Regel integrieren und uns andererseits |
| das Aufzaehlen aller IDs des Automaten ersparen. |
| |
| Wie immer werden die gefundenen Matches als Parameterarray an die |
| angegebene Methode uebergeben. Das Array enthaelt die mit @PRESENT |
| und @ID gefundenen Treffer praktischerweise als Objekte. |
| |
| AddCmd("steck|stecke&@PRESENT&in&@ID",#'action_stopf, |
| "Was willst du wohin stopfen?|" |
| "Willst du @WEN2 in etwas stopfen?|" |
| "Wohinein willst du @WEN2 stopfen?"); |
| // ... |
| private int action_stopf(string str, mixed *param) { |
| if(param[0]->QueryProp(...)) { |
| write(break_string( |
| "Du stopfst "+param[0]->name(WEN,1)+" in den Automaten.",78)); |
| say(...); |
| } else { |
| write(break_string( |
| "Du versuchst "+param[0]->name(WEN,1)+" in den Automaten zu " |
| "stopfen, aber "+param[0]->QueryPronoun(WER)+" passt nicht " |
| "hinein.",78)); |
| say(...); |
| } |
| return 1; |
| } |
| |
| |
| Komplexbeispiel: gleiches Verb, mehrere Regeln |
| ---------------------------------------------- |
| |
| Das Problem mehrerer Regeln fuer ein Kommandoverb besteht darin, |
| dass letztlich nur eine der Fehlermeldungen zum Tragen kommt - |
| welche genau, ist etwas vage. Dabei kann man sich auf eines |
| verlassen: juengere AddCmd werden zuerst ausgewertet. Wenn sich das |
| aendert, tretet euren EM. |
| |
| |
| Problem 1: Mehrere Regeln weil mehrere Zwecke |
| --------------------------------------------- |
| |
| |
| Variante 1 - GLEICHLAUTENDE FEHLERMELDUNG |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Fuer alles wird eine identische Fehlermeldung gesetzt, das ist |
| natuerlich nicht sehr flexibel oder schoen oder man versucht eine |
| bessere Regel zu schaffen, was hier durch die Moeglichkeit von zwei |
| oder drei Parameter unmoeglich ist |
| |
| AddCmd("kriech|krieche&hoch|hinauf|hinaus|heraus|raus",#'result_kriech, |
| "Wohin willst Du kriechen?"); |
| AddCmd("kriech|krieche&nach&oben",#'result_kriech, |
| "Wohin willst Du kriechen??|Wohin willst Du kriechen?"); |
| AddCmd("kriech|krieche&aus&loch|grube|falle",#'result_kriech); |
| "Wohin willst Du kriechen?|Wohin willst Du kriechen?"); |
| |
| |
| Variante 2 - EIGENE AUSWERTUNG |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| Statt der Verwendung mehrerer aehnlicher AddCmd() bietet sich die |
| eigene Weiterauswertung an, was durch die Uebergabe der |
| getriggerten Argumente erleichtert wird. |
| |
| AddCmd("kriech|krieche&hoch|hinauf|hinaus|heraus|raus|aus|nach", |
| #'result_kriech, |
| "Wohin willst Du kriechen?"); |
| // ... |
| private int result_kriech(string str, mixed *extra) { |
| if(member(extra,"aus")>=0 && |
| !sizeof(({str}),"*.\\<(hoehle|grube|falle)\\>.*")) |
| notify_fail("Woraus willst Du kriechen?\n"); |
| else if(member(extra,"nach")>=0 && strstr(str,"oben")<0) |
| notify_fail("In welche Richtung willst Du kriechen?\n"); |
| else if(this_player()->QueryAttribute(A_DEX)>10 || |
| member(holding_root,this_player())) { |
| write("Du kriechst mit Muehe heraus.\n"); |
| this_player()->move((XXXROOM+"draussen"), M_GO, 0, |
| "kriecht mit Muehe aus der Grube", |
| "kriecht aus einer Grube"); |
| return 1; |
| } else |
| write("Du bist zu ungeschickt, halt Dich irgendwo fest.\n"); |
| return 1; |
| } |
| return 0; |
| } |
| |
| |
| Problem 2: mehrere Regeln aufgrund von optionalen Parametern |
| ------------------------------------------------------------ |
| |
| Manchmal will man optionale Parameter erlauben, die aber eine |
| Wirkung zeigen sollen. Hierbei ist die Reihenfolge der AddCmd()- |
| Anweisungen und ggf. deren Aufbau entscheidend. |
| |
| AddCmd("schlag|schlage&@ID&hart",#'action_schlag_hart, |
| "Was oder wen willst du @verben?|" |
| "Wie willst du @WEN2 schlagen?"); |
| AddCmd("schlag|schlage&@ID",#'action_schlag, |
| "Was oder wen willst du @verben?"); |
| |
| Da juengere AddCmd aelteren vorgehen, wird die komplexere Regel samt |
| ihrer Fehlermeldung nie ausgewertet, da ein "schlag ball hart" auch |
| die zweite Regel triggert. |
| |
| Anders herum: |
| |
| AddCmd("schlag|schlage&@ID",#'action_schlag, |
| "Was oder wen willst du @verben?"); |
| AddCmd("schlag|schlage&@ID&hart",#'action_schlag_hart, |
| "Was oder wen willst du @verben?|" |
| "Wie willst du @WEN2 schlagen?"); |
| |
| Jetzt wird die komplexere Regel zuerst ueberprueft und triggert |
| auch die richtige Funktion. |
| Leider kommt die Fehlermeldung nie zum Tragen, denn was durch Regel 2 |
| durchfaellt, triggert entweder Regel 1 oder faellt auch durch Regel 1 |
| durch und ueberschreibt dabei die Meldung. |
| |
| AddCmd("schlag|schlage&@ID",#'action_schlag, |
| "Was oder wen willst du wie @verben?"); |
| AddCmd("schlag|schlage&@ID&hart",#'action_schlag_hart); |
| |
| Das ist zwar auch nur fast perfekt, besser wird es aber nicht. |
| |
| Letzte Aenderung: 20.11.2019, Arathorn |