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 MeineFunktion()
{
  Message("Meine Funktion wurde aufgerufen!");
}
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: "MeineFunktion"). In der Klammer nach dem Funktionsnamen können Funktionsparameter definiert werden (s.u.).
Beim Aufruf der Funktion wird die Meldung "Meine Funktion wurde aufgerufen!" ausgegeben.
func ZeigeZahl(int zahl)
{
  Message("ZeigeZahl wurde mit dem Parameter %d aufgerufen!", 0, zahl);
}
Hier wurde ein Parameter mit dem Namen "zahl" vom Typ "int" definiert. Wird beim Aufruf der Funktion ein Parameter angegeben, so erhält "zahl" den übergebenen Wert. Der übergebene Wert wird in diesem Fall in einer entsprechenden Nachricht ausgegeben.
Ein Aufruf der Funktion "ZeigeZahl" könnte z.B. folgendermaßen aussehen:
ZeigeZahl(42)
Durch die Ausführung dieses Aufrufes würde die Nachricht "ZeigeZahl wurde mit dem Parameter 42 aufgerufen!" erzeugt.
Es ist auch erlaubt, mehrere Parameter zu definieren. Dazu ein Beispiel:
func ZeigeSumme(zahl1, zahl2, zahl3, zahl4)
{
  Message("Die Summe der ersten 4 Parameter ist %d.", 0, zahl1 + zahl2 + zahl3 + zahl4);
}
In diesem Fall wurden vier Parameter benannt. Ihnen wurden die Namen "zahl1" bis "zahl4" zugewiesen (die Parameternamen werden jeweils durch Kommata getrennt).
In der Nachricht wird dann jeweils die Summe der vier übergebenen Zahlen ausgegeben. Der Funktionsaufruf
ZeigeSumme(1, 2, 3, 4);
führt also zu der Nachricht "Die Summe der ersten 4 Parameter ist 10.".

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 TypParameterFunktion(object clnk, id def, int anzahl, string msg)
{
  for(var i = 0; i < anzahl; i++)
    CreateContents(def, clnk);
  Message(msg, clnk);
}
Diese Funktion gibt einem übergebenen Clonk (clnk) 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 Typechecks).
Ein Aufruf wie TypParameterFunktion(1, CLNK, "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:
ZeigeSumme(1, 2);
ZeigeSumme(1, 2, 3, 4, 5, 6);
Beim ersten Aufruf wurden weniger Parameter übergeben, als in ZeigeSumme deklariert wurden. Die "überschüssigen" Parameter in ZeigeSumme erhalten nun einfach den Wert 0, die Meldung lautet entsprechend "Die Summe der ersten 4 Parameter ist 3." (=1+2+0+0).
Wird ein Parameter nicht übergeben, so ist das mit der Übergabe von 0 identisch.
Umgekehrt wurden beim zweiten Aufruf mehr Parameter übergeben als in ZeigeSumme definiert wurden. Der 5. und 6. Parameter werden nun keinem Parameter in ZeigeSumme zugewiesen. Sie können allerdings noch mittels der Funktion Par() abgefragt werden [Par(4) und Par(5)]. Da die letzten beiden Parameter von ZeigeSumme also nicht ausgewertet werden können, wird hier "nur" die Meldung "Die Summe der ersten 4 Parameter ist 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 Differenz(Zahl1, Zahl2)
{
  return(Abs(Zahl1 - Zahl2));
}
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 ZeigeDifferenz(Zahl1, Zahl2)
{
  Message("Die Differenz zwischen %d und %d beträgt %d!", Zahl1, Zahl2, Differenz(Zahl1, Zahl2));
}	
Der Aufruf "ZeigeDifferenz(5, 2)" würde also die Meldung "Die Differenz zwischen 5 und 2 beträgt 3!" erzeugen.

Aufrufberechtigungen

Für jede Funktion kann eine "Aufrufberechtigung" festgelegt werden, die bestimmt, von wo die Funktion aufgerufen werden darf. Hier gibt es drei verschiedene Stufen:
public darf von überall aufgerufen werden, auch aus anderen Scripten (Standard)
protected darf nur aus dem eigenen Script und von der Engine aufgerufen werden
private darf nur aus dem eigenen Script aufgerufen werden
Die Aufrufberechtigung wird jeweils direkt vor "func" geschrieben:
private func PrivateFunktion()
{
  // Diese Funktion darf nur im selben Script aufgerufen werden!
}
Wird diese Funktion nun von einem externen Script aufgerufen, so gibt die Engine einen Fehler aus.

Anmerkung

Da es in manchen Fällen notwendig erscheint, auch eine geschützte Funktion aus einem fremden Script aufzurufen, kann die Aufrufbeschränkung mittels PrivateCall bzw. ProtectedCall umgangen werden.

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, pObj, iCnt)
{
  var pObj;
  for(var i = 0; i < iCnt; i++)
    pObj = inherited(id, pObj);
  return(pObj);
}
Definiert die Engine-Funktion CreateContents neu. Dabei wird sie um einen neuen Parameter iCnt 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). 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 iNummer;
func ObjektFunktion()
{
  Log("ObjectFunktion: local iNummer hat den Wert %d!", iNummer);
}

global func GlobaleFunktion()
{
  ObjectFunktion(); // Fehler!
  iNummer++; // Fehler!
}
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 "iNummer" 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.

Referenzen

Manchmal ist es nötig, nicht den Wert einer Variable zu übergeben, sondern einen "Verweis" auf die Variable selbst.
Nehmen wir an, wir wollen eine Funktion schreiben, die die Position des höchstrangingsten Clonks eines Spielers zurückgibt. Dies ist normalerweise ohne Tricks nicht möglich, da eine Funktion ja nur einen Rückgabewert hat (die Position hat aber zwei Komponeneten: X und Y).
Doch durch Verwendung von Referenzübergabe als Parameter kann hier die Information trotzdem zurückgegeben werden:
func GetHiRankPosition(int plr, &x, &y)
{
  var oHiRank = GetHiRank(plr);
  x = GetX(oHiRank);
  y = GetY(oHiRank);
}

func Aufruf()
{
  var iHiRankX, iHiRankY;
  GetHiRankPosition(0, iHiRankX, iHiRankY);
  Message("Die Position des HiRanks von Spieler 1 ist: %d/%d", 0, iHiRankX, iHiRankY);
}
x und y in GetHiRankPosition sind hier Referenzen, d.h. wenn man sie setzt, werden die Variablen gesetzt, die man als Parameter angab (in diesem Fall iHiRankX und iHiRankY). Man beachte, dass ein Aufruf wie "GetHiRankPosition(0, iHiRankX + 1, iHiRankY)" zu einem Typfehler führt, da "iHiRankX + 1" keine Variable, sondern eine Zahl ist!
Man beachte, dass "&" ein eigenständiger Typ ist, deshalb darf für einen Referenzparameter kein weiterer Typ angegeben werden, "int &x" wäre also ungültig!
PeterW, Juli 2002
matthes, Juni 2004