Konvertieren von C-Skript-Code nach lite-C
lite-C ist der Syntax von C/C++ sehr viel ähnlicher als C-Skript. Während
der Code selbst weitgehend unverändert bleiben kann, sind einige subtile
Änderungen in Definitionen und Angaben nötig. Hier finden Sie alle
notwendigen Schritte zum Konvertieren Ihres C-Skript-Codes in C .
Bevor Sie anfangen - bereinigen Sie Ihren Code
Bisher konnten Sie beim Schreiben Ihres Codes ruhig ein wenig nachlässig sein
und z.B. Gleichheitszeichen (=), Kommata oder Klammern weglassen, ohne daß
sich der Kompilierer beschwert hätte. Nicht mehr. Sorgen Sie dafür, daß die
Syntax Ihrer Skripte korrekt ist. Sollten Sie immer noch überkommene Schlüsselworte
und Anweisungen aus uralten WDL-Skripten verwenden, ist es spätestens jetzt
an der Zeit, diesen alten Krempel loszuwerden. Der lite-C-Kompilierer kennt
definitiv keine Schlüsselworte, die nicht im Handbuch beschrieben sind, die
führen zu Fehlermeldungen. Nur einwandfreier C-Skript-Code läßt sich reibungslos
in lite-C übertragen.
Um das Ganze einfacher zu gestalten, können Sie mit Textumwandlungs-Software
arbeiten. ReplaceEm (z. B. unter http://www.freeware-archiv.de/BKReplaceEm-Dateien.htm) ist ein kostenloses Werkzeug, welches sich zum fast automatischen Aufräumen
Ihres C-Skript-Codes und anschließenden Konvertieren in lite-C verwenden läßt.
Hier finden Sie zwei Austauschtabellen für dieses Tool: wdl_to_wdl.txt bereinigt
schlampigen Code in Ihrer C-Skript-Datei und wdl_to_c.txt konvertiert
ihn anschließend in lite-C. In vielen Fällen wird das Konvertieren dennoch
etwas Handarbeit (s. u.) brauchen, die beiden automatischen Austauschoperationen
sollten 70% der Aufgabe übernehmen.
Vergewissern Sie sich auch, daß Ihr Code mathematisch korrekt ist. Fehler
- z. B. das Vergleichen nicht ganzzahliger Wert mit '==' or '!=' -
können in C-Skript und in lite-C zu verschiedenen Ergebnissen führen.
Nehmen Sie '.wdl' oder '#define PRAGMA_'
für Projekt-Vorgaben
Anstelle von .wdl haben Skripte nun die Extension .c
oder .h. Machen Sie es sich zur Gewohnheit, .c für
Skripte, die Variablen, Pointer oder Code enthalten, zu verwenden und .h für
Skripte, die nur Definitionen oder Includes beinhalten.
Am Anfang des Main-Skripts muss jeweils immer acknex.h eingefügt
sein. Die Default-Engine-Keys und Funktionen zur Anzeige des Debugpanels,
das
Bewegen der Kamera usw. werden nun in default.c eingefügt.
Sie können während der Zeit des Entwickelns default.c. einfügen und es zum Publishen
entfernen.
In einem Projekt mit lite-C dienen .wdl-Dateien nun lediglich
zum Definieren von Pfaden, Resourcen, Ordnern und dem StartUp-Fenster. Für
Code, Variablen, Includes etc. lassen sie sich nicht verwenden. Wenn sie denselben
Namen wie die .c-Datei hat, wird eine.wdl-Datei beim Hochfahren automatisch
gelesen. Hat Ihr Projekt z.B. den Namen "test.c",
geben Sie Ihre Satements PATH, RESOURCE, SAVEDIR, PLUGINDIR,
WINDOW etc. in der "test.wdl" an. Alternativ
benutzen Sie #define PRAGMA_..-Statements für Projekteinstellungen.
Namen: beachten Sie die Schreibweise
Achten Sie darauf, immer dieselbe Schreibweise für Ihre Variablen- und Objektnamen
zu verwenden. MyVariable und myvariable sind in C zwei verschiedene
Variablen! Als Konvention werden vordefinierte Structs, Flags und Definitionen
in Großbuchstaben (wie PANEL oder SHOW) geschrieben, während
Engine-Funktionen und -Variablen (wie level_load() oder time_step)
in Kleinbuchstaben geschrieben werden.
# Praeprozessor-Definitionen
C-Script hatte keinen Praeprozessor, daher wurden define- oder include-Zeilen
vom Kompilierer selbst ausgewertet. In C werden solche Statements vor dem Kompilieren
von einem Praeprozessor verarbeitet. Zur Unterscheidung zwischen Compiler-Code
und Praeprozessor-Anweisungen beginnen letztere mit '#' und haben kein
Semikolon am Ende:
#include "material.c" // C-Script: include <material.wdl>;
#define MYNUMBER 17 // C-Script: define MYNUMBER,17;
#ifdef MYDEF // C-Script: ifdef MYDEF;
...
#else // C-Script: ifelse;
...
#endif // C-Script: endif;
Sie haben nun den Vorteil, dass Sie einfache Berechnungen in #define-Statements
haben können und sich `Macros´ definieren lassen, die vor dem Kompilieren
ausgewertet
werden.
#define MYNUMBER (17+15)
#define NEGATIVE(x) (-x)
Organisieren Sie Ihre Skripte
Der Kompiler von lite-C handhabt per include eingefügte Skripte wie jeder
C/C++-Kompiler. Wir empfehlen, alle .c-Skripte im Work-Ordner zu haben, aber
sie können auch Unterordner verwenden. In diesem Fall müssen Sie den
entprechenden Pfad angeben, beispielsweise #include "myscripts\script.c".
Ordnernamen für Include-Dateien dürfen keine Leerzeichen enthalten.
Die Haupt-c.-Datei muss genauso wie vorher im Work-Ordner sein. PATH-
oder RESOURCE-Statements aus der .wdl-Datei lassen sich für
Includes nicht benutzen. Nun ist Ihr .c-Code vor dem Publishen
vollständig als .exe kompiliert, so dass Ihre Skripte im
veröffentlichten Spiel nicht enthalten sind.
Ersetzen Sie <> durch""
In C-Skript, vor Version 6.40, kennzeichneten spitze Klammern <> Dateinamen
zur Aufnahme ins gepublishte Game. Nicht mehr. In C verursachen Strings in spitzen
Klammern Syntaxfehler. Dateinamen werden nun automatisch aufgespürt und
einbezogen. Die Dateiextensionen zum Aufnehmen bzw. Ausschließen können
in data\options.scr. gesetzt werden.
In #include-Statements bedeuten <..> eine
Systemdatei (im Unterordner include) und ".." steht
für eine Projektdatei (im work-Ordner des betreffenden
Projekts
oder
einem Unterordner).
Verwenden Sie Funktions-Prototypen
In C-Skript könnten Sie eine Funktion, so sie keine Argumente hätte, aufrufen,
ehe sie definiert wurde. In lite-C muß jede Funktion - auch wenn sie keine
Argumente hat - entweder definiert oder es muß ein Prototyp angemeldet sein,
ehe sie aufgerufen werden kann. Beispiel:
function particle_explosion(PARTICLE* p); // function prototype
...
particle_explosion(p); // function call
...
function particle_explosion(PARTICLE* p) // function definition
{
...
}
Verwenden Sie stets Pointer
In C-Skript mussten wir zwischen Objektdefinitionen (PANEL mypanel = {...
}) und Objekt-Pointern (PANEL* mypanelpointer = mypanel;) unterscheiden.
In lite-C sind Objekte immer Pointer, nun heißt es PANEL* mypanel =
{ ... }.
Initialisieren Sie stets die Variablen
In C-Skript wurden nicht initialisierte lokale Variablen (z.B. var myvar;)
automatisch auf Null initialisiert. Nicht mehr. Nun müssen Sie Variablen
entweder in der Definition initialisieren (z.B. var myvar = 0;) oder Sie
lassen sie, aber nur, wenn ihr Initialwert nicht von Bedeutung ist, uninitialisiert.
Da Structs nicht in ihrer Definition definiert werden können, verwenden
Sie zum Initialisieren lokaler oder globaler Structs auf Null das Makro zero() (es
ist in acknex.h definiert) und zum Initialisieren von Vektoren, die aus
3 vars bestehen, vec_zero():
VECTOR speed;
vec_zero(speed); // initializes the VECTOR "speed" to x=0,y=0,z=0
Zum Umstellen und Testen kann lite-C mit der Definition PRAGMA_ZERO automatisch
alle globalen und lokalen Variablen auf Null initialisieren. Dazu fügen
Sie folgendes hinzu:
#define PRAGMA_ZERO // initialize variables
Am Anfang Ihres Skriptes werden sämtliche nicht-initialisierte globale
und lokale Variablen auf Null gesetzt. Dies verlangsamt ein wenig die Funktionsaufrufe
und daher ist es vorzuziehen, PRAGMA_ZERO nicht in Ihrer Endversion
zu benutzen. Globale Variablen und Structs werden in lite-C immer och automatisch
auf Null initialisiert, dennoch sollten Sie es sich zur Gevohnheit machen,
ihre Initialwerte anzugeben.
Prüfen Sie Vektoren und Arrays
In C-Skript könnten wir für var-Arrays x-, y-, z-Elemente
benutzen. In lite-C gibt es das VECTOR-Struct. Um also x-, y-, z-Elemente
anzuwenden und um Engine-Funktionen, die VECTOR* -Pointer erwarten
aufzurufen, müssen Sie nun anstelle eines var[3] einen VECTOR* definieren:
var vSpeed[3] = 10,20,30; // C-Script
var vSpeed[3] = { 10,20,30 }; // lite-C - address with [0], [1], [2]
VECTOR* vSpeed = {x=10;y=20;z=30;} // lite-C - address with .x, .y, .z
!! Ein falsches Anwenden von
Arrays kann zu Ärger beim Konvertieren eines Skripts führen. In C-Skript
entsprach der Name eines Arrays seinem ersten Element. In C ist er die
Adresse des Arras. Der folgende Code nun ist in seiner Syntax korrekt,
führt jedoch in C-Skript und lite-C zu unterschiedlichen Ergebnissen.
var my_vector[3];
...
var x = my_vector; // C-Script: x is set to the first array element
var x = my_vector; // lite-C - wrong: x is set to the array pointer!
var x = my_vector[0]; // lite-C - correct: x is set to the first array element
Das Verwenden von Funktionen, Starter-Funktionen und DLL-Funktionen
function und action sind nach wie vor gültig und zu var und void per
'typedef' definiert. Dllfunction wird nicht mehr benutzt -
definieren Sie den Prototypen der Plugin-Funktion einfach wie jede andere Funktion.
Der Kompiler erkennt
automatisch ob die Funktion in einem PlugIn sitzt oder nicht. Starter-Funktionen
brauchen den Anhang _startup. Näheres siehe function.
In lite-C läuft die main-Funktion vor dem ersten Frame und ermöglicht
so Videoeinstellungen bevor die Video-Hardware initialisiert wird. Das
bedeutet,
dass Sie einen Frame warten müssen - wait(1) - ehe
Sie Funktionen, wie beispielsweise video_switch() oder video_set() aufrufen,
die auf die Video-Hardware zugreifen.
Setzen Sie Engine-Variablen oder Events in Funktionen
In C-Skript konnten Tastatur- oder Mausevents außerhalb
von Funktionen gesetzt oder neu definiert werden. In C müssen sämtliche
Anweisungen - einschließlich
Events - innerhalb einer Funktion stehen. Um beim Starten Tasten zuzuweisen,
nehmen Sie die main- oder eine Startup-Funktion:
function ie_startup()
{
d3d_autotransparency = ON;
on_f2 = ie_save;
on_f3 = ie_load;
on_f5 = ie_video;
on_f6 = ie_shot;
on_f10 = ie_exit;
}
Setzen, Zurücksetzen oder Umschalten von Flags
In C-Skript wurden Flags durch ein flags-Statement in einer
Objektdefinition gesetzt, in Funktionen aber als Einzelparameter behandelt.
In C wird der flags-Parameter außerdem in Funktionen zum
Setzen oder Zurücksetzen von Flags benutzt.
mypanel.flags |= SHOW; // C-Script: mypanel.SHOW = ON;
mypanel.flags &= ~SHOW; // C-Script: mypanel.SHOW = OFF;
Das mag Anfängern unheimlich erscheinen und ist in der Tat weniger intuitiv
als die Flag-Methode von C-Skript. Daher
wurden
in acknex.h zum
Setzen, Zurücksetzen, Umschalten oder Abfragen von Flags einige
Makros
definiert:
#define set(obj,flag) obj.flags |= (flag)
#define reset(obj,flag) obj.flags &= ~(flag)
#define toggle(obj,flag) obj.flags ^= (flag)
#define is(obj,flag) (obj.flags & (flag))
...
set(mypanel,SHOW);
reset(mypanel,SHOW);
toggle(mypanel,SHOW);
if is(mypanel,SHOW) { ... }
Sie haben nun den Vorteil, dass Sie verschiedene Flags in einem einzigen Statement
setzen oder zurücksetzen können:
mypanel.flags |= (SHOW|LIGHT|TRANSLUCENT);
set(mypanel,SHOW|LIGHT|TRANSLUCENT);
!!
Bitte beachten Sie: der alte Flag TRANSPARENT wurde in TRANSLUCENT umbenannt
(denn TRANSPARENT wird bereits vom Windows-GDI-Header benutzt).
Achten Sie ausserdem darauf, den richtigen flags-Parameter
zu setzten, denn Entities haben mehr als einen: Es gibt auch eflags, emask,
smask, und flags2 -Parameter, die nicht von Makros
begleitet werden (obgleich Sie einige definieren könnten):
ENTITY* eSky = { type = "clouds+2.tga"; flags2 = SKY | DOME | SHOW; }
Speichern und Laden
Wenn Sie in den while() { ... wait(); }-Schleifen oder -Funktionen
dynamisch veränderte globale oder lokale Pointer verwenden, seien Sie
sich der Tatsache bewußt, daß Pointer mit Ausnahme der my-
und
you-Entity-Pointer unter lite-C nicht gespeichert werden.
Sie müssen daher sicher gehen, daß Pointer, wenn sie innerhalb der Schleife
benutzt werden, auch auf das richtige Objekt zeigen. Entity-Pointer oder Engine-Objektpointer
können, um speichersicher zu sein, in ein handle konvertiert werden.
Execute
Der lite-C-Compiler hat keine Interpreter-Funktion. Die execute-Anweisung
kann lediglich zum Aufruf von Engine-Funktionen mit konstanten Parametern verwendet
werden. Es gibt die neue Funktion var_for_name,
welche den Inhalt einer Variablen oder eines Strings zur Laufzeit anzeigt und
verändert. Die [Tab]-Konsole verwendet nun var_for_name.
Geben Sie den Namen eines globalen var, int,
long, float oder eines STRINGs
ein und drücken Sie [Eingabe]. Sein Inhalt wird angezeigt.
Indem Sie wieder [Eingabe] drücken, können Sie den
Inhalt editieren und verändern. Hier ist der lite-C-Code für [Tab]
(in include\default.c):
TEXT* def_ctxt = { string = "Enter var or STRING","#80"; layer = 999; }
void def_console() // Tab
{
def_ctxt.pos_x = 2;
def_ctxt.pos_y = screen_size.y-30;
toggle(def_ctxt,SHOW);
while is(def_ctxt,SHOW) {
beep();
inkey((def_ctxt.string)[1]);
if (13 == result)
var_for_name((def_ctxt.string)[1]);
else
reset(def_ctxt,SHOW);
}
}
Proc_late
Wurde ersetzt durch proc_mode = PROC_LATE; vor dem wait() Aufruf.
sleep(x)
Gibt es nicht mehr - verwenden Sie stattdessen wait(-x).
Aber Achtung: Sleep(x) (mit großem 'S') gibt es sehr wohl,
es ist eine Standard-Windows-Funktion, die sich deutlich anders verhält,
als das alte sleep()!
Trigonometrische Funktionen
In C verwenden die trigometrischen float/double-Funktionen
(sin, cos, tan, asin,
acos, atan) für Winkelwerte Rad
anstelle von Grad. Für die Grad-Trigonometrie bietet lite-C spezielle var-Funktionen:
(sinv, cosv, tanv, asinv,
acosv, atanv). Sie entsprechen ihren Gegenstücken
sin, cos, tan, asin,
acos, atan in C-Skript. Die 6 trigonometrischen
Funktionen sind 'overlaoded', d.h. sie ermitteln anhand der
Tatsache, ob ihr Argument ein var oder ein float/double ist,
automatisch, ob Sie Grad oder Rad verwenden sollen. Wenn jedoch mögliche
Unklarheiten bestehen - z.B. beim Aufrufen einer trigonometrischen Funktion
mit einer Konstanten - müssen Sie zwischen den Versionen float/double und var
unterscheiden.
x = sinv(45.0); // C-Script: x = sin(45.0);
var_info, var_nsave
Wurden beide durch var ersetzt. Anstatt var_info zu
benutzen, lassen Sie die Variable mit "_i" enden; anstelle
von var_nsave , lassen Sie die Variable mit "_n" enden.
Skills
Entity- und Materialskills sind nun ein Array (skill[0]..skill[99])
anstatt einzelne Parameter (skill1..skill100). Damit jedoch nicht
sämtliche Skill-Anweisungen geändert werden müssen, lassen sich
die alten Skillnamen immer noch verwenden. Dafür gibt es in include\compat.h,
das automatisch im Code enthalten ist, viele #defines.
Arrays
In C werden die Anfangswerte von Arrays von geschweiften Klammern {} umschlossen.
Beispiel:
var test[3] = { 1,2,3 }; // C-Script: var test[3] = 1,2,3;
STRING
In lite-C muss Strings beim Definieren ein Wert zugewiesen werden.
Andernfalls
sind sie lediglich leere String-Pointer. Sie sind immer von variabler Länge.
Die Syntax hierfür ist nun:
//STRING* str; // this would be just an empty pointer in C
//STRING* str[80]; // this would be just an array of empty pointers in C
STRING* str = "Now this is \n a string!"; // Allocate a string
STRING* str = ""; // Allocate an empty string
STRING* str = "#1000"; // Allocate a 1000 characters string filled with spaces;
!! Konstante Schriftzeichen,
die Sie (wie "Hier sind ein paar Schriftzeichen")
zwischen Anführungszeichen setzen, haben in C den Typus char[],
in C-Skript aber STRING*.
Da fast alle Engine-Funktionen sowohl char[] als auch STRING* als
Argumente annehmen können, brauchen Sie sich in der Regel also gar
nicht um diesen Unterschied zu kümmern. Einige Anwender-DLL-Funktionen
allerdings
könnten
explizit einen STRING* erwarten. Um solchen Funktionen Argumente
zu übergeben, müssen Sie demzufolge Strings definieren.
TEXT-Strings
TEXT.string ist ein Pointer zu einem Pointer-Array. Daher muß
immer ein Array-Index benutzt werden und aufgrund der Vorrangigkeit der Operatoren
müssen um TEXT.pstring nun Klammern gesetzt werden.
//str_cpy(mytext.string,"Test!"); // C-Script style
str_cpy((mytext.pstring)[0],"Test!"); // C style
FONT
Die Schriftzeichengröße wird nun automatisch aus einem FONT-Bild
berechnet. Ist die Bildbreite durch 11 teilbar, muss das Bild die Zahlen 0...9
zuzüglich
Leerzeichen in einer Reihe beinhalten. Liegt das Verhältnis Breite/Höhe
über 4, muss es aus 4 Reihen von je 32 Schriftzeichen bestehen, andernfalls
aus 8 Reihen zu 32 Schriftzeichen. Diese Kriterien werden von allen uns bekannten
Bitmap-Font erfüllt. Sollten sie auf Ihr Bild nicht zutreffen, muss das
Font-Bild umgestaltet werden. Größe und Typus von Truetype-Fonts
werden durch ein angefügtes "#nbi" angegeben wobei n = Schriftzeichengröße
und optional b = fett (bold) und i = kursiv
(italic) ist. Die Defaultgröße ist 12.
//FONT imgfont = "fontimage.pcx", 7,12; // C-Script style
FONT* myfont = "fontimage.pcx"; // C style
//FONT ttffont = "Arial",1,10; // C-Script style
FONT* ttffont = "Arial #10b"; // C style, size 10, bold
ENTITY, SKY
Layer-Entities lassen sich ähnlich wie in C-Script-Layer definieren,
allerdings gibt es kein separates SKY-Objekt. Der Sky ist eine ENTITY
mit im Parameter flags2 gesetztem SKY-Flag. Beispiel einer Sky-Entity:
ENTITY* eSky = { type = "blood_gorge+6.tga"; flags2 = SKY|CUBE|SHOW; }
PARTICLE
.. ist ein anderes Struct als ENTITY, daher können Sie den my Pointer
nicht mehr für Partikel benutzen. Verwenden Sie stattdessen den PARTICLE* Pointer,
der jeder Partikelfunktion übergeben wird:
function particle_effect (PARTICLE *p)
{
p.size = 50;
...
}}
my, you
..sind globale Pointer, wenn Sie also eine Funktion, um diese zu verändern
aufrufen, werden sie auf einem globalen Gebiet verändert und nicht nur
innerhalb des Bereiches dieser Funktion. Sie werden während wait() noch individuell
beibehalten.
Debugging
Das Debuggen in SED funktioniert genause wie vorher, ohne SED ist es aber
ein wenig anders:
-Setzen Sie, indem sie den Kommentar-Tag '//!' ans Ende der
Zeile stellen, einen Breakpoint in den Code. Breakpoints können nur in
reinen lite-C-Programmen (mit einer main() -Funktion, nicht
mit WinMain()) gesetzt werden und dies auch nur in der Haupt-Datei
(main), nicht in einer eingefügten (include). Sie funktionieren nur nach
dem ersten Frame, wenn Sie sie also in die Main-Funktion setzen, fügen
Sie davor ein wait(1) ein.
- Starten Sie die Engine im Fenstermodus und mit der Kommandozeilenoption -debug.
Sobald der Breakpoint getroffen wird, hält die Engine an und es wird
die gegenwärtige Quellzeile im Titelbalken angezeigt. [Esc]
bricht das Debuggen ab, [F10] geht zur nächsten Anweisung
und [Shift-F10] geht über Funktionsaufrufe hinweg und [Ctrl-F10] läuft
zum nächsten
Breakpoint. Globale Variable können in einem Panel oder einem SED-Watch
beobachtet werden. Lokale Variable lassen sich überprüfen, indem
man sie vorübergehend
durch globale ersetzt.
-d Kommandozeilenoptionen
Zum Veröffentlichen wird der lite-C-Code in eine.exe-Datei
kompiliert. Diese startet um einiges schneller als ein .c-Skript.
Daher können können #defines nicht mehr zur Laufzeit
verändert werden, denn der Code lässt sich nun mal nach dem Kompilieren
nicht mehr ändern. Zum Auswerten der Kommandozeile zur Laufzeit können
Sie stattdessen command_str benutzen. Der -d-Kommandozeilenparameter
setzt globale vars und STRINGs nun direkt. Beispiele:
var Test1 = 0; // is set to 1 by "-d Test1"
var Test2 = 77; // is set to 78 by "-d Test2,78"
STRING* sTest3 = "old"; // is set to "new" by "-d sTest3,new"
Siehe auch:
Pointers, Structs, Functions
► Aktuelle Version Online