Der Skript-Editor
Um deine Spiel-Skripte zu bearbeiten, klicke auf den Link "Mod (API) Skripte" auf der Seite "Spielinformationen" für dein Spiel (dort, wo sich auch Optionen wie "Chatprotokoll" und "Spiel kopieren/erweitern" befinden). Es werden dir verschiedene Funktionen angezeigt:
- Eine Liste mit Registerkarten oben. Dein Spiel kann mehrere Skripte haben, um die Organisation zu erleichtern. Beachte, dass alle Skripte weiterhin im selben Kontext ausgeführt werden. Das bedeutet, dass nicht mehrere Skripte gleichzeitig versuchen sollten, dieselben Werte zu überschreiben, da du sonst unbeabsichtigte Ergebnisse erhalten könntest.
- Ein Skriptcode-Editor. Du kannst diesen Editor verwenden oder deine Skripte in einem externen Editor deiner Wahl bearbeiten und sie dann hier einfügen.
- Eine "Mod (API) Konsole" befindet sich unten (siehe unten).
Jedes Mal, wenn du auf die Schaltfläche "Skripte speichern" klickst, wird die Sandbox für dein Spiel neu gestartet (dabei gehen alle Daten im Speicher verloren, die nicht im Objekt state
oder in Roll20-Objekten gespeichert wurden). Das gilt auch, wenn du ein neues Skript hinzufügst, ein Skript löschst oder ein Skript umschaltest, um es zu aktivieren/deaktivieren.
Die Mod (API) Konsole
Die Mod (API) Konsole ist das "Fenster" zu deinen Skripten. Da Mod (API) Skripte in einer Sandbox laufen, hast du keinen direkten Zugriff auf sie, während sie laufen, um Informationen zu den Ergebnissen oder Fehlern des Skripts anzuzeigen. Die Mod (API) Konsole zeigt diese Informationen außerhalb der Sandbox an, damit du sie anzeigen kannst, während du deine Skripte bearbeitest. Alle log()
Befehle werden hier angezeigt, sowie alle Fehler, die während der Ausführung deiner Skripte auftreten. Weitere Informationen findest du im Artikel über das Debuggen von Skripten.
Reaktive Skripte: Ereignisse abhören, Objekte ändern
Der erste (und einfachste Typ) der Mod (API) Verwendung ist, auf Änderungen am Spieltisch zu reagieren und dann mit zusätzlicher Funktionalität auf die geänderten Objekte zu reagieren. Diese Art von Skript besteht aus einer Reihe von Funktionen, die Ereignisse überwachen, die während des Spiels passieren. Anschließend werden die während dieser Ereignisse übergebenen Objekte geändert, wodurch sich das Geschehen auf dem Spieltisch ändert.
Ein einfaches Skript, das ein Stück um weitere 5 Fuß verschiebt (unter der Annahme der Standardseiteneinstellungen), wäre:
on("change:graphic", function(obj) {
obj.set({
left: obj.get("left") + 70
});
});
Wie du sehen kannst, haben wir eine einfache on-Funktion erstellt, die immer dann ausgeführt wird, wenn das change:graphic-Ereignis eintritt. Der Funktion wird das Grafikobjekt obj übergeben. Um eine Änderung vorzunehmen, modifizieren wir obj einfach mit der Funktion set -- was auch immer wir ändern, wird erkannt und auf der Spieltisch geändert.
set
und get
verwenden, um die aktuellen Werte von Objekten zu setzen und zu erhalten, sonst werden deine Änderungen nicht gespeichert. (Weiter unten findest du eine Liste der Objekttypen und ihrer Eigenschaften sowie eine Liste aller Ereignisse und der Argumente, die jedem Ereignis übergeben werden).Ein Hinweis zu Utility-Funktionen
Natürlich ist das vorherige Beispiel nicht besonders hilfreich, da es der Position des Spielmarkers immer 70 Pixel hinzufügt. Was aber, wenn der Benutzer den Maßstab so geändert hat, dass 5 Fuß 140 Pixel entspricht? Die Roll20-API bietet mehrere praktische Hilfsfunktionen, die bei diesem (und anderen) häufigen Szenarios helfen. Ändern wir unser vorheriges Beispiel, um diedistanceToPixels-Funktion
zu verwenden, die uns sagt, wie viele Pixel „fünf Fuß“ (oder Zoll, oder Meter, oder was auch immer für einen anderen Abstandstyp festgelegt wurde) auf der Tischplatte sind.
on("change:graphic", function(obj) {
obj.set({
left: obj.get("left") + distanceToPixels(5);
});
});
Wenn nun die aktuelle Seite so eingerichtet ist, dass sie die Standard-Rastergröße verwendet,distanceToPixels(5);
gibt immer noch 70 Pixel zurück, aber wenn die Seite so eingerichtet ist, dass sie eine doppelt so große Skalierung wie normal hat, würde sie 140 zurückgeben.
Es ist immer eine gute Idee, Hilfsfunktionen zu verwenden, wenn sie verfügbar sind, damit dein Skript nicht zusammenbricht, wenn sich die Einstellungen einer Seite oder eines Spielmarkers ändern.
Proaktive Skripte: Dinge ohne Benutzereingriff erledigen
Du kannst nicht nur auf Nutzerereignisse reagieren, sondern mit der API auch automatisch Dinge tun, die nicht an ein bestimmtes Ereignis von den Spielern gebunden sind. Nehmen wir zum Beispiel einen Token an, der auf der Karte hin und her patrouilliert.
Hinweis: Obwohl dieser Skripttyp nicht von Benutzerinteraktion abhängt, werden die Mod (API) Skripte für dein Spiel nur ausgeführt, wenn mindestens eine Person mit deinem Spiel verbunden ist.
on("ready", function() {
//Warte, bis das ready-Ereignis ausgelöst wird, damit wir wissen, dass das Spiel vollständig geladen ist.
//Erhalte eine Referenz zu unserem Patrouillen-Spielmarker.
var patroltoken = findObjs({_type: "graphic", name: "Guard A"})[0]; //Wir wissen, dass es im Spiel einen Token namens „Guard A“ gibt.
var Direction = -1*distanceToPixels(5); //Gehe 70 Pixel nach links.
varstepstaken = 0; //Wie viele Schritte sind wir in der aktuellen Richtung gegangen?
setInterval(function() {
if(stepstaken > 3) {
//Richtung wechseln!
Richtung = Richtung * -1; //wird die Richtung, in die wir gehen, „umdrehen“ 0stepstaken =
; //reset geht auf 0 zurück.
}
patroltoken.set("left", patroltoken.get("left") + Richtung); //gehen!
Schritte gemacht++;
}, 5000); //alle 5 Sekunden eine Aktion ausführen
});
Eine Abhandlung über asynchrone Funktionen
Eine asynchrone Funktion ist eine Funktion, die den Steuerungsthread sofort an den aufrufenden Bereich zurückgibt und im Hintergrund bestimmte Aufgaben ausführt. Hier ist ein sehr einfaches und leicht verständliches Beispiel, das du in einem Mod (API) Skripte-Tab einfügen kannst:
on('load',function(){
log('Übergeordneter Bereich – Vor Aufruf der asynchronen Funktion.');
setTimeout(function(){
log('Asynchroner Funktionsbereich – Ausführung der asynchronen Funktion.');
},10 /* 10 Millisekunden */);
log('Übergeordneter Bereich – nach Aufruf der asynchronen Funktion.');
});
Im Mod (API) Protokoll siehst du etwas wie das hier:
„Übergeordneter Bereich – Vor dem Aufruf einer asynchronen Funktion.“ „Übergeordneter Bereich – nach Aufruf der asynchronen Funktion.“ „Asynchroner Funktionsumfang – Ausführung der asynchronen Funktion.“
Wenn du dir den Code ansiehst, denkst du: "Natürlich wird das später passieren, du hast ihm ja gesagt, dass er in 10 Millisekunden laufen soll, duh? Hier ist ein weniger offensichtliches Beispiel, das dieselben Protokollmeldungen enthält:
on('load',function(){
log('Parent Scope – Vor Aufruf der asynchronen Funktion.');
sendChat('Async Function','Evaluate this: [[1d6]]',function(msg){
log ('Asynchroner Funktionsbereich – Ausführen der asynchronen Funktion.');
});
log('Übergeordneter Bereich – nach Aufruf der asynchronen Funktion.');
Asynchrone Funktionen sind erforderlich, um zu verhindern, dass die API ständig hängt. Wenn jeder Würfelwurf synchron verarbeitet würde, wäre die API sehr träge und reagiert nicht. Fast jede Funktion, die einen Rückruf annimmt, ist asynchron. (Mit Ausnahme einiger der Funktionen _.map, _.reduce usw. handelt es sich hierbei um Beispiele funktionaler Programmierung im Gegensatz zur prozeduralen Programmierung, an die die meisten Menschen gewöhnt sind.)