Funktionen
Eine Skriptfunktion fasst ein bestimmtes Stück Code zusammen, welches dann von der Engine oder einem anderen Script aufgerufen (also ausgeführt) werden kann. Das gesamte Script eines Szenarios oder Objekts ist in solchen Funktionen untergebracht.
Parameter und Rückgabewerte
An eine Scriptfunktion können beim Aufruf bis zu 10 Parameter übergeben werden. Diese können dann innerhalb der Funktion ausgewertet und verwendet werden. Beim Beenden kann eine Funktion mit dem Befehl return einen einzelnen Wert an den Aufrufer zurückgeben.
Syntax
Eine einfache Funktionsdeklaration könnte z.B. folgendermaßen aussehen:
func MyFunction() { Log("My function has been called!"); }
Das Script der Funktion steht in einem sogenannten Block (der Bereich zwischen '{' und '}' ). Vor diesem Block steht die Funktionsdeklaration. Diese beginnt mit "
func
", gefolgt von dem Funktionsnamen (hier: "MyFunction"). In der Klammer nach dem Funktionsnamen können Funktionsparameter definiert werden (s.u.).Beim Aufruf der Funktion wird die Meldung "My function has been called!" ausgegeben.
func ShowNumber(int number) { Log("ShowNumber has been called with parameter %d!", number); }
Hier wurde ein Parameter mit dem Namen "number" vom Typ "int" definiert. Wird beim Aufruf der Funktion ein Parameter angegeben, so erhält "number" den übergebenen Wert. Der übergebene Wert wird in diesem Fall in einer entsprechenden Nachricht ausgegeben.
Ein Aufruf der Funktion "ShowNumber" könnte z.B. folgendermaßen aussehen:
ShowNumber(42);
Durch die Ausführung dieses Aufrufes würde die Nachricht "ShowNumber has been called with parameter 42!" erzeugt.
Es ist auch erlaubt, mehrere Parameter zu definieren. Dazu ein Beispiel:
func ShowSum(number1, number2, number3, number4) { Log("The sum of the first four parameters is %d.", number1 + number2 + number3 + number4); }
In diesem Fall wurden vier Parameter benannt. Ihnen wurden die Namen "number1" bis "number4" zugewiesen (die Parameternamen werden jeweils durch Kommata getrennt).
In der Nachricht wird dann jeweils die Summe der vier übergebenen Zahlen ausgegeben. Der Funktionsaufruf
ShowSum(1, 2, 3, 4);
führt also zu der Nachricht "The sum of the first four parameters is 10.".
Alle Funktionen haben einen versteckten Parameter, der
this
genannt wird. Wenn eine Funktion in einer anderen Proplist (welche auch ein Objekt, Definition oder irgendeine andere Art von Proplist sein kann) mit p -> Foo();
oder p ->~ Foo();
aufgerufen wird, bekommt die Funktion Foo
die Proplist p
als this
.func Foo(proplist self) { Log("this == self: %v", this == self); } func Bar() { var p = { Baz = this.Foo }; p->Baz(p); p->Baz("not p"); Foo(this); }
In diesem Script Bar aufzurufen ergibt diese Ausgabe:
this == self: true this == self: false this == self: true
Der letzte Aufruf von
Foo
zeigt den Grund für this
: Die meisten Funktionen in einem Objekt brauchen das Objekt um damit etwas zu tun, und das Objekt an jede Funktion zu übergeben würde zu einer Menge sich wiederholendem Code führen.Hinweise für Kenner vorheriger Versionen von C4Script: Beim Aufruf einer Funktion in einer Definition wie etwa
Flint->Hit();
, wird die Definition von this
zurückgegeben. In diesem Beispiel führt das wahrscheinlich zu einer Fehlermeldung wie "passed proplist, but expected object", da die Hit-Funktion des Flints nicht dazu gedacht ist, so aufgerufen zu werden.Parametertypen
Es ist möglich, den Typ der ürgebenen Parameter zu erzwingen. Dazu einfach den Namen des entsprechenden Typs (siehe dazu Typechecks) vor den Parameternamen setzen:
func TypeParameterFunction(object myClonk, id def, int count, string msg) { for(var i = 0; i < count; i++) CreateContents(def, myClonk); myClonk->Message(msg); }
Diese Funktion gibt einem übergebenen Clonk (
Clonk
) eine Anzahl von Objekten (anzahl
) vom angegebenen Typ (def
) und gibt die Nachricht msg
über seinem Kopf aus. Dabei wird bei Funktionsaufruf sichergestellt, dass alle Parameter in den angegebenen Typ konvertiert werden, sofern dies möglich ist (siehe Datentypen).Ein Aufruf wie
TypeParameterFunction(1, Clonk, "Text", 5)
hier würde also einen Typfehler auslösen.Vorgabeparameter
Anders als in verschiedenen anderen Programmiersprachen ist es in C4Script immer erlaubt, weniger oder auch mehr Parameter zu übergeben als in der Funktion definiert (maximal jedoch 10).
Die folgenden Aufrufe der obigen Funktion sind also völlig legal:
ShowSum(1, 2); ShowSum(1, 2, 3, 4, 5, 6);
Beim ersten Aufruf wurden weniger Parameter übergeben, als in ShowSum deklariert wurden. Die "überschüssigen" Parameter in ShowSum erhalten nun einfach
nil
, die Meldung lautet entsprechend "The sum of the first four parameters is 3." (=1+2+0+0).Wird ein Parameter nicht übergeben, so ist das mit der Übergabe von
nil
identisch.Umgekehrt wurden beim zweiten Aufruf mehr Parameter übergeben als in ShowSum definiert wurden. Der 5. und 6. Parameter werden nun keinem Parameter in ShowSum zugewiesen. Sie können allerdings noch mittels der Funktion Par() abgefragt werden [Par(4) und Par(5)]. Da die letzten beiden Parameter von ShowSum also nicht ausgewertet werden können, wird hier "nur" die Meldung "The sum of the first four parameters is 10." ausgegeben.
Rückgabewerte
Jede Scriptfunktion gibt einen Rückgabewert an den Aufrufer (die aufrufende Funktion bzw. die Engine) zurück. Dieser Wert wird im Script mittels return() gesetzt.
Beispiel:
func Difference(number1, number2) { return(Abs(number1 - number2)); }
Hier wird die Differenz der ersten beiden Parameter bestimmt und das Ergebnis "zurückgegeben". Eine andere Scriptfunktion könnte nun diese Funktion benutzen, um die Differenz zweier Zahlen zu berechnen:
func ShowDifference(number1, number2) { Log("The difference between %d and %d is %d!", number1, number2, Difference(number1, number2)); }
Der Aufruf "ShowDifference(5, 2)" würde also die Meldung "The difference between 5 and 2 is 3!" erzeugen.
Wenn die Funktion keinen Wert explizit zurückliefert gibt sie automatisch
nil
zurück.Globale Funktionen
Eine Funktion wird
global
definiert, indem "global
" vor "func
" gestellt wird.Eine als
global
definierte Funktion kann aus jedem Script aufgerufen werden. Ihr Gültigkeitsbereich stimmt mit denen der Engine-Funktionen überein. Sie können damit auch verwendet werden, um Engine-Funktionen zu überladen und ihre Funktionsweise zu modifizieren.Beispiel:
global func CreateContents(id, obj, count) { var obj; for(var i = 0; i < count; i++) obj = inherited(id, obj); return obj; }
Definiert die Engine-Funktion CreateContents neu. Dabei wird sie um einen neuen Parameter count erweitert, der es erlaubt, mehrere Objekte zu erzeugen. Man beachte, dass inherited innerhalb dieser Funktion der durch sie überladenen Engine-Funktion CreateContents entspricht!
Achtung!
Eine globale Skriptfunktion übernimmt den Kontext der aufrufenden Funktion. Das bedeutet insbesondere, dass this das aufrufende Objekt ist (natürlich nur, wenn die Funktion auch aus einem Objekt aufgerufen wurde), oder
nil
(wenn aus einem Szenarioscript aufgerufen). Deshalb darf in einer globalen Funktion auch keine objektlokale Variable verwendet werden und auch keine andere lokale Funktion aufgerufen oder lokale Variablen verändert werden! Das folgende Objektscript ist also nicht zulässig:local number; func ObjectFunction() { Log("ObjectFunction: local number has the value %d!", number); } global func GlobalFunction() { ObjectFunction(); // Error! number++; // Error!! }
Beide Versuche, auf objektlokale Elemente zuzugreifen, schlagen fehl. Das ist leicht einsichtig, da GlobalFunktion() ja aus jedem beliebigen Script, also auch aus dem Szenarioscript oder aus dem Script eines anderen Objekts aufgerufen werden kann. Dieses Script besitzt dann höchstwahrscheinlich auch keine Variable "number" oder Funktion "ObjectFunktion", die benutzt werden könnte. Da die Engine dies beim Vararbeiten des Scripts nicht sicherstellen kann, wird ein Fehler ausgelöst.
Anmerkung: Soll eine lokale Funktion in einem fremden Kontext aufgerufen werden, so sollte "
this->Funktion(...)
" verwendet werden. Dann gibt es nur einen Fehler, wenn die Funktion auch wirklich nicht im Objekt existiert.