Intern/RFC1

From BlinkenSisters

(Redirected from Intern/RFC OO LUA)

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.
Move-Typen
bs.player.movetype.right

bs.player.movetype.left

Entspricht sister_moveright und sister_moveleft. Immer verfügbar.
bs.player.movetype.rightup

bs.player.movetype.rightdown bs.player.movetype.leftup bs.player.movetype.leftdown bs.player.movetype.up bs.player.movetype.down

Zusätzliche Move-Types für den FreeMode
bs.player.movetype.nomove Zusätzlicher Move-Type für den FreeMode. Wird angezeigt, wenn sich die Spielfigur nicht vom Spieler bewegt wird. DIeser Move-Type kann derzeit nur über ein ANIM-Objekt animiert werden!

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.
Loop-Typen
bs.anim.type.forwardloop Normaler vorne-nach-hinten Loop; default für neue Anims
bs.anim.type.reverseloop hinten-nach-vorne Loop
bs.anim.type.pingpong vorne-nach-hinten-nach-vorne Loop
obj.currentframe(frame); Liest/setzt den aktuell dargestellten Frame
obj.currentdirection(dir); Liest/setzt bei Pingpong-Loops die Animationsrichtung (nach vorne/hinten)
Richtungen
bs.anim.direction.forward Vorwärts
bs.anim.direction.backward Rückwärts
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).
Beispiele
"killing" Setzt Killing
"killing,visible" Killing und Sichtbar
"!killing,visible,!pixel" Sichtbar, aber weder killing noch Pixel
"ontop" Wird in oberster Ebene gezeichnet
"evevator" Ist Aufzug
"pixel" ist pixel
"blocking,visible" sichtbar und blockierend

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...

Links