Erzeugung von Meldungen, aehnlich der bei den Bewegungen bzw. bei ----------------------------------------------------------------- Nahrung und Getraenken. ----------------------- Oft hat man das Problem, dass man Objekte erschaffen will, in denen man waehrend des create() Meldungen mittels set_... Funktionen setzen moechte, in denen aber Satzteile enthalten sind, die abhaengig vom Namen und Geschlecht eines anderen Objektes sind. Beispiel: void create() { set_other_chew_message("Garthan kaut an seinem Brot rum.\n"); ======= ====== ---- } Die doppelt unterstrichenen Teile haengen also davon ab, wer das Brot SPAETER isst. Das Problem ist, dass zur 'Erschaffungszeit' des Objektes, also im create() NOCH NICHT feststeht, wer das sein wird! Das ist sozusgagen eine zeitliche Vorwaertsreferenz, die erst in der Zukunft ausgewertet werden kann. Wie loest man das Problem? ========================== Es funktioniert NICHT, in dem man einfach schreibt: ======= void create() { set_other_chew_message(Der(this_player())+" kaut an "+seinem()+" rum.\n"); ================= =-=-=-=- } Es wird dann fuer Der(), der Name des Spielers eingesetzt, das Objekt (also das Brot) erschaffen hat, also den create() aufgerufen hat und nicht der Name des Spielers, der es SPAETER einmal essen wird! Fuer seinen() gilt das gleiche. Man muss also wirklich einen anderen Weg gehen. Hier in UNItopia macht man das entweder 'hardcore'-maessig mit Closures, was aber hier nicht demonstriert werden soll. [Die im folgenden beschriebene Methode verwendet intern in der Tat Closures.] Um das Problem zu loesen muss man das Brot (hier als allgemeines Objekt eben die Nahrung (/obj/nahrung)) so programmieren, dass es moeglich wird (siehe PROGRAMMIERERSEITE). ANWENDERSEITE ============= Der Einfachheit halber wird jetzt davon ausgegangen, dass die Nahrung entsprechend programmiert wurde. Fuer den Anwender verbleibt nur noch der korrekte Aufruf im create() oder von aussen durch ein call_other (->). Das funktioniert dann so: (aufgerufen z.b von einem Raum aus) brot = clone_object("/obj/nahrung"); brot->set_name("brot"); brot->set_gender("saechlich"); brot->set_other_chew_message("$Der(OBJ_TP) kaut an $seinem() rum.\n"); ============ =-=-=-=-= Die Vorwaertsreferenzen werden durch symbolische Grammatikaufrufe im String realisiert: $Der(...) und $seinem(...) Wird die Meldung dann im Objekt SPAETER tatsaechlich gebraucht, so werden diese Referenzen dann aufgeloest und durch richtige Grammatikaufrufe ersetzt, kurz bevor die Meldung an den Spieler ausgegeben wird. Damit das funktioniert, muessen diese Objekte (hier /obj/nahrung) entsprechend programmiert sein. Wie man das relativ einfach macht kommt jetzt: PROGRAMMIERERSEITE ================== Um ein Objekt mit der oben geschilderten Funktionalitaet zu erhalten muss man ein paar Veraenderungen in den set_... Funktionen und bei der Ausgabe der Meldung einbauen. Statt der string Variable, die sonst im Objekt die Meldung beinhalten, definiert man nun eine closure Variable: Statt string other_chew_message; schreibt man also closure other_chew_message; Die set_other_chew_message() Funktion sieht statt void set_other_chew_message(string str) { other_chew_message = str; } dann so aus: void set_other_chew_message(mixed str) { other_chew_message = mixed_to_closure(str); } Wenn man die Meldung dann ausgeben will benutzt man statt say(.....other_chew_message...); den Aufruf say(closure_to_string(other_chew_message)); Das war's von Programmiererseite aus auch schon. NACHTEILE: ========== Bei der beschriebenen Methode hat das Objekt keine Moeglichkeit, eine query_other_chew_message() einzubauen, die das liefert, was man eingegeben hat. Wenn man das will muss man den string den man bei set_... uebergibt zusaetzlich zur Closure im Objekt speichern und bei query_... dann zurueckgeben. (Konsistente query_.... / set_... Paare sind fast immer sinnvoll.) Die Objekte speichern die Closures bei save_objekt nicht so ab, dass sie nach restore_object wieder verwendet werden koennen. AUFRUFMOEGLICHKEITEN FUER CLOSURE_TO_STRING =========================================== Die folgenden vier Syntaxen (? ;-)) sind moeglich (als Beispiel hier messages wie sie im Standard essen/nahrung auftreten): 1) Historischer UND FALSCHER!!! Aufruf closure_to_string("wird schlecht von "+seinem(steak)+ " und "+er()+" kotzt alles wieder raus!\n"); Beim Setzen dieser Meldung weiss man schliesslich in der regel noch nicht, wem das Steak gehoert, und wer es wieder ausspuckt!!! NICHT SO!!! Also muss man es anders machen: 2) In einem uebergebenen String wird jeder String nach einer Funktion der Art ".... $fun(arg) ..." durchsucht. Als Argument sind die Defines OBJ_XY aus deklin.h moeglich, wird dies nicht gefunden, wird als Argument this_object() beim Aufruf der Funktion benutzt. (vgl. Funktionsweise der Bewegungsmeldungen) Man erhaelt damit: closure_to_string(mixed_to_closure( "$Dem(OBJ_TP) wird schlecht von $seinem() und "+ "$er(OBJ_TP) kotzt alles wieder raus!\n")); 3) Ein einzelnes closure-programm, das den String erzeugt, closure_to_string( lambda(({}), ({#'+,({#'Dem,({#'this_player})}), ({#'+," wird schlecht von ", ({#'+,({#'seinem,({#'this_object})}), ({#'+," und ", ({#'+,({#'er,({#'this_player})}), ({#'+," kotzt ", ({#'+,({#'wen,({#'this_object}),ART_ER}), " wieder raus!\n" }) }) }) }) }) }) }) )); 4) Ein mixed-array bestehend aus einzelnen closure-programmen und strings. Alle werden aneinander gehaengt (nachdem die closures durchlaufen wurden), siehe auch 2): closure_to_string(mixed_to_closure(({ ({#'Dem,({#'this_player})}), " wird schlecht von ", ({#'seinem,({#'this_object})}), " und ",({#'er,({#'this_player})}), " kotzt ", ({#'wen,({#'this_object}), ART_DIESER}), " wieder raus!\n"}))); oder auch gemischt mit 2) : closure_to_string(mixed_to_closure( ({"$Dem(OBJ_TP) wird schlecht von $seinem(OBJ_TO)", " und $er(OBJ_TP) kotzt ", ({#'wen,({#'this_object}),ART_DIESER}), " wieder raus!\n"}) )); Beispiele: ========== Anwendung siehe /i/object/nahrung.c siehe /i/object/tuer.c Sourcen: siehe /secure/deklin.c (string_parser, mixed_to_closure) siehe /i/item/messages.c (closure_to_string)