Kapitel 8: Der Datentyp 'object'
8.1 Einfuehrung
Wenn Du die bisherigen Kapitel gelesen und verstanden hast, kannst Du nun in
LPC alles machen, was moeglich ist (also quasi alles), wenn man mal davon
absieht, dass noch nicht bekannt ist, wie Du Funktionen im eigenen Objekt
aufrufst. Ausserdem kannst Du inzwischen eine create()-Funktion erstellen,
und sie starten, wenn Du das Objekt in den Speicher laedst. Du weisst
(auch wenn wir es nur an einem kurzen Beispiel kurz erklaert haben), dass
die reset()-Funktion benutzt wird, um den Anfangszustand wieder herzustellen
und aufzuraeumen. Diese Funktionen muessen nicht vorkommen, (s. Vererbung)
aber der Driver ueberprueft, ob diese Funktionen vorkommen. Wenn nicht,
stoert es nicht weiter. Du weisst ausserdem, wie man die Datentypen void,
int und string einsetzt.
8.2 Objekte als Datentypen
In diesem Kapitel geht es um einen Datentyp, den es bei C nicht gibt.
Eine Objektvariable zeigt auf ein echtes Objekt, das sich im Arbeitsspeicher
befindet. Es wird wie gehabt deklariert:
object ob;
Es gibt einige beachtenswerte Unterschiede. So macht keinen Sinn, folgende,
nichtabschliessende Auflistung an Operatoren zu benutzen:+, -, +=,/ etc.
Was haette es wohl fuer einen Sinn, einen NPC durch eine Waffe zu dividieren?
Auch macht nicht jede efun Sinn. Aber sie sind mit anderen efuns nutzbar.
8.3 Die Efun this_object()
Diese Efun hat als return-Wert das Objekt, dass sie aufgerufen hat, so es
ein Objekt gibt. Anders ausgedrueckt: this_object() verweist auf das Objekt,
welches Deine Datei darstellt, wenn es gecloned wurde.
Das ist nuetzlich, wenn es von einem anderen Objekt benutzt wird. Angenommen,
Du schreibst Dein eigenes living.c, welches sowohl von /std/player.c (fuer die
User) und auch von /std/npc.c benutzt wird, aber niemals alleine. Du moechtest
mitloggen, wenn set_level() aufgerufen wird, um einem Spieler den Level zu
erhoehen, waehrend es Dir bei NPC egal ist.
Das koennte in etwa so aussehen:
void set_level(int x)
{
if(this_object()->query_interactive())
log_file("level","blah\n");
level=x;
}
Dabei gibt die Funktion query_interactive() eine 0 zurueck, falls das
Objekt kein interaktives Objekt, also ein Spieler, ist.
Da query_interactive() nicht in living.c oder einem anderen Objekt, das
geerbt wird, definiert ist, wuerde if(query_interactive()) einen Fehler
verursachen. this_object() erlaubt den Zugriff auf Funktionen, die nicht
in den endgueltigen Dateien vorhanden sind, ohne einen Fehler zu produzieren.
Verwandte Funktionen sind this_player() und this_living(), die sich aehnlich
verhalten, jedoch sich auf Spieler und Lebewesen beziehen.
Es sind unter dem Strich die spezialisierten Aufrufe von this_object().
8.4 Aufruf von Funktionen in anderen Objekten
Das ist der wichtigste Unterschied zwischen LPC und anderen Sprachen.
Das ist auch der gravierendste Unterschied zwischen dem Datentyp object und
anderen Datentypen.
Bisher haben wir das mehrfach benutzt, ohne genauer darauf einzugehen.
Aufrufe in anderen Objekten kann auf zwei Wege passieren:
object->funktion(parameter);
call_other(object,"funktion", parameter);
Um es mal ganz hart zu sagen: ein MUD ist letztlich nur eine Aneinanderreihung
von Funktionsaufrufen, die durch Spielerkommandos ausgeloest werden.
Hoffentlich habe ich jetzt alle Illusionen zerstoert .
Das wird permanent in den wichtigsten lfuns passieren.
Ueber reset() und create() haben wir bereits gesprochen. Da alle guten Dinge 3
sind, gehen wir nun zur Dritten im Bunde ueber.
8.5 Die lfun: init()
Immer, wenn ein Lebewesen auf ein Objekt trifft, also einen neuen Raum betritt,
einen Raum betritt, wo bereits ein weiteres Objekt sich befindet, wird init()
aufgerufen. Und zwar passiert das in allen beteiligten Objekten.
Die Reihenfolge, in der das passiert, ist fest vorgegeben.
Am Anfang der Abarbeitung steht das ausloesende Objekt, gefolgt von allen
Spielern, allen sonstigen Lebewesen, saemtlichen sonstigen Objekten und dem
Raum, oder allgemein, die Objektumgebung (environment()), in dem das
stattfindet.
Dabei wird immer jedes Objekt einzeln vollstaendig abgearbeitet.
Dazu gehoert, dass alle Objekte in dem Objekt ebenfalls (bis in die tiefste
Ebene) einen init()-Aufruf haben.
Frage:
Was wird zuerst abgearbeitet:
Der Spieler, oder die Waffe, die der Spieler in einem Beutel mit sich fuehrt?
Eine typische init-Funktion sieht so aus:
void init()
{
::init();
if(this_player()->(QueryProp(P_LEVEL)>10))
write(capitalize(this_player()->name())+" ist ein hoeherer Spieler.\n");
}
Frage:
Was macht diese Funktion genau?
QueryProp() das Gegenstueck zu SetProp und capitalize() wandelt bei dem
uebergebenen String den ersten Buchstaben in einen Grossbuchstaben.
8.6 Zusammenfassung
Nun solltest Du genug Erfahrung haben, um wirklich schoenen Code zu produzieren.
Natuerlich kann das nur funktionieren, wenn Du die man-Pages des MUDs vor
Augen haelst, wo viele spezifische Informationen verzeichnet sind. Auch wenn
es immer wieder erwaehnt wurde, ist es nicht zwingend notwendig, alle Details
der MUDLIB zu kennen. Die Grundideen wurden immer wieder angesprochen und sind
sehr wichtig. Das erklaert naemlich oft genug, warum bestimmte Sachen so
und nicht anders funktionieren, wie sie funktionieren.
Nun noch ein paar Hinweise fuer schoenen und interessanten Code.
Erstelle jeden Raum neu. Benutze keine default-Raeume, die permanent gleich
aussehen.
Fuelle sie mit interessanten Funktionen, die den Spieler an Blumen riechen
lassen oder Pfuetzen leertrinken lassen etc. Erstelle Waffen, die magische
Faehigkeiten haben und nach 2 Resets zerstoert werden, dabei dem Spieler eine
schoene Meldung ausgeben, und ihm schaden.
Das solltest Du inzwischen hinbekommen. Wenn Du bis zu diesem Punkt gekommen
bist, solltest Du auch faehig sein, wenn Du etwas machen moechtest, und jeder
sagt, dass es in LPC nicht umsetzbar ist, diese Dinge doch hinzubekommen.
Ausserdem wirst Du Fehler in fremdem Code sehr schnell erkennen.
Nico'Gralkor'Kammel
gralkor@fantasywelt.net
gralkor@tamedhon
nach dem LPC-Tutorial von George Reese.