next up previous contents index
Next: ,,... und siehe, eine Up: Schritt für Schritt ins Previous: Das Betreten des Rasens

Subsections


,,...ein Strom, zu wässern den Garten...''

Nach der Beschreibung befindet sich mitten im Garten eine Quelle, von denen Flüsse in vier Richtungen laufen. Hier greifen wir nicht zur Schiebekulisse, sondern definieren eine Klasse für die Eigenschaften der verschiedenen Gewässer. Flüssigkeiten sind immer etwas schwierig in der Handhabung, wie man sieht:

Wasser? Klasse!

Class  Wasser 
 with  description "Das Wasser ist klar und rein. Du siehst bis auf den 
                    Grund hinunter. Bunte Fische schwimmen darin umher.", 
       name 'Bach' 'Wasser' 'Gewaesser' 'Grund', 
       before [; 
         search: <<examine self>>;

         lift: "Du spritzt etwas Wasser in die ohnehin schon feuchte  
                Luft."; 
         touch: "Du greift mit deinen Händen ins klare Wasser. Die

                Fische nehmen Reißaus.";  
         take, eat, drink, taste : "Du schöpfst mit den Händen aus dem  
                klaren Wasser und trinkst einige Schlucke. Das Wasser  
                schmeckt erquickend."; 
         smell: "Das Wasser riecht frisch."; 
         transfer, empty, emptyT: "Das könnte Ewigkeiten dauern."; 
         fill:  "Es ist schon genug Wasser darin"; 
         enter: if (child(player)) 
                { 
                    print "Du legst erst alles ab, was du bei dir 
                           trägst.^"; 
                    while (child(player))  
                           move (child(player)) to limbo; 
                } 
       react_before [; 
         fill: if (noun ~= self) "Du brauchst ein Behältnis für das Wasser."; 
         Exit: if (player in self) 
               { 
                   print "Du gehst wieder an Land"; 
                   if (child(limbo)) 
                   { 
                       while (child(limbo))  
                              move (child(limbo)) to player; 
                       print " und nimmst deine Sachen an dich.^"; 
                   } 
                   else  
                       print ".^"; 
                   playerto (parent(self)); 
                   return true;  
                } 
         swim: if (player notin self)  
                   <<enter self>>; 
               else  
                   "Du schwimmst ein paar Züge. Das Wasser ist 
                    erfrischend."; 
         take, putOn, Wear, remove: 
               if ((player in self) && (noun in limbo)) 
                   "Du hast ", (den) noun , " doch erst am Ufer abgelegt, 
                    damit ", (er) noun, " nicht nass wird.";  
         ], 
       after [; 
         Enter: "Du gehst einige Schritte ins Wasser und lässt dich 
                 durch das kühle Nass erfrischen."; 
         ], 
       inside_description [;

         print "Du bist im Wasser.^"; 
         ], 
       add_to_scope [ sache; 
         objectloop (sache in limbo) 
         { 
                AddToScope(sache); 
         } 
        ], 
 has   scenery container enterable open; 

Behälter und ihre Eigenschaften

Das Wasser ist ausgestattet wie ein Behälter - container - in den auch die Spielerfigur hinein kann - enterable - und zwar - open - ohne ihn vorher aufmachen zu müssen. Man kann Behälter auch verschließen und sogar zusperren; dazu aber später mehr beim Thema ,,Türen''. Solange der Behälter offen ist, ist fällt Licht aus dem Raum in ihn hinein; wenn er geschlossen ist, geht das nur dann, wenn er auch die Eigenschaft transparent besitzt. Die capacity habe ich hier weggelassen, also ist sie auf 100 gesetzt.

Ein Behälter hat auch eine Innenansicht; sie steht in der property inside_description. Diese Beschreibung ist hier so geschrieben (mit print statt print_ret), dass sie nicht true, sondern false zurückgibt. Dies bewirkt, dass die Beschreibung des Raums und die inside_description ausgegeben werden; stünde hier nur "Du bist im Wasser.", würde implizit true zurückgegeben und die Beschreibung des Raums weggelassen werden.

Wieder legt Inform nur fest, dass sich die Spielerfigur im Behälter befindet. Was sie dort macht, ob sie schwimmt, kocht oder eingefroren wird, entscheidet die Autorin.

return, richtig und falsch

Beim Lesen und Schreiben von Inform-Code ist es überlebenswichtig, sich immer darüber im Klaren zu sein, ob bzw. welche Codezeile einen Rücksprung mit true oder false auslöst:23

return true, rtrue immer true
return false, rfalse immer false
String in Anführungszeichen in einer Zuweisung oder nach print ohne Rücksprung
print_ret immer true
String in Anführungszeichen als Kurzschreibweise für print_ret true
Ende einer Routine ], unselbständig, innerhalb eines Objekts false
Ende einer Routine ]; selbständig, außerhalb eines Objekts true

In einer before-property bedeutet ein Rücksprung mit true soviel wie: Ende der Aktion - nichts passiert weiter. Es ist gängiger Programmierstil, verschiedene logisch aufeinanderfolgende Abbruchkriterien nicht in eine verschachtelte if-Anweisung zu packen, sondern mehrere if-Anweisungen mit - bedingten - Rücksprungbefehlen aufeinanderfolgen zu lassen.24

Die Umkleidekabine oder das Verschieben von Child-Objekten

Eine Besonderheit ist die virtuelle unsichtbare Umkleidekabine. Sie ist zunächst nichts als ein freischwebendes Objekt ohne Eigenschaften:

Object limbo;
Vor dem Betreten des Wassers wird in der before-property die Aktion ##Enter abgefangen: Wenn die Spielerfigur Besitztümer bei sich trägt - die Funktion child(player) also einen Wert ungleich 0 (false) zurückgibt - werden diese Stück für Stück mit einer while-Schleife und dem Befehl move to solange nach limbo verschoben, bis keine mehr da sind. Beim Verlassen des Wassers (Regel für die Aktion ##Exit in der property react_before) erhält die Spielerfigur die in limbo geparkten Gegenstände auf die gleiche Weise zurück. Versucht er sie zu nehmen (Regel für ##take), während er im Wasser ist, wird der Versuch zurückgewiesen (zur Erinnerung: noun ist das direkte Objekt des vom Spieler eingegebenen Befehls).

Beachte:

die Regeln in before werden nur ausgeführt, wenn das Wasser direktes Objekt der Aktion ist (,,Gehe ins Wasser''); die Regeln in react_before werden bereits ausgeführt, wenn das Wasser im Blickfeld ist (die Spielerfigur das Wasser sehen kann). Einige der von react_before behandelten Aktionen sind nur sinnvoll, wenn die Spielerfigur tatsächlich im Wasser drin ist, die Abfrage (player in self) also true zurückliefert.

Beachte:

die Programmstellen, an denen die Besitztümer de Spielfigur hin und zurück verschoben werden, enthalten keine Definition eines Rückgabewerts (mit return true oder return false). (Es handelt sich in beiden Fällen um mit eckigen Klammern [ ] eingeschlossene Routinen innerhalb einer Objektdefinition. Solche ,,unselbständigen'' Routinen geben immer false zurück, wenn es nicht anders angegeben ist, s.o.) Das führt hier dazu, dass zwar die Besitztümer verschoben werden, der Code aber sonst nicht beeinflusst, wie die Spielfigur baden geht und welche Meldungen von anderen Programmteilen dabei ausgegeben werden.

add_to_scope und objectloop

Die abgelegten Gegenstände sind zwar ganz woanders, aber für den Spieler trotzdem sichtbar. Dies wird erreicht durch die property add_to_scope. Scope heißt soviel wie Reichweite oder Blickfeld und steuert, welche Objekte der Spieler sehen kann. Versucht er, z.B. ein Objekt zu untersuchen, das nicht im Blickfeld ist, erhält er die Rückmeldung: ,,Du kannst nichts dergleichen sehen.''

add_to_scope steuert, welche Objekte zusammen mit dem Wasser automatisch im Blickfeld sind - egal wo sie sich tatsächlich befinden. Ginge es nur um einen oder mehrere immer gleiche Gegenstände, würde es ausreichen, einfach die Namen der Objekte hier aufzuführen. Da wir im voraus nicht wissen können, welche Gegenstände der Spieler bei sich haben wird, wählen wir einen anderen Weg: Für jeden ins Blickfeld zu rückenden Gegenstand wird die Funktion AddToScope(Gegenstand) einmal aufgerufen.

Mit der oben verwendeten while-Schleife hätten wir hier kein Glück: Ihre Funktionsweise beruht darauf, dass sie die Child-Objekte nacheinander entfernt und von selbst aufhört, wenn keines mehr da ist. Macht nichts - objectloop ist ohnehin eleganter, um eine Schleife über alle Objekte mit einer bestimten Eigenschaft laufen zu lassen. Zur Syntax bitte nachschlagen: 3.1.3. Dort steht auch, warum wir nicht gleich objectloop für alle Schleifen verwendet haben.

Vom Wasser zum Gewässer

Eine Klasse ist für den Spieler noch nicht sichtbar, wir brauchen ein von ihr abgeleitetes Objekt. Eine Quelle ist schnell herbeigezaubert:

Object  Quelle Mitte 
 class  Wasser 
 with   dekl 9, 
        name 'Quelle', 
        short_name "Quelle", 
 has    female; 
Vier Bäche? Das ist Luxus; wir brauchen nur einen, den wir wieder als Schiebekulisse ausgestalten:

Object  Bach 
 class  Wasser 
 with   short_name "Bach", 
        name 'bach' 'fluss', 
        adj "klar", 
        dekl 1, 
        initial "Ein klarer Bach fliesst durch diesen Teil des 
                 Gartens.", 
        found_in EdenNW EdenSW EdenNE EdenSE, 
 has    male;

Vom Gewässer zur Landschaft

Dort, wo ein Bach fließt, sieht der Garten anders aus. Wir definieren eine Klasse GartenW, die auf der oben schon eingeführten Klasse Garten aufbaut:

Class   GartenW 
 class  Garten

 with   richtung "[Fehler: Klasse GartenW.richtung geerbt, aber nicht 
                  definiert]",

        description [;  
           print_ret "Du bist im Garten Eden. Ringsum sind Beete, Obstbäume,  
                      Gebüsche; alles ist gepflegt und feierlich. Ein klarer  
                      Bach fließt durch diesen Teil des Gartens in ",  
                     (string) self.richtung, 
                     " Richtung."; 
        ];

Hier definieren wir ,,aus dem Stand'' eine neue property - richtung - indem wir sie einfach verwenden und ihr einen Wert zuweisen. In der Klasse ist es noch eine Fehlermeldung, denn jeder Gartenteil soll ja seine eigene Richtung mitbringen. (string) self.richtung gibt den Inhalt von richtung als Text aus. Was das soll, wird klarer, wenn wir uns ein abgeleitetes Objekt der Klasse ansehen, den nordwestlichen Teil des Gartens:

Object  EdenNW

 class  GartenW 
 with   richtung "nordwestliche", 
        se_to Mitte, 
        e_to EdenN, 
        s_to EdenW;

Hier lautet also die Beschreibung: ,,... Ein klarer Bach fließt durch diesen Teil des Gartens in nordwestliche Richtung.''



Footnotes

...23
Auf den ersten Blick sieht das so logisch aus wie ein Tarifplan der Bahn-AG. Es ist aber genial daraufhin optimiert, möglichst wenig explizite return-Befehle schreiben zu müssen. Ein Mathematiker ist ein Mensch, der lieber eine halbe Stunde nachdenkt, als fünf Minuten zu arbeiten.
...24
Beispiele etwa beim Code für die Tür: 2.7.

next up previous contents index
Next: ,,... und siehe, eine Up: Schritt für Schritt ins Previous: Das Betreten des Rasens
Frank Borger
2003-05-02