Scripting-Hilfe: Prozeduren selber erstellen

So umfangreich die Funktionen in mAirList auch sind, so fehlt doch immer wieder mal das eine oder andere Feature für den gewünschten Zweck. Macht aber nichts, denn genau zu diesem Zweck gibt es die Scripting-Engine, mit welcher die Möglichkeiten in mAirList noch wesentlich umfangreicher werden als ohnehin schon. Also:

Prozeduren selber erstellen:

Beim Programmieren von Skripten stolpert man früher oder später über das Wort procedure, welches einem eher unerfahrenen Skript-Autoren immer ein wenig geheimnisvoll erscheint. Da gibt es doch diese Liste mit Prozeduren, ja, und die lassen sich verwenden, um auf bestimmte Ereignisse hin (z. B. Start eines Players) bestimmte Kommandos auszuführen (eben unser Hintergrundskript). Wir gehen jetzt aber einen Schritt weiter: Wir schreiben selber eine Prozedur!

Also, was ist das genau, so ein Ding? Im Grunde nichts anderes als eine Abfolge von Kommandos, ein Stückchen Progammcode, ja, ein eigenes kleines Programm für sich, welche(s) man unter einem „Oberbegriff“, dem Namen der Prozedur, zusammenfaßt. Das ist immer dann hilfreich, wenn man immer wiederkehrende Aufgaben im Skript bewältigen möchte.

Ein einfaches, zugegebenmaßen ein wenig einfältiges Beispiel: Ich habe fünf leuchtende Buttons irgendwo, und die sollen alle auf einmal ausgehen. Natürlich könnte man schreiben:

begin
  ExecuteCommand('BUTTON.1 OFF');
  ExecuteCommand('BUTTON.2 OFF');
  ExecuteCommand('BUTTON.3 OFF');
  ExecuteCommand('BUTTON.4 OFF');
  ExecuteCommand('BUTTON.5 OFF');
end.

Das machen wir aber nicht, denn wir haben schon gelernt, wie das eleganter geht:

var
  i: integer;

begin
  for i := 1 to 5 do
    ExecuteCommand('BUTTON.' + IntToStr(i) + ' OFF');
end;

Und dabei könnte man es auch belassen, käme diese Abschaltaufabe nur ein einziges mal vor. Es könnte jedoch sein, daß ihr diese stinklangweilige Aufgabe mehrfach in Eurem Skript braucht. Und schon lohnt es sich zu schreiben:

procedure ButtonsOff;
var
  i: integer;
begin
  for i := 1 to 5 do
    ExecuteCommand('BUTTON.' + IntToStr(i) + ' OFF');
end;

begin
  // Hier steht Euer eigentliches Hauptprogramm …
  // … und an die entsprechende Stelle …
  // … setzt man folgenden Aufruf:
  
  ButtonsOff;

  // Die Buttons sind jetzt dunkel …
  // … und weiter geht's mit Eurem Code.
end.

Wann immer ich jetzt die Knöpfe abschalten möchte, brauche ich nur noch ButtonsOff zu schreiben, und schon sind sie aus! (Habt Ihr gemerkt? Die Zählvariable i wird erst innerhalb der Prozedur selber deklariert. Sie gilt auch nur für diese, also könnte man in einer weiteren Prozedur eine andere Variable ebenfalls i nennen, sie würden sich nicht gegenseitig stören.)

Es geht aber noch besser. Sagen wir, ich möchte nur die ersten vier Buttons abschalten. Oder die ersten zweie. Dafür kann ich etwas superelegantes anbringen: Wir übergeben einen Parameter! Klingt protzig, ist aber ganz einfach. Schaut es Euch an:

procedure ButtonsOff(MaxButton: integer);
var
  i: integer;
begin
  for i := 1 to MaxButton do
    ExecuteCommand('BUTTON.' + IntToStr(i) + ' OFF');
end;

Wenn ich den Aufruf nun folgendermaßen starte,

  ButtonsOff(2);

dann werden eben nur die ersten zwei Buttons abgeschaltet. Undsoweiter. Die Variablen in der Klammer hinter procedure werden durch den Prozeduraufruf definiert und dann innerhalb weiterverarbeitet. Das geht auch mit mehreren:

procedure ButtonsOff(MinButton: integer; MaxButton: integer);
var
  i: integer;
begin
  for i := MinButton to MaxButton do
    ExecuteCommand('BUTTON.' + IntToStr(i) + ' OFF');
end;

Hier übergibt man zwei Werte („Parameter“) und schaltet damit alle Buttons vom ersten bis zum zweiten ab, nämlich so:

ButtonsOff(2, 4);

Diese Werte können, müssen aber nicht in der weiteren Funktion verwurstet werden. (Beim Aufruf müssen sie aber da sein.) Achtung: Die Parameter im Aufruf werden immer durch Kommata getrennt, sonst meckert die Syntaxprüfung! (Obwohl in der Definition Semikola zur Trennung verwendet werden. *seufz* )

Ob die Werte sinnvoll und/oder überhaupt erlaubt sind, wird allerdings nicht geprüft. Dafür müßte man getrennt sorgen.

So, und mit diesem Wissen gerüstet, bekommen wir jetzt einen tiefen Einblick in die Funktion der mAirList-Scripting-Engine:

Im Hauptprogramm hat @Torben an bestimmten Stellen, zum Beispiel wenn ein Player startet, einen Funktionsaufruf eingebaut, in diesem Beispiel OnPlayerStart, der allerdings zunächst mal ins Leere führt. Ist nicht weiter schlimm, mAirList macht dann einfach weiter. Dabei werden gleich auch verschiedene Werte übergeben, nämlich die laufende Nummer der betreffenden Playlist als PlaylistIndex, der Player, der gerade startete, als PlayerIndex und das soeben gestartete Element als Item.

Und jetzt können wir halt in unser Skript schreiben:

procedure OnPlayerStart(PlaylistIndex: integer; PlayerIndex: integer; Item: IPlaylistItem);
begin
  // Mach irgendwas mit diesen Werten oder auch nicht …
  // … oder mach was ganz anderes.
end;

Wann immer jetzt also ein Player startet, wird diese Prozedur aufgerufen und es passiert dann das, was wir dort zwischen begin und end; hingeschrieben haben. Oder halt nichts, wenn nicht.

Aufgerufene Grüße

TSD