Intern/RFC1
From BlinkenSisters
RFC 1: Neuentwicklung objekt-orientierter Bindings zur LUA-Scriptsprache.
see also Scripting
Contents |
RFC 1: OO-LUA
Dies ist ein "BlinkenSisters Request For Comment", ich ersuch also um eure Meinung dazu:
Dieses RFC betrifft die Neuentwicklung der LUA-Bindings (Scripting). Bei Differenzen zwischen dieser Dokumentation und der Implementation ist diese Dokumentation maßgebend!
Der Grund für die Neuentwicklung: Das Konzept ist viel leichter zu implementieren als die alten LUA-Bindings; Funktionsargumente lassen sich ebenfalls besser überprüfen; Erweiterungen können *viel* leichter implementiert werden. Zusätzlich ist das Interface für den Leveldesigner viel sauberer und passt besser zu den anderen LUA-Erweiterungsmodulen (math-Library usw).
Die neuen LUA-Bindings werden die alten Bindings ablösen; die Weiterentwicklung der alten, nicht-OO Bindings wurden gestoppt. Nach erfolgreichem Umstieg auf die neuen Bindings werden die Files der alten Bindings aus dem Programmcode entfernt und der Support für die alten Bindings damit beendet.
| Dieses RFC ist zur besseren verständlichkeit Farbkodiert |
|---|
| Allgmeine (nicht-objektorientierte) Funktionen |
| Objektorientierte Funktionen |
| Beispiele und zulässige ENUM-Werte |
Allgemeines
Das neue Scripting ist objektorientiert aufgebaut und lehnt an die verfügbaren LUA-Extensions und an Javascript an.
Die meisten Funktionen - vor allem alle Eigenschaften - sind gleichzeitig getter und setter; d.h. es wird immer der aktuelle Wert zurückgegeben. Wird ein Wert der Funktion mitgegeben, wird dieser als neuer Wert gesetzt.
| Beispiel | |
|---|---|
| x = bs.player.x(); | Gibt die aktuelle X-Position zurück |
| bs.player.x(100); | Setzt X auf 100 |
| x = bs.player.x(100); | Setzt X auf 100 und gibt 100 zurück (sofern das setzen erfolgreich war; sonst gibts die ALTE Position, weil die sich nicht geändert hat) |
Alle Funktionen (ausser Objektfunktionen) sind im "bs." Namespace, und da wieder unterteilt in das Aufgabengebiet.
Koordinaten werden in OO LUA *immer* als Pixel- *nie* als Tile-Koordinaten angegeben.
Boolsche Werte müssen über bs.yes und bs.no angegeben werden.
Funktionen
Engine
"Engine"-Funktionen sind Funktionen, die entweder direkt die Engine betreffen (z.B. bs.die()) oder die keinen eigenen Namespace haben (z.B. bs.playvideo()).
| Engine-Funktionen | |
|---|---|
| bs.die("TEXT"); | Killt die Engine mit der Ausgabe von TEXT |
| bs.include("FILE"); | Lädt zusätzlich Datei FILE in das aktuelle Script. Nur zulässig in der INIT Funktion |
| bs.playvideo("FILE", bool, bool); | Spielt das Video FILE ab. (Alle Callbacks und Physics werden temporär gestoppt). Der interne Player wird nach Dateiendung (.bmf oder .ogm) ausgewählt. Der zweite Parameter stellt ein ob die TrackerGui aktiv ist, der dritte ob der Benutzer abbrechen kann. |
Screen
Dieser Namespace enthält funktionen, um die Bildschirmausgabe anzupassen und zu filtern
| Screen-Funktionen | |
|---|---|
| bs.screen.resolution.x();
bs.screen.resolution.y(); bs.screen.resolution.tilesize(); | Gibt die aktuelle Bildschirm-Auflösung bzw. die Tilesize zurück. (Es wird *irgendwann* auch möglich sein, diese zu setzen; derzeit allerdings nicht) |
| bs.screen.filter.color.greyscale(bool); | Schaltet Graustufenanzeige ein/aus bzw. gibt die aktuelle Einstellung zurück. |
| bs.screen.filter.color.invert(bool); | Schaltet Farbinvertierung ein/aus bzw. gibt die aktuelle Einstellung zurück. |
| bs.screen.filter.noise.rectangle(bool); | Schaltet Rauschen mit Rechtecken ein/aus bzw. gibt die aktuelle Einstellung zurück. |
| bs.screen.filter.noise.circle(bool); | Schaltet Rauschen mit Kreisen ein/aus bzw. gibt die aktuelle Einstellung zurück. |
| bs.screen.filter.noise.stripes(bool); | Schaltet Rauschen mit Streifen ein/aus bzw. gibt die aktuelle Einstellung zurück. |
| bs.screen.filter.fading(number); | Blendet den ganzen Bildschirm mit dem gegebenen Wert ab bzw. gibt den aktuellen Wert zurück. 0=schwarzer Bildschirm, 255 normale Anzeige. |
Callbacks
Ein Callback ist eine Script-Funktion, die von der BS-Engine bei verschiedenen Ereignissen aufgerufen wird
In Zukunft wird kein Callback mehr automatisch aufgerufen, auch der INIT Callback muss registriert werden. Diese Designänderung erlaub ein flexibleres Design. Zusätzlich kann während des Spiels zwischen verschiedenen Callback-Funktionen für dieselbe Funktionalität umgeschalten werden, z.B. bei Spielmodi-Wechsel.
| Callback-Funktionen | |
|---|---|
| bs.callback.engine.init("FUNCNAME"); | Setzt/Liest den Callback für die INIT Phase |
| bs.callback.engine.physics("FUNCNAME"); | Setzt/Liest den standard Physics-Loop Callback (100x Sekunde) |
| bs.callback.paint.stage1("FUNCNAME");
bs.callback.paint.stage2("FUNCNAME"); bs.callback.paint.stage3("FUNCNAME"); bs.callback.paint.stage4("FUNCNAME"); bs.callback.paint.stage5("FUNCNAME"); bs.callback.paint.hud("FUNCNAME"); | Setzt/Liest die Callbacks für die einzelnen Zeichenphasen. Die genaue Beschreibung der Zeichenphasen wird bei Implementation noch definiert. |
| bs.callback.game.input("FUNCNAME");
bs.callback.game.mainloop("FUNCNAME"); | Mit diesen Callbacks wird man seinen eigenen Mainloop mit komplett eigener Physics/Gameengine basteln können. Verändert/Deaktiviert die Funktionalität der bs.level und bs.player Funktionen und natürlich wird auch der bs.callback.physics() Callback nicht mehr aufgerufen. |
Weitere Callback-Hooks werden noch entworfen.
Callbacks kann man auch wieder abschalten, indem man die Funktion mit einem leeren String aufruft.
Player
Mit diesen Funktionen kann man die Spielerfigur "fernsteuern" und div. Eigenschaften setzen.
| Player-Funktionen | |||||||||
|---|---|---|---|---|---|---|---|---|---|
| bs.player.x(X);
bs.player.y(Y); | Liest/Ändert die Spieler-Koordinaten. | ||||||||
| bs.player.speed_x(X);
bs.player.speed_y(Y); | Liest/Ändert die Spieler-Geschwindigkeit. | ||||||||
| bs.player.kill(); | Spieler stirbt und verliert ein Leben. | ||||||||
| bs.player.score(SCORE); | Liest/Ändert den Punktestand | ||||||||
| bs.player.lives(LIVES); | Liest/Ändert die Anzahl der verbleibenden Leben des Spielers | ||||||||
| bs.player.gfx(movetype, gfxobj);
bs.player.gfx(movetype); | Setzt/Liest die GFX für die verschiedenen Spieler-Bewegungen. GFX-Objekte können sowohl statische als auch ANIM-GFX sein. Die GFX-Objekte müssen konform den Standard-Grafiken sein, also "32 x multiples von 32". Für ANIMS muss jede Grafik genau "32x32" sein! Die Spieler-Bewegung wird über den Movetype ausgewählt. Manche der Bewegungen sind nur im Free-Mode verfügbar.
Ist ein Move-Type nicht definiert, wird versucht, das "klassische" Verhalten zu emulieren, indem passende, andere Move-Types benutzt werden. | ||||||||
| bs.player.callback.livelost("FUNCNAME"); | Callback, wenn der Spieler ein Lebel verliert. | ||||||||
- @bs.player.kill();: Ein optionaler Parameter, der bestimmte unterschiedliche Sterbe-Screens/Videso zeigen kann, wäre nett. Dann könnte man "ertrinken" und "verbrennen" unterscheiden. Vielleicht sogar bei den "Default-Monstern" jeweils ein eigenes Bild/Video machbar? --Took 00:15, 24 November 2007 (CET)
- entsp. passendes audio zu den versch. movies natülich auch... :) --Took 14:44, 6 December 2007 (CET)
Level
Mit diesen Funktionen kann man Umgebungswerte ändern und div. Anzeigen-Elemente anpassen.
| Level-Funktionen | |
|---|---|
| bs.level.gravity(bool); | Liest/schaltet Schwerkraft |
| bs.level.freemovement(bool); | Liest/schaltet den Freemovement-Modus |
| bs.level.exitopen(bool); | Liest/schaltet den Ausgang auf offen/zu |
| bs.level.paintspecialtiles(bool); | Liest/schaltet das Zeichnen der "Special Tiles" (Eingang, Ausgang, etc) |
| bs.level.lockedbg(bool); | Liest/schaltet Parallax-Scrolling vom Hintergrund |
| bs.level.pixels.required(COUNT); | Liest/Schaltet Anzahl benötigter Pixel |
| bs.level.pixels.collected(COUNT); | Liest/Schaltet Anzahl aufgesammelter Pixel |
| bs.level.gfx.start(gfxobj);
bs.level.gfx.finish(gfxobj); bs.level.gfx.respawn(gfxobj); bs.level.gfx.tiles(gfxobj); bs.level.gfx.monster.X.left(gfxobj); bs.level.gfx.monster.X.right(gfxobj); | Setzt/Liest die GFX für die div. Level-Elemente. GFX-Objekte können sowohl statische als auch ANIM-GFX sein. Die GFX-Objekte müssen konform den Standard-Grafiken sein (TILESIZE-Konform).
Bei Tiles mit ANIMS werden alle Tile-Grafiken gleichzeitig getauscht, da dieses GFX dem Format "32 x multiples von 32" entsprechen muss. Bei Monstern wirds zwar auch gehen, macht aber nur Sinn, wenn pro GFX nur ein einzelnes Frame definiert ist (32x32). (Das X steht für die Monster-Nummer). |
Sound
Mit den bs.snd. Funktionen werden sowohl Hintergrundmusik als auch Soundeffekte gesteuert.
| Sound-Funktionen | |
|---|---|
| snd.bg.filename | "Read-only" variable mit der gerade abgespielten Hintergrundmusik |
| snd.bg.play(FILE) | Spielt die Datei FILE als aktuelle Hintergrundmusik ab. |
| snd.bg.callback.start();
snd.bg.callback.finish(); | Liest/Setzt Callbacks für die Hintergrundmusik, wenn das abspielen gerade gestartet/beendet wird. |
| snd.bg.loop(BOOL); | Liest/Setzt die Eigenschaft, ob die Hintergrundmusik als Loop abgespielt wird (damit man dann z.B. über snd.bg.callback.finish() eine neue Musik abspielen kann). |
| obj = bs.snd.load("FILE"); | Erstellt ein Objekt aus Sounddatei FILE |
| Sound-Objekt-Funktionen | |
|---|---|
| obj.callback.start();
obj.callback.finish(); | Liest/Setzt Callbacks für dieses Soundobjekt, wenn das abspielen gestartet bzw. beendet wird. |
| obj.play(); | Spielt das Soundobjekt ab. |
| obj.delete(); | Löscht das Soundobjekt aus dem Speicher. |
| dateiname = obj.filename; | Dateiname als String (Readonly) |
Timer
Mit den bs.timer. Funktionen können zeitlich gesteuert Callbacks aufgerufen werden.
| Timer-Funktionen | |
|---|---|
| obj = bs.timer.new("FUNCNAME", seconds); | Erstellt einen neuen timer, der die Funktion FUNCNAME zyklisch alle "seconds" Sekunden aufruft. "seconds" können natürlich auch Kommawerte und Bruchteile von Sekunden sein (z.B. 2x pro Sekunde aufrufen: seconds = 0.5). |
| Timer-Objekt-Funktionen | |
|---|---|
| obj.active(bool); | Liest/Setzt den Timer aktiv/inaktiv |
| obj.intervall(seconds); | Liest/Setzt den Timer-Intervall |
| obj.delete(); | Löscht das Timerobjekt aus dem Speicher. |
statische Graphic-Objekte (GFX)
Das sind "statische" GFX-Objekte:
| GFX-Funktionen | |
|---|---|
| obj = bs.gfx.load("FILE"); | Erstellt ein Objekt aus Graphik FILE |
| GFX-Objekt-Funktionen | |
|---|---|
| obj.delete(); | Löscht das Objekt aus dem Speicher. |
| dateiname = obj.filename; | Dateiname als String (Readonly) |
animierte Graphik-Objekte (ANIM)
Anim-Objekte sind (engine-intern) eine Zusammenfassung mehrerer GFX-Objekte, die selbstständig von der Engine animiert werden.
| ANIM-Funktionen | |
|---|---|
| obj = bs.anim.load("TEMPLATE", firstNum, lastNum, fps, looptype); | Lädt multiple GFX-Files in ein ANIM-Objekt; TEMPLATE ist ein Template des Dateinamens, also z.B. "flocke_%%%%.png", firstNum und lastNum sind die erste bzw. letzte Nummer der zu ladenden File. "fps" ist die Framerate, "looptype" ist der Looptype. |
| newanim = bs.anim.duplicate(oldanim); | Erstellt neue Animation newanim aus bereits vorhandener Animation oldanim ohne neuen Speicher für die Bilder zu belegen. So kann man beliebig an framerate, currentframe, looptype usw rumschrauben, ohne die Grafiken mehrfach im Speicher halten zu müssen. |
| Anim-Objekt-Funktionen | |||||||||
|---|---|---|---|---|---|---|---|---|---|
| gfxobj= obj.gfx[X]; | Array aller geladenen GFX-Objekte | ||||||||
| obj.framerate(fps); | Liest/Setzt die Framerate | ||||||||
| obj.looptype(type); | Liest/setzt den Typ des Animationsloops.
| ||||||||
| obj.currentframe(frame); | Liest/setzt den aktuell dargestellten Frame | ||||||||
| obj.currentdirection(dir); | Liest/setzt bei Pingpong-Loops die Animationsrichtung (nach vorne/hinten)
| ||||||||
| obj.delete(); | Löscht das Objekt aus dem Speicher. | ||||||||
fgObjects
Das sind die fgObjects. Überall, wo in GFX-Objekt gefordert wird, können sowohl GFX- als auch ANIM-Objekte verwendet werden.
| fgObject-Funktionen | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| objid = bs.obj.new(x, y, gfxobj, "properties"); | Erstellt ein neues fgObject. "properties" ist ein Optionaler String mit textuellen Eigenschaften (macht das Setzen vieler Eigenschaften etwas einfacher; mehrere Eigenschaften werden durch Komma getrennt, mit Rufzeichen kann man negieren).
Nicht gesetzte Eigenschaften sind auf default "bs.no" | ||||||||||||||||
| fgObject-Objekt-Funktionen | |
|---|---|
| obj.x(x);
obj.y(y); | Setzt/Liest X bzw. Y Position |
| obj.gfx(gfxobj); | Setzt/Liest das GFX-Object |
| obj.blocking(bool);
obj.visible(bool); obj.ontop(bool); obj.killing(bool); obj.pixel(bool); obj.elevator(bool); | Liest/setzt die BOOL-Eigenschaften des Objekts |
| obj.callback.playercollision("FUNCNAME"); | Liest/setzt den Callback für eine Player/fgObject Collision.
Dieser Callback wird mit dem Objekt und der fgOverlap-Tabelle als Parameter aufgerufen, z.B.: function myCallback(myobject, collisionOverlap)
io.write("oben:",collisionOverlap.top," unten:",collisionOverlap.bottom," right:",collisionOverlap.right," left:",collisionOverlap.left, "!\n");
myobject.x(myobject.x() + 10);
end
|
| obj.callback.playeraction("FUNCNAME"); | Liest/setzt den Callback für eine Player/fgObject Aktion (mit der Leertaste).
Dieser Callback wird mit dem Objekt als Parameter aufgerufen, z.B.: function myCallback(myobject)
myobject.x(myobject.x() + 10);
end
|
| obj.textproperties("properties"); | Liest/setzt die BOOL-Properties über den gleichen Textstring wie "new". Es werden nur angegebene Eigenschaften geändert. |
| obj.delete(); | Löscht das Objekt aus dem Speicher. |
Zeichnen
Die Zeichen-Funktionen sind noch nicht genau ausdefiniert, hier mal ein Ansatz, wie's warscheinlich aussehen wird.
Achtung: Die Zeichenfunktionen dürfen nur in den dafür zuständigen Callbacks aufgerufen werden; alle anderen Aufrufe produzieren einen Engine-Fehler (d.h. die Engine "sieht" einen illegalen Aufruf und produziert eine Fehlermeldung).
Koordinaten-System: Je nach Art des Callbacks werden 3 unterschiedliche Koordinaten-Systeme verwendet:
- Hintergrund-Koordinaten: gezeichnet wird direkt auf dem parallax-Scrollenden Hintergrund
- FG-Koordinaten: gezeichnet wird in div. Zeichen-Phasen mit dem gleichen Koordinaten-System, mit dem FG-Objects arbeiten
- Screen-Koordinaten: Zum zeichnen eines eigenen HUD (Head-Up-Display; Punkteanzeige u.ä.) wird in Bildschirm-Koordinaten gearbeitet
Alle Funktionen befinden sich im "bs.paint." Namespace, der ungefähr so aussehen wird (unvollständiges Beispiel):
| Zeichen-Funktionen | |
|---|---|
| bs.paint.line(x1, y1, x2, y2, color); | Linie |
| bs.paint.rectangle(x1, y1, x2, y2, color); | Rechteck |
| bs.paint.filledrectangle(x1, y1, x2, y2, color); | Ausgefülltes Rechteck |
| bs.paint.circle(x, y, radiusx, radiusy, color); | Kreis/Ellipse |
| bs.paint.filledcircle(x, y, radiusx, radiusy, color); | Ausgefüllter Kreis oder Ellipse |
| bs.paint.text(x, y, "text", color); | Text |
| bs.paint.blend.add(x1, y1, x2, y2, factor);
bs.paint.blend.multiply(x1, y1, x2, y2, factor); bs.paint.blend.divide(x1, y1, x2, y2, factor); | Blending-Funktionen (Rechteck) |
| bs.paint.rotozoom(x, y, width, height, degree, gfxobj); | Zeichnen eines GFX-Objektes mit optionaler Rotation und Zoom |
In Planung
BMF/BFS
Geplant sind Bindings zum integrierten BMF-Handling (read-only) und zum neuen BFS Read/Write "Filesystem".
Menüsteuerung
Später einmal wird das Menü in LUA neu implementiert. Auch LUA-Addons werden diese Menü-Funktionen für eigene Zwecke nutzen können.
Default-Monster, ExtraLeben und BonusPoints per lua "ins leben rufen"
Prima wäre auch, wenn man Default-Monster, ExtraLeben und BonusPoints per lua "ins leben rufen" könnte oder zumindest zugriff auf vorhandene Objekte hätte. So könnte zB erst nach einem bestimmten Ereigniss ohne großen Aufwand Bonus-Punkte sichtbar machen und Monster "wieder auferstehen lassen".
Falls man keine neuen Objekte erzeugt per lua sondern zugriff auf vorhandene nimmt (die man dann zb in levelinit erstmal auf "unsichtbar" und "bereits eingesammelt" bzw "schon getötet" setzt) ist natürlich die frage, wie man die dann gezielt "adressieren" kann... ...vielleicht über die tile-coords die das gewünschte objekt in der .dat hat?
ToDo
Still todo:
- zeichnen per lua
- alle bsl-scripte konvertieren nach oo-lua
- delte-funktionen
und:
Die "Textproperties" von fgObjects, die Callbacks, wo man eine komplett eigene Physics machen kann, die Callbacks für FX- und BG-Sound, die gesamten BG-Sound-Funktionen, die Paint-Funktionen und -Callbacks, BMF/BFS-Integration, Menü-Scripting, Scripting-Objects im level.dat (a-z), Handling von Default-Monstern ExtraLeben und BonusPunkten, und noch ein paar Kleinigkeiten...
