[WIP!!!] X3 Scripting-Tutorial

Hier ist der ideale Ort um über Scripts und Mods für X³: Terran Conflict und X³: Albion Prelude zu diskutieren.

Moderators: Scripting / Modding Moderators, Moderatoren für Deutsches X-Forum

User avatar
Reflexer
Posts: 863
Joined: Sat, 21. Feb 09, 12:54
x4

[WIP!!!] X3 Scripting-Tutorial

Post by Reflexer »

Liebe Scripter und solche, die es werden wollen,

in Ermangelung der Tatsache, dass es aktuell kein halbwegs umfassendes Scripting-Tutorial gibt, habe ich mich entschlossen eins zu schreiben.

Das Tutorial richtet sich an Scriptanfänger und fortgeschrittene Scripter. Ich befürchte den Profis unter uns wird es nicht viel Neues bescheren.

Das Tutorial untergliedert sich in unterschiedliche Bereiche.
Ich habe versucht die Themen so zu gliedern, dass sie auf einander aufbauen.
Es ist selbstverständlich nicht notwendig das komplette Tutorial durchzuarbeiten, wenn nur Fragen oder Verständnisprobleme in einem bestimmten Bereich bestehen.

Das Tutorial kann ebenfalls als Ideengeber und Nachschlagewerk verstanden werden.

Ich habe versucht Basiswissen des Scriptens zu vermitteln, Zusammenhänge zu erläutern und alles durch praktische Beispiele abzurunden.

Der Spagat Neulinge nicht zu überfordern und Fortgeschrittenen nicht zu viel bereits bekanntes erneut zu erläutern ist mir vermutlich nicht überall gelungen. Ich bitte dies zu entschuldigen.

Ich hoffe dennoch, dass es mir halbwegs gelungen ist das Tutorial so zu gestalten, dass zumindest einige Scripter was davon haben.



IN BEARBEITUNG



Grundlagen
Einführung in das Scripten unter X3
Scriptvariablen, lokale Variablen und globale Variablen
Arrays

Menüs
Erstellung von Menüs

Sprach- und Logdateien
Sprachdatei (t-File)
Logdatei


Scriptaufrufe
Aufrufparameter und Rückgabewerte
Endlosscripte
Das Setup-Script


Versionierung und automatische Scriptupdates

Signale, Kommandos und Events

Sonstiges
Kommentare
Variablennamen
Hotkey
Ship Command


Beispielprojekt


Obwohl alle Erläuterung hier im Forum nachgelesen werden können, habe ich mich entschlossen das komplette Tutorial zusätzlich als PDF-Datei anzubieten.

PDF-Download (folgt bei Fertigstellung des Tutorials)
Last edited by Reflexer on Sun, 17. Feb 13, 00:36, edited 35 times in total.
User avatar
Reflexer
Posts: 863
Joined: Sat, 21. Feb 09, 12:54
x4

Post by Reflexer »

Einführung in das Scripten unter X3

Das Scripten unter X3 entspricht in vielerlei Hinsicht dem Scripten mit anderen Scriptsprachen.
Wer eine vollwertige, moderne Programmiersprache wie JAVA oder C# erwartet wird sich enttäuscht sehen.
Die Scriptsprache von X3 ist vor vielen Jahren entwickelt worden und entspricht daher dem damaligen Entwicklungsstand solcher Sprachen.
Dies ist meines Erachtens nicht negativ zu bewerten, da die X3-Scriptsprache zum Zeitpunkt ihrer Entstehung durchaus "state of the art" war.
Junge Entwickler müssen sich darüber im Klaren sein, dass unter X3 einige Dinge "kompliziert" und "umständlich" umgesetzt werden müssen – verglichen mit z.B. JScript oder VBA.
An diesem Faktum lässt sich nichts ändern.


Wahl des Editors

Der in X3 angebotene Scripteditor (in der Regel SE genannt) eignet sich um schnell ein paar Zeilen Script zu schreiben.
Meine persönliche Abneigung gegen den SE außen vor gelassen, ist der SE bei genauer Betrachtung äußerst unkomfortabel und die Bedienung ist umständlich.
Meiner Erfahrung nach ist ein zügiges Entwickeln von Scripten hiermit kaum möglich.
Aus diesem Grund gibt es einen externen Editor – den Exscriptor. Ich persönlich verwende ausschließlich diesen Editor.
Der Exscriptor wird wie die meisten anderen Scripteditoren anderer Sprachen verwendet und bietet im Vergleich zum SE einigen Komfort.
Ich würde jedem, der viel und komfortabel scripten möchte, die Verwendung dieses Editors empfehlen.
All meine Beispiele wurden mit dem Exscriptor erstellt – daher fehlen auch die Zeilen-Nummerierungen.

Ein weiterer externen Editor ist X-Studio Script Editor von Mr.Bear.
Ich hab keinerlei Erfahrung mit diesem Editor, wollte ihn jeodch der Vollständigkeit halber angeben.

Alle Scripte müssen mit dem SE oder einem der externen X-Editoren bearbeitet werden, damit sie funktionsfähig sind.
Die Verwendung nativer Editoren ist nicht möglich, da die so erstellten/geänderten Scripte von X nicht korrekt geladen werden können!


.pck oder .xml?

Die Scriptsprache unterstützt die beiden Dateitypen .pck und .xml.
Für X macht es keinen Unterschied in welchem Dateiformat ihr eure Scripte schreibt.
Dennoch empfehle ich das XML-Format, da die Scriptdateien dann mit dem Browser oder einem anderen XML-Viewer/Editor betrachtet werden können.
Das .pck-Format lässt sich hingegen nur mit dem SE oder einem anderen externen X-Scripteditor öffnen.


Die Befehls-Bibliothek und das MSCI


IN BEARBEITUNG
Last edited by Reflexer on Sun, 17. Feb 13, 12:24, edited 22 times in total.
User avatar
Reflexer
Posts: 863
Joined: Sat, 21. Feb 09, 12:54
x4

Post by Reflexer »

Scriptvariablen, lokale Variablen und globale Variablen

Es gibt grundlegende Unterschiede bei den unterschiedlichen Variablendeklarationen.

Variablen, die ihr nur innerhalb des aktuellen Scripts verwendet, heißen Scriptvariablen. Die Deklaration und Verwendung wird nachfolgend erläutert.
Scriptvariablen sind nur innerhalb des jeweiligen Scripts existent und können von einem anderen Script nicht verwendet werden.
Diese Variablen werden nach Beendigung des Scripts „gelöscht“. Ein Zugriff von „außen“ ist nicht möglich.

Um eine Variable von unterschiedlichen Scripten aus verwenden und bearbeiten zu können muss sie entweder als „local variable“ oder als „global variable“ deklariert sein.
Die Erläuterungen hierzu erfolgen nach den Erklärungen der Scriptvariablen.

[ external image ]

Scriptvariablen

Ich gehe davon aus, dass jeder der ein Script schreiben möchte weiß, was eine Variable ist. Falls dies nicht der Fall ist kann das Wissen HIER ergänzt werden.

Alle Variablen in X beginnen grundsätzlich mit einem $ (Dollarzeichen). Ihr solltet bei der Benennung der Variablen immer darauf achten sprechende Namen zu finden.
Eine Variable $1234 oder $hahaha zu nennen erschwert euch selbst und anderen das Lesen eures Codes.
Wenn ihr z.B. ein neues Schiff erzeugt, dann nennt es z.B. $NewShip. Damit könnt ihr was anfangen und auch andere Scripter die euren Code lesen und verstehen wollen.

In X gibt es KEINE Typsicherheit! Die Deklaration von Variablen erfolgt ohne jegliche Typangaben!

Die Deklaration

Code: Select all

$Variable1 = 'Test'
erzeugt eine String-Variable mit dem Wert 'Test'.

Die Deklaration

Code: Select all

$Variable2 = 123
erzeugt eine Integer-Variable mit dem Wert 123.

Die fehlende Typsicherheit führt dazu, dass der Scripter selbst dafür sorgen muss, dass bei Rechenoperationen auch rechenbare Werte in den Variablen stehen.

Die Deklaration

Code: Select all

$Variable3 = '123'
erzeugt einen String!! Alles was in Anführungszeichen zugewiesen wird ist automatisch ein String.
So sind auch alle Werte, die aus t-Files gelesen werden Strings!!!
Habt ihr in einem t-File einen Wert mit dem ihr rechnen wollt, so müsst ihr ihn immer zuerst zu einem rechenbaren Wert umwandeln.
Dies erfolgt mittels:

Code: Select all

 <RetVar> = string <Var/String> to integer
Ein Beispiel dazu:

Code: Select all

$Variable4 = '123'
$Variable4 = string $Variable4 to integer
Das Verbinden von mehreren Strings zu einem längeren String erfolgt wie eine Rechenoperation mittels "+".
Beispiel:

Code: Select all

$String1  = 'Das ist '
$String2 = 'ein langer String.'
$String3 = $String1 + $String2
Wenn ihr allerdings Zahlenwerte in vermeintlichen Text-Variablen habt, so wird es passieren, dass anstatt des Zusammenfügens zu einem längeren String eine Rechenoperation durchgeführt wird.
Aus diesem Grund solltet ihr bei Typunsicherheit die Variable vorher immer in einen String wandeln.
Der Code:

Code: Select all

$Var1 = 4
$Var2 = 6
$Text1 = $Var1 + $Var2
ergibt nicht wie gewünscht '46', sondern 10, da gerechnet wird.

Wollt ihr aus den beiden Variablen des Beispiels einen String zusammensetzen, so müsst ihr die Variablen vorher in einen String umwandeln.
Der Befehl hierzu lautet:

Code: Select all

<RetVar> = convert number <Var/Number> to string
Für unser Beispiel also:

Code: Select all

$Var1 = 4
$Var2 = 6
$Var1 = convert number $Var1 to string
$Var2 = convert number $Var2 to string
$Text1 = $Var1 + $Var2
Das Ergebnis wäre nun das gewünschte, nämlich '46'.


Thema: Berechnungen

X kann nur Ganzzahlen! Es gibt kein Floats oder andere Nachkomma-Typen.
Bei Berechnungen wird alles nach dem Komma abgeschnitten!
Die Berechung

Code: Select all

$Result = 1 / 10
ergibt 0!!

Die Zuweisung eines Kommawertes würde zu einem Fehler führen, weshalb es im Ego-SE auch nicht möglich ist einen solchen Wert einzugeben.

Code: Select all

$Float = 2,2
Ist unmöglich! Ist nicht zu ändern – einfach dran denken.

Der größte Wert einer Anzeigevariable ist 2 Milliarden.

Eine halbwegs schöne Sache gibt es jedoch. Werden Zahlenwerte zu Strings konvertiert, so werden die Tausenderpunkte automatisch gesetzt.

[ external image ]

Lokale Variablen und globale Variablen

Wenden wir uns nun den lokalen und globalen Variablen zu.

Lokale Variablen (lV)

Alle Variablen, die als „local variable“ deklariert sind benötigen ein Objekt, an das sie gebunden sind.
Objekte können Schiffe, Stationen, das Spielerschiff usw. sein. Die grundsätzliche Deklaration sieht folgendermaßen aus:

Code: Select all

 <RefObj> -> set local variable: name=<Var/String> value=<Value>
Ein Beispiel hierzu:
Ihr habt ein Endlosscript geschrieben, welches auf allen Stationen läuft.
Ihr wollt die Version des Scripts auf jeder Station speichern, damit ihr bei einem Update die aktuelle laufende Version ermitteln könnt.
Hierzu speichert ihr die Version folgendermaßen ab:

Code: Select all

 [THIS] -> set local variable: name=’lV.scriptxyz.version’ value=$Version
Nun haben alle Scripte, die auf der Station laufen die Möglichkeit die Version des Scripts abzufragen und irgendetwas zu tun.
Jedes Script der Station kann die Variable folgendermaßen abfragen:

Code: Select all

$Version =  [THIS] -> get local variable: name=’lV.scriptxyz.version’ 
Die lVs sind solange existent, wie das jeweilige Objekt existiert.
Wird das Objekt zerstört verschwindet die lV selbstverständlich ebenfalls.


Globale Variablen (gV)

Alle Variablen, die als “global variable” deklariert sind, sind NICHT an ein Objekt gebunden.
Sie sind von allen Scripten überall abrufbar. Sie existieren einfach innerhalb des Spiels.
Die Deklaration sieht folgendermaßen aus:

Code: Select all

set global variable: name=<Var/String> value=<Value>
Das Problem der gVs besteht darin, dass sie Speicher verbrauchen!
Ihr solltet euch stets darüber im Klaren sein, dass die gVs nicht einfach so „gelöscht“ werden. Solange sie einen Wert haben werden sie Speicher benötigen!
Aus diesem Grund ist es absolut wichtig, dass ihr nicht mehr benötigte gVs wieder freigebt, in dem ihr ihnen den Wert null zuweist.
Genullte gVs werden von der Engine irgendwann automatisch gelöscht – ohne euer Zutun.

Warum sollte man globale Variablen nutzen?
Ich möchte zwei praktische Beispiele meiner Scripte anführen:

Bei meinem Script SOM wird der aktuelle Auftrag der Bestellung mit allen wichtigen Daten in einer gV gespeichert.
Auf diese gV greifen während des Bestellprozesses 10 Scripte zu und verarbeiten bzw. erweitern diese Daten.
Wenn der Auftrag abgeschlossen ist, wird die gV wieder freigegeben (genullt).

In der SFDB ermittle ich zu Beginn EINMALIG alle möglichen Schiffe, Schiffstypen, Rassen usw.
Diese werden in Arrays als gV abgelegt. Die Ermittlung der Daten ist sehr aufwändig und dauert mehrere Sekunden in denen das System „steht“.
Um diese ganzen (relativ statischen Ermittlungen) nur einmalig durchführen zu müssen werden die Ergebnisse in einer gV gespeichert.

Es gibt einige sinnvolle Möglichkeiten für die Verwendung von gVs.


Nachteil der gVs/lVs

Der größte Vorteil der gVs und lVs ist gleichzeitig auch ihr größter Nachteil.
Sie können von allen möglichen Scripts aus verändert werden, was dazu führen kann, dass mehrere Scripte gleichzeitig auf die selbe Variable zugreifen und den Wert verändern.
Es gibt keinen automatischen Lock, der ein gleichzeitiges Ändern verhindert.
Um sicherzustellen, dass immer nur ein Script auf die Variable zugreift (um sie zu ändern) müsst ihr euch einen eigenen Lock-Mechanismus schreiben.

Hierzu habe ich euch ein kleines Beispiel gemacht:

Code: Select all

* ********************************************************************************************************
* ********************************************************************************************************
* ********************************************************************************************************
* Diese Funktion löscht den ersten Eintrag der Feindesliste.
fct.Delete.Enemy:

* Die lV wird gelockt:
gosub fct.Lock.lV.EnemyShips

* Der erste Eintrag wird entfernt:
remove element from array $anEnemyList at index 0

* Die lV wird zurückgeschrieben:
[THIS]->set local variable: name='lV.enemy.ships' value=$anEnemyList

* Der lock wird wieder entfernt:
[THIS]->set local variable: name='plugin.nw.enemyships.opened' value=[FALSE]

endsub
* ********************************************************************************************************
* ********************************************************************************************************
* ********************************************************************************************************
* Diese Funktion locked die lV der Feindschiffe:
fct.Lock.lV.EnemyShips:

* Da meherere Scripte schreibend auf diese lV zugreifen, muss der jeweilige Schreiber diese vorher locken:

* Sprungmarke:
Jump.Edit.lV:
$tmplVOpen = [THIS]->get local variable: name='plugin.nw.enemyships.opened'
if $tmplVOpen
  * In diesem Fall ist die lV gerade in Bearbeitung und es wird 2 Sekunden gewartet
  = wait 2000 ms
  goto label Jump.Edit.lV
else
  * In diesem Fall wird die lV als geöffnet markiert:
  [THIS]->set local variable: name='plugin.nw.enemyships.opened' value=[TRUE]
end

endsub
* ********************************************************************************************************
* ********************************************************************************************************
* ********************************************************************************************************
Um den Lock nicht komplett aus dem Kontext zu reißen habe ich die aufrufende Funktion ebenfalls mit angeführt.
Die erste Funktion löscht einen Eintrag in einem Array, welches in einer lV liegt. Hierzu muss die "Bearbeitung" in einer anderen Variablen "markiert" werden. Ein so genanntes Flag.

Ich hoffe der Code erklärt sich von selbst.


Anmerkung zum Namen

Ich möchte aus gegebenem Anlass noch auf die korrekte Verwendung des Attributs

Code: Select all

... name=<Var/String> ...
hinweisen. Hier ist auf alle Fälle ein String anzugeben! Diese Angabe erfolgt entweder direkt

Code: Select all

... name='Meine.gV1' ....
oder über die Zuweisung einer Variablen, die einen String enthält

Code: Select all

$gVName = 'Meine.gV1'
... name=$gVName ....
Enthält die Variable keinen String, so ist die gV null!

Beim SOM werden die Schiffe pro Rasse/Klasse-Kombination in einem Array abgelegt.
Die gV der Split M2 heißt demnach "plugin.ship.order.manager.Split.M2.all".
Im Bespellprozess wird die entsprechende gV - je nach Usereingabe - geladen, um die kaufbaren Schiffe anzuzeigen:

Code: Select all

$tmpgVName = 'plugin.ship.order.manager.' + $anRace + '.' + $anType + '.all'
$tmpShipsAll = get global variable: name=$tmpgVName
Last edited by Reflexer on Sun, 17. Feb 13, 03:17, edited 27 times in total.
User avatar
Reflexer
Posts: 863
Joined: Sat, 21. Feb 09, 12:54
x4

Post by Reflexer »

Arrays

Arrays werden leider immer wieder falsch verstanden – manchmal sogar überhaupt nicht. Leider sind Arrays nicht ganz so einfach zu erklären wie eine Integer-Variable.

Ich möchte versuchen die Erklärung so einfach wie möglich zu gestalten.

Der grundsätzliche Unterschied zwischen einem Array und einer Variable besteht darin, dass es sich bei einem Array um eine Struktur handelt!
Diese Struktur enthält Variablen, die die eigentlichen Werte enthalten.
Technisch gesehen enthält das Array Zeiger (Pointer) auf die eigentlichen Variablen. Das Array-Objekt existiert hierbei getrennt vom eigentlichen Script.

Ich möchte es an dieser Stelle nicht zu technisch werden lassen. Wer es noch genauer wissen möchte kann sein Wissen über Arrays gerne HIER oder das Wissen über Pointerarithmetik HIER vertiefen.

Es gibt verschiedene Arten von Arrays. Es gibt einfache Arrays, zweidimensionale und multidimensionale Arrays. Aber der Reihenfolge nach!

Alle Arraybefehle befinden sich im Bereich "General Commands" -> "Arrays" der Befehls-Bibliothek.
Link zum MSCI

Das einfache Array

Das einfache Array kann mit einer einfachen Liste verglichen werden. Einem Einkaufszettel zum Beispiel – auf dem untereinander eine beliebige Anzahl von Waren stehen.
Um ein Array anzulegen lautet der Befehl:

Code: Select all

 <RetVar> = array alloc: size=<Var/Number>
Angenommen ihr wollt den Namen, die Geschwindigkeit und die Schild- und Hüllenwerte eures Schiffs in einem Array speichern, so gibt es mehrere Möglichkeiten dies zu tun.

Ihr wisst, dass es 4 Werte sind die ihr speichern wollt und legt die Größe vorher fest.

Code: Select all

* Werte ermitteln:
$tmpName = [PLAYERSHIP]->get name
$tmpSpeed = [PLAYERSHIP]->get current speed
$tmpShield = [PLAYERSHIP]->get current shield strength
$tmpHull = [PLAYERSHIP]->get hull

* Neues Array erzeugen:
$tmpNewArray = array alloc:size=4

* Werte speichern:
$tmpNewArray[0] = $tmpName
$tmpNewArray[1] = $tmpSpeed
$tmpNewArray[2] = $tmpShield
$tmpNewArray[3] = $tmpHull
Wie ihr sehen könnt werden die einzelnen Werte der “Liste” über den Index in den eckigen Klammern gesetzt. Jeder Zugriff (egal ob lesend oder schreibend) auf Einträge eines Arrays erfolgt über den Index. Dieser beginnt immer bei 0!!!

Die Größe vorher festzulegen ist in dem meisten Fällen nicht sinnvoll und verursacht nur unnötigen Aufwand. Ihr könnt ein Array jederzeit mit einer Größe von 0 anlegen und die einzelnen Einträge einfach hinzufügen. Der Befehl hierzu lautet:

Code: Select all

 append <Value> to array <Var/Array>
Der Code unseres Beispiels sähe also mit der Verwendung von „append“ folgendermaßen aus:

Code: Select all

 * Neues Array erzeugen:
$tmpNewArray = array alloc:size=0

* Werte speichern:
append $tmpName to array $tmpNewArray
append $tmpSpeed to array $tmpNewArray 
append $tmpShield to array $tmpNewArray 
append $tmpHull to array $tmpNewArray 
Warum gibt es nun diese beiden Möglichkeiten? Es ist sehr oft so, dass ihr vorher nicht wisst, wie viele Einträge das Array haben wird.

Hierzu ein einfaches Codebeispiel. Nachfolgender Code ermittelt alle Sektoren des Universums und speichert diese in einem einfachen Array ab:

Code: Select all

* Ergebnisarray der Sektoren:
$tmpAllSectors = array alloc: size=0
* Maximale Ausdehnung des Unversums:
$tmpMaxSectorsX = get max sectors in x direction
$tmpMaxSectorsY = get max sectors in y direction
* Startsektoren:
$tmpSectorX = 0
$tmpSectorY = 0
while $tmpSectorX < $tmpMaxSectorsX
  while $tmpSectorY < $tmpMaxSectorsY
    * Einzelner Sektor:
    $tmpSector = get sector from universe index: x=$tmpSectorX, y=$tmpSectorY
    if $tmpSector != null
      * In diesem Fall existiert der Sektor und er wird dem Array hinzugefügt:
      append $tmpSector to array $tmpAllSectors
    end
    inc $tmpSectorY =
  end
  * Zurücksetzen des Y-Wertes für die nächste Reihe
  $tmpSectorY = 0
  inc $tmpSectorX =
end
Da ihr anfangs nicht wisst wie viele Sektoren es gibt, würde es wenig Sinn machen die Größe vorher festzulegen.

Es gibt noch eine weitere Möglichkeit ein Array zu erzeugen.
Wenn ihr nur ein „kleines“ Array mit maximal 5 Werten benötigt, so könnt ihr dies ebenfalls über folgenden Befehl tun:

Code: Select all

 <RetVar> = create new array, arguments=<Value>, <Value>, <Value>, <Value>, <Value>
In unserem ersten Beispiel sähe der Code hierzu also folgendermaßen aus:

Code: Select all

* Neues Array erzeugen und Werte speichern:
$tmpNewArray = create new array, arguments=$tmpName, $tmpSpeed, $tmpShield, $tmpHull, null 
Diese Variante ist bei kleinen Arrays deren Inhalt ihr kennt die schnellste und einfachste Variante.
Achtet jedoch darauf, dass der create immer 5 Argumente haben muss!
Habt ihr weniger als 5 Werte die ihr initialisieren wollt, so müsst ihr die "leeren" Werte mit null angeben.


Das zweidimensionale Array

Kommen wir nun zum zweidimensionalen Array. Diese Art des Arrays könnt ihr am einfachsten mit einer Tabelle vergleichen. Es gibt so zu sagen Zeilen und Spalten. Technisch gesehen besteht der einzelne Eintrag des Arrays aus einem Array. Klingt vielleicht verwirrend – ist es aber nicht.

Hier ein Beispiel dazu:
Ihr habt 2 Freunde und wollt von jedem Freund den Vor- und Nachnamen sowie das Geburtsdatum speichern. Der Code hierzu sähe folgendermaßen aus:

Code: Select all

*Hauparray erzeugen:
$tmpMainArray = array alloc: size=2

* Erster Freund:
$tmpFirstName = 'Fritz'
$tmpLastName = 'Nachname 1'
$tmpBirthday = '12.12.2012'

* Daten in ein Array verpacken:
$tmpFriend = create new array, arguments= $tmpFirstName, $tmpLastName, $tmpBirthday, null, null

* 1. Freund dem Hauptarray hinzufügen:
$tmpMainArray[0] = $tmpFriend

* Zweiter Freund:
$tmpFirstName = 'Hugo'
$tmpLastName = 'Nachname 2'
$tmpBirthday = '10.10.2010'

* Daten in ein Array verpacken:
$tmpFriend = create new array, arguments= $tmpFirstName, $tmpLastName, $tmpBirthday, null, null

* 2. Freund dem Hauptarray hinzufügen:
$tmpMainArray[1] = $tmpFriend
Wie ihr seht habt ihr im "Hauprarray" jetzt 2 Einträge, die jeweils aus einem weiteren Array bestehen. Wollt ihr nun den Nachnamen des ersten Freundes wissen, so erfolgt die Ermittlung (wie bereits erwähnt) über den Index. Das Gute daran ist, dass ihr den Wert über den Index des Index abfragen könnt. Das geht folgendermaßen:

Code: Select all

$tmpLastNameFirstFriend = $tmpMainArray[0][1]
Das Geburtsdatum des zweiten Freundes ist folglich:

Code: Select all

$tmpBithdaySecondFriend = $tmpMainArray[1][2]
Wollen wir dem Beispiel etwas mehr X-Nähe geben und kommen zurück zum Sektor-Beispiel.
Da die Sektoren in X nicht eindeutig über den Namen ermittelt werden können (es gibt mehrere „Unbekannter Sektor“) erfolgt die Identifizierung der Sektoren über die Position in der Galaxie – sprich die X und Y Werte.
Um nun mit unserer schönen Sektorenliste etwas anfangen zu können erweitern wir das obige Script und speichern neben dem Namen noch die X- und Y-Position des jeweiligen Sektors.
Der neue Mittelteil des Codes hierzu sähe folgendermaßen aus:

Code: Select all

if $tmpSector != null
* In diesem Fall existiert der Sektor:
  $tmpNewEntry = create new array, arguments=$tmpSector, $tmpSectorX, $tmpSectorY, null, null
  append $tmpNewEntry to array $tmpAllSectors
end
Abschließend möchte ich noch gerne ein plakatives Beispiel zur Verwendung unseres Sektor-Arrays geben.
Ihr könntet euch ganz einfach ein Logfile erzeugen und alle bekannten Sektoren ausgeben. Das Ganze sähe folgendermaßen aus:

Code: Select all

* Größe des Sektorarrays:
$tmpAllSectorsCount = size of array $tmpAllSectors

* Ausgabe im Logfile:
$tmpText = 'Es sind insgesamt ' + $tmpAllSectorsCount + ' Sektoren vorhanden. Diese sind:'
write to log file 8888  append=[FALSE]  value=$tmpText

* Werte aller Sektoren:
$tmpCounter = 0
while $tmpCounter < $tmpAllSectorsCount
  * Einzelne Sektorwerte:
  $tmpSector = $tmpAllSectors[$tmpCounter][0]
  $tmpX  = $tmpAllSectors[$tmpCounter][1]
  $tmpY  = $tmpAllSectors[$tmpCounter][2]
  * String für die Ausgabe im Logfile erzeugen:
  $tmpText = $tmpCounter + '. Sektor: ' + $tmpSector + ' (X = ' + $tmpX + ' / Y = ' + $tmpY + ')'
  write to log file 8888  append=[TRUE]  value=$tmpText
  * Nächster Eintrag:
  inc $tmpCounter =
end

Das multidimensionale Array

Um das Ganze auf die Spitze treiben zu können gibt es so genannte multidimensionale Arrays.
Hierbei sind vereinfacht gesagt "unendlich" viele Arrays in Arrays "geschachtelt". Es handelt sich dabei um eine Fortführung der zweidimensionalen Arrays.
Der Wert eines Arrays im Array ist hierbei wieder ein Array. Und so weiter und so weiter.
Man nennt so was auch eine Matrix. Ich denke jeder der die Ausführungen zu den Arrays bis hierher verstanden hat kann sich das Prinzip vorstellen.
Ich möchte an dieser Stelle von einem Beispiel absehen, da es meiner Meinung nach nichts bringt.
Wer das Grundprinzip nicht verstanden hat wird auch mit einem Beispiel zur Matrix nicht viel anfangen können.


Array[x][y] = Array[y][x]

Ein gerne gemachter Anfängerfehler ist der Versuch den Wert eines Arrays einem anderen Array zuzuweisen. Beispiel:

Code: Select all

$myArray1[0][0] = $myArray2[1][2]
Dieser Versuch wird nicht von Erfolg gekrönt sein, da die Scriptsprache es nicht zulässt.
Wollt ihr Werte eines Arrays einem andern Array zuweisen, so müsst ihr den Umweg über eine Variable wählen.
Um das gewünschte Ergebnis zu erzielen wäre der Weg:

Code: Select all

$ZwischenVariable = $myArray2[1][2]
$myArray1[0][0] = $ZwischenVariable


Fallstrick Array

Wie bereits in der Einleitung erwähnt ist ein Array eine Struktur mit Pointern. Pointer folgen eigenen, ganz speziellen Regeln.
Ein Pointer „zeigt“ lediglich auf einen Wert, ohne diesen selbst zu „besitzen“.

Ich möchte ein - vielleicht blödes – aber hoffentlich anschauliches Beispiel zur Erläuterung geben.
Stellen wir uns das Register eines Katasteramts als Array vor. Dem Katasteramt sind alle Häuser einer Stadt bekannt. Es weiß wo genau sich welches Haus befindet.
Nun sind die Einträge im Register des Amtes vergleichbar mit Pointern, da sie auf das Objekt (Haus) verweisen.
Die Häuser sind in diesem Beispiel unsere Variablen.
Die Eigentümer bzw. Mieter der Häuser sind vergleichbar mit dem Inhalt der Variablen.
Ändert sich nun der Eigentümer oder Mieter des Hauses, so ist dies dem Katasteramt egal. Das Haus bleibt das Haus und steht nach wie vor an derselben Stelle.
Der Eintrag im Register wird sich deshalb nicht ändern.
Das Katasteramt besitzt auch die Häuser nicht, sondern verwaltet lediglich die Standorte.

IN BEARBEITUNG
Last edited by Reflexer on Wed, 28. Nov 12, 02:11, edited 15 times in total.
User avatar
Reflexer
Posts: 863
Joined: Sat, 21. Feb 09, 12:54
x4

Post by Reflexer »

Erstellung von Menüs

Die Erstellung von Menüs ist eine der einfachsten Aufgaben beim Scripten in X.

Ich möchte das Ganze anhand von zwei Beispielmenüs erklären.

Grundsätzlich ist es bei allen Menüs wichtig, dass alle Texte in einem t-File abgelegt sind, damit sie einfach zu internationalisieren sind.
Änderungen an Texten, die mehrfach verwendet werden, sind hierdurch ebenfalls deutlich schneller gemacht.
Grundsätzliches zu den t-Files kann dem entsprechenden Themenblock dieses Tutorials entnommen werden.

Es ist sehr empfehlenswert sich vor Beginn der Arbeiten an einem Menü Gedanken über den Aufbau zu machen.
Meiner Erfahrung nach eignet sich eine Skizze ziemlich gut dazu. Einfach ins „Blaue hinein“ ein Menü zu erstellen wird in der Regel viel mehr Arbeit bereiten, als sich im Vorfeld Gedanken darüber zu machen.

Alle Menübefehle befinden sich im Bereich "Others" der Befehls-Bibliothek.
Link zum MSCI

[ external image ]

Beispielmenü 1

Entgegen meiner Einführungsempfehlung möchte ich der Einfachheit halber im ersten Schritt der Erklärung die jeweiligen Texte statisch codieren und nicht aus einem t-File lesen.

Das erste Beispielmenü soll folgendermaßen aussehen:

[ external image ]

Ein Menü ist ein Array, welches die einzelnen Menüelemente beinhaltet.
Es ist der "Container", der alles enthält, was angezeigt werden soll.

Um das Menü-Array zu erzeugen wird folgender Befehl verwendet:

Code: Select all

 <RetVar> = create custom menu array
In unserem Beispiel wollen wir das Menü folgendermaßen benennen:

Code: Select all

* Menü-Array erzeugen:
$Mainmenu.Menu = create custom menu array
Als nächstes wird der erste "Bereich" mit der Überschrift "Schiffsdaten" erzeugt.
Der Befehl hierzu lautet:

Code: Select all

 add custom menu heading to array <Value>: title=<Var/String> 
Im unserem Fall also:

Code: Select all

 add custom menu heading to array $Mainmenu.Menu: title='Schiffsdaten' 
Um den Bereich mit einzelnen Einträgen zu versehen wird folgender Befehl verwendet:

Code: Select all

 add custom menu item to array <Value>: text=<Var/String> returnvalue=<Value> 
Hierbei ist der bei "returnvalue" (Rückgabewert) angegebene Wert der Wert, der bei einer Selektion des Eintrags durch den Anwender zurückgegeben wird. Hierzu später mehr.
Da wir unser Menü zu diesem Zeitpunkt mit keiner Funktion versehen geben wird beim Rückgabewert null an.

Unsere ganzen Menüeinträge werden also folgendermaßen erzeugt:

Code: Select all

add custom menu item to array $Mainmenu.Menu: text='Schiff:' returnvalue=null
add custom menu item to array $Mainmenu.Menu: text='Besitzer:' returnvalue=null
add custom menu item to array $Mainmenu.Menu: text='Klasse:' returnvalue=null
add custom menu item to array $Mainmenu.Menu: text='Schildstärke:' returnvalue=null
add custom menu item to array $Mainmenu.Menu: text='Hülle:' returnvalue=null 
Um einen kleinen optischen Abstand zwischen die "Klasse" und die "Schildstärke" zu bekommen, wird eine Art "Trenner" eingefügt.
Dieser ist rein optischer Natur und dient der besseren Strukturierung.

Ein "Trenner" wird mit folgendem Befehl erzeugt:

Code: Select all

 add section to custom menu: <Var/Array> 
Der zweite Bereich des Menüs wird genauso erzeugt wie der erste.

Die Überschrift des Menüs, sowie der Beschreibungstext werden beim Anzeigen des Menüs angegeben.

Um das komplette Menü anzuzeigen wird folgender Befehl verwendet:

Code: Select all

 <RetVar/IF> open custom menu: title=<Var/String> description=<Var/String> option array=<Var/Array> 
Unser aktueller Code - der bislang nichts kann, außer ein paar Texte anzuzeigen – sieht nun folgendermaßen aus:

Code: Select all

* Menü-Array erzeugen:
$Mainmenu.Menu = create custom menu array
* Erster Bereich:
add custom menu heading to array $Mainmenu.Menu: title='Schiffsdaten'
* Einträge:
add custom menu item to array $Mainmenu.Menu: text='Schiff:' returnvalue=null
add custom menu item to array $Mainmenu.Menu: text='Besitzer:' returnvalue=null
add custom menu item to array $Mainmenu.Menu: text='Klasse:' returnvalue=null
* Trenner:
add section to custom menu: $Mainmenu.Menu
add custom menu item to array $Mainmenu.Menu: text='Schildstärke:' returnvalue=null
add custom menu item to array $Mainmenu.Menu: text='Hülle:' returnvalue=null

* Zweiter Bereich:
add custom menu heading to array $Mainmenu.Menu: title='Zurück/Abbruch'
* Eintrag:
add custom menu item to array $Mainmenu.Menu: text='Script Tutorial Menü beenden' returnvalue=null

* Menü anzeigen:
$anMenu = open custom menu: title='Script Tutorial' description='Script Tutorial' option array=$Mainmenu.Menu 
Wichtig bei der Erstellung des Menü-Arrays ist die Tatsache, dass die Einträge in der Reihenfolge angezeigt werden, in der sie dem Menü-Array hinzugefügt wurden.
Ein nachträgliches Einfügen von Menüeinträgen irgendwo in der Mitte ist nicht möglich.
Ebenso ist es nicht möglich einen bereits hinzugefügten Eintrag wieder zu entfernen.

Ich denke die Grundlagen des Menüaufbaus sollten an dieser Stelle jetzt klar sein.

Leider kann unser bisheriges Menü rein gar nichts und die Texte sind statisch codiert.
Alles in allem ist das Menü bis hierher zwar funktionsfähig aber leider sehr unprofessionell gelöst.

Um dies zu ändern möchte ich das aktuelle Menü um folgende Punkte erweitern:

1. Die Texte sollen aus einem t-File gelesen werden.
2. Die Daten des Schiffs, das der Spieler im Ziel hat, sollen angezeigt werden.

Das gewünschte Ergebnismenü soll nach den Änderungen folgendermaßen aussehen:

[ external image ]

Hierzu habe ich ein rudimentäres t-File mit der ID 8600 angelegt, welches folgende Daten beinhaltet:

Code: Select all

<?xml version="1.0" encoding="UTF-8"?>
<language id="49">

<page id="8600" title="ScriptTutorial" descr="ScriptTutorial">
<t id="1">Script Tutorial</t>

<t id="100">Schiffsdaten</t>
<t id="101">Schiff:</t>
<t id="102">Besitzer:</t>
<t id="103">Klasse:</t>
<t id="104">Schildstärke:</t>
<t id="105">Hülle:</t>

<t id="1000">Zurück/Abbruch</t>
<t id="1001">Script Tutorial Menü beenden</t>

<t id="2000">1.0.00</t>
</page>

</language>
Die benötigten Daten des vom Spieler gewählten Ziels werden folgendermaßen ermittelt:

Code: Select all

* Ermitteln der Daten:
* --------------------
* Ziel des Spielers:
$tmpTarget = get player tracking aim
* Name des Schiffs:
$tmpName = $tmpTarget -> get name
* Klasse des Schiffs:
$tmpClass = $tmpTarget -> get object class
* Besitzer:
$tmpOwner = $tmpTarget -> get true owner
* aktueller Schildwert:
$tmpShield = $tmpTarget -> get current shield strength
* Zahlenwert in einen String convertieren um die Tausenderpunkte zu setzen:
$tmpShield = convert number $tmpShield to string
* aktueller Hüllenwert:
$tmpHull = $tmpTarget -> get hull
$tmpHull = convert number $tmpHull to string
Ich denke der Code sollte selbsterklärend sein.

Um das angelegte t-File im Script verwenden zu können muss es folgendermaßen initialisiert werden:

Code: Select all

* Lesen des t-Files:
* ------------------
$PageID = 8600
load text: id=$PageID 
Nun geht es daran die neuen Daten in das Menü zu integrieren.

Im Gegensatz zur ersten Version des Menüs besteht im neuen Menü jeder Menüeintrag aus 2 Teilen.
Links steht der beschreibende Text aus dem t-File, rechts der eigentliche Wert, der im Vorfeld ermittelt wurde.

Um zwei Werte in einem Menüeintrag zu verbinden gibt es folgenden Befehl:

Code: Select all

 <RetVar> = create text for custom menu, left=<Var/String>, right=<Var/String> 
Mittels dieses Befehls werden die beiden Komponenten zu einer Text-Variable verbunden.

Beginnen wir wieder mit dem ersten Block.
Die Überschrift "Schiffsdaten" wird aus dem t-File gelesen und dem heading als title übergeben. Danach erfolgt die Zuweisung zum Menü-Array:

Code: Select all

 $Menutext = read text: page=$PageID id=100
add custom menu heading to array $Mainmenu.Menu: title=$Menutext 
Bei der Erzeugung der Einträge des Bereichs wird identisch verfahren, allerdings wird der aus dem t-File gelesene Text zuerst noch mit dem eigentlichen Wert "verbunden":

Code: Select all

* Text 101: Schiff:
$Text = read text: page=$PageID id=101
$Menutext = create text for custom menu, left=$Text, right=$tmpName
add custom menu item to array $Mainmenu.Menu: text=$Menutext returnvalue=null 
Diese Änderungen werden für alle Einträge durchgeführt. Der komplette Code des Menüaufbaus sollte anschließend folgendermaßen aussehen:

Code: Select all

* Menüaufbau:
* ------------
* Menü-Array erzeugen:
$Mainmenu.Menu = create custom menu array


* Neuer Bereich - "Schiffsdaten":
* -------------------------------
$Menutext = read text: page=$PageID id=100
add custom menu heading to array $Mainmenu.Menu: title=$Menutext

* Text 101: Schiff:
$Text = read text: page=$PageID id=101
$Menutext = create text for custom menu, left=$Text, right=$tmpName
add custom menu item to array $Mainmenu.Menu: text=$Menutext returnvalue=null

* Text 102: Besitzer:
$Text = read text: page=$PageID id=102
$Menutext = create text for custom menu, left=$Text, right=$tmpOwner
add custom menu item to array $Mainmenu.Menu: text=$Menutext returnvalue=null

* Text 103: Klasse:
$Text = read text: page=$PageID id=103
$Menutext = create text for custom menu, left=$Text, right=$tmpClass
add custom menu item to array $Mainmenu.Menu: text=$Menutext returnvalue=null

* Trenner:
add section to custom menu: $Mainmenu.Menu

* Text 104: Schildstärke:
$Text = read text: page=$PageID id=104
$Menutext = create text for custom menu, left=$Text, right=$tmpShield
add custom menu item to array $Mainmenu.Menu: text=$Menutext returnvalue=null

* Text 105: Hülle:
$Text = read text: page=$PageID id=105
$Menutext = create text for custom menu, left=$Text, right=$tmpHull
add custom menu item to array $Mainmenu.Menu: text=$Menutext returnvalue=null


* Neuer Bereich - "Zurück/Abbruch":
* ---------------------------------
* Text 1000: Zurück/Abbruch
$Menutext = read text: page=$PageID id=1000
add custom menu heading to array $Mainmenu.Menu: title=$Menutext

* Text 1001: Script Tutorial Menü beenden
$Menutext = read text: page=$PageID id=1001
add custom menu item to array $Mainmenu.Menu: text=$Menutext returnvalue='endall'


* Beschreibungstext:
* ------------------
$tmpDescription = read text: page=$PageID id=1


* Titel:
* ------
$tmpTitle = read text: page=$PageID id=1


* Anzeige des Menüs:
* ------------------
$anMenu = open custom menu: title=$tmpTitle description=$tmpDescription option array=$Mainmenu.Menu 
Das Menü ist nun soweit fertiggestellt.

Einen Punkt gäbe es noch zu erledigen, der allerdings nichts mit dem Menü an sich zu tun hat.
Da das Script nur dann ausgeführt werden soll, wenn der Spieler ein Schiff im Ziel hat muss zu Beginn des Scripts eine Abfrage erfolgen, ob ein Schiff gewählt ist.
Ist dies nicht der Fall wird das Script abgebrochen.

Die Überprüfung hierzu sieht folgendermaßen aus:

Code: Select all

* Ermitteln der Daten:
* --------------------
* Ziel des Spielers:
$tmpTarget = get player tracking aim
* Überprüfung, ob der Spieler ein Schiff selektiert hat:
if not $tmpTarget -> is of class {Schiff}
  * In diesem Fall wurde kein Schiff selektiert und das Script wird beendet:
  return null
end 
Dieser letzte Codeabschnitt des Beispielmenüs wurde nur der Vollständigkeit halber angeführt.

[ external image ]

Refresh/Reload von Menüs

Entgegen der Architektur moderner Programmiersysteme ist es mittels eines Scripts nicht möglich einen automatischen refresh/reload eines angezeigten Menüs zu erzwingen.
Daher ist ein "automatisches" Reagieren auf Userinteraktionen nicht möglich!

"Dynamische" Menüs müssen aufwändig auscodiert werden. Es gibt keine Events oder Methoden, die ein automatisches Neuladen der Menüs ermöglichen.

Auf jede einzelne Usereingabe muss entsprechend reagiert werden und Änderungen des Menüs, auf Grund von Usereingaben, erzwingen ein erneutes Erzeugen des kompletten Menüs!

Dieser Umstand ist zwar nicht wirklich schlimm, aber er erfordert leider teilweise einen deutlichen Mehraufwand an Programmierlogik.

[ external image ]

Beispielmenü 2

Nachdem nun die ersten Grundlagen der Menüs erklärt wurden möchte ich einige "fortgeschrittene" Techniken der Menüs erläutern.
Hierzu zählen die "value selections", die Verarbeitung von Rückgabewerten und das Erzeugen von Usereingaben.

IN BEARBEITUNG
Last edited by Reflexer on Tue, 27. Nov 12, 12:37, edited 32 times in total.
User avatar
Reflexer
Posts: 863
Joined: Sat, 21. Feb 09, 12:54
x4

Post by Reflexer »

Sprach- und Logdateien

[ external image ]

Sprachdatei (t-File)

Beschreibung folgt.

[ external image ]

Logdatei

Beschreibung folgt.
Last edited by Reflexer on Tue, 27. Nov 12, 12:31, edited 2 times in total.
User avatar
Reflexer
Posts: 863
Joined: Sat, 21. Feb 09, 12:54
x4

Post by Reflexer »

Scriptaufrufe

In X gibt es unterschiedliche Möglichkeiten Scripte aufzurufen (zu starten).


IN BEARBEITUNG



Alle Scriptaufruf-Befehle befinden sich im Bereich "General Commands" -> "Script Calls" der Befehls-Bibliothek.
Link zum MSCI

[ external image ]

Aufrufparameter und Rückgabewerte

Beschreibung folgt.

[ external image ]

Endlosscripte

Ein besonderes Augenmerk möchte ich auf Endlosscripte lenken.
In der Regel werden Scripte so programmiert, dass sie etwas tun und sich dann beenden.
Es gibt allerdings auch Situationen, in denen ein Script endlos laufen soll.
Diese Scripte können sowohl globale, als auch lokale Scripte sein.

Beispiel eines endlos laufenden Scripts auf einer Station wäre z.B. das Auffüllen der "Waren" in Schiffswerften.
In diesem Script wird endlos alle x Sekunden geprüft, ob noch kaufbare Schiffe in der Werft vorhanden sind.
Ist dies nicht der Fall, wird die Ware aufgefüllt.

Endlos laufende Scripte sollten immer einen "Kill"-Schalter haben. Vor allem global laufende Scripte.
Global laufende Endlosscripte können ohne einen solchen Schalter nie wieder beendet werden, da nicht ermittelt werden kann "wo" sie laufen.
Bei Start eines globalen Endlosscripts wird dieses "irgendwo" ausgeführt.
Es gibt keine ermittelbare Task-ID und auch sonst keinen Weg an das Script ranzukommen!

Meiner Meinung nach ist der einfachste Weg einen "Kill"-Schalter einzubauen beim Scriptstart eine gV mit der Spielzeit anzulegen.

Ich möchte das Ganze an einem Beispiel verdeutlichen:

Code: Select all

* ********************************************************************************************************
* ********************************************************************************************************
* ********************************************************************************************************
* Da es sich um ein Endlosscript handelt, wird beim ersten Start die Spielzeit in einer gV
* gespeichert. Bei jedem Durchlauf wird geprüft, ob die gV noch identisch ist. Wurde das Script
* erneut gestartet stimmen die Werte nicht überein und das Script beendet sich automatisch selbst.
* Ebenso, wenn von außen der Wert null gesetzt wird.

$anStartTime = playing time
set global variable: name='gV.aos.defence.global.starttime' value=$anStartTime
Bei jedem Durchlauf des Scripts wird geprüft, ob sich der Wert verändert hat:

Code: Select all

Loop:
$tmpStartTimegV = get global variable: name='gV.aos.defence.global.starttime'
if $tmpStartTimegV != $anStartTime
  * Wenn die Daten nicht übereistimmen wird das Script beendet:
  goto label End
end
Dies ist meiner Meinung nach die eleganteste Methode um ein Endlosscript beendbar zu machen.
Wird das Script neu (erneut) gestartet, wird die neue Version eine neue Startzeit eintragen.
Eine laufende, ältere Version des Scripts wird beim nächsten Durchlauf feststellen, dass die Zeiten nicht mehr übereinstimmen und sich selbst beenden.
Soll das Script komplett beendet werden muss lediglich die gV (von "außen") auf null gesetzt werden und das Script beendet sich beim nächsten Durchlauf selbst.

Analog funktioniert diese Art des "Kill"-Schalters auch bei lokalen Endlosscripten.
Hierbei wird die Startzeit einfach als lokale Variable auf dem jeweiligen Objekt gespeichert.

Code: Select all

$anStartTime = playing time
[THIS] -> set local variable: name='lV.xyz-script.starttime' value=$anStartTime

[ external image ]

Das Setup-Scrtipt

Beschreibung folgt.

[ external image ]
Last edited by Reflexer on Wed, 28. Nov 12, 02:40, edited 14 times in total.
User avatar
Reflexer
Posts: 863
Joined: Sat, 21. Feb 09, 12:54
x4

Post by Reflexer »

Versionierung und automatische Scriptupdates
Last edited by Reflexer on Sun, 25. Nov 12, 13:23, edited 1 time in total.
User avatar
Reflexer
Posts: 863
Joined: Sat, 21. Feb 09, 12:54
x4

Post by Reflexer »

Signale, Kommandos und Events
Last edited by Reflexer on Sun, 25. Nov 12, 14:41, edited 2 times in total.
User avatar
Reflexer
Posts: 863
Joined: Sat, 21. Feb 09, 12:54
x4

Post by Reflexer »

Sonstiges

Dieser Bereich des Tutorials umfasst all jene Dinge, die nicht eindeutig thematisch zugeordnet werden können.

[ external image ]

Kommentare

Einige werden sich vermutlich fragen, warum ich den Kommentaren einen eigenen Abschnitt spendiere.

Auf Grund meiner längjärigen Erfahrung als Programmierer möchte ich deutlich auf die Wichtigkeit von Kommentaren hinweisen!!
Je mehr ihr kommentiert, desto einfacher wird es für Andere euren Code zu verstehen.
Kommentare sind ebenfalls für euch selbst sehr wichtig. Die Art und Weise wie man programmiert ändert sich im Laufe der Zeit bzw. entwickelt sich.
Wenn ihr euren eigenen Code Monate oder Jahre später lest, werdet ihr euch öfters fragen "warum habe ich das eigentlich so gemacht?" oder "was genau passiert hier?".

Um euch die erneute "Einarbeitung" in euren eigenen Code zu vereinfachen sind möglichst viele sinnvolle Kommentare zu machen.

Wenn ich nun den SOM als Beispiel nehme, so hat dieser über 20.000 Zeilen Code. Niemand kann sich das alles merken. Und das ist nur eines von vielen meiner Projekte.
Ich hab ein den vergangenen 25 Jahren sicherlich mehrere Millionen Zeilen Sourcecode in verschiedenen Sprachen geschrieben.
Ohne Kommentare wäre ich bei meinen eigenen Projekten also aufgeschmissen. Und nun stellt euch vor ein anderer Scripter wollte versuchen euren Code zu verstehen. Ohne Kommentare -> no chance!

[ external image ]

Hotkey

Beschreibung folgt.

[ external image ]

Ship Command

Beschreibung folgt.
Last edited by Reflexer on Tue, 27. Nov 12, 13:33, edited 6 times in total.
User avatar
Reflexer
Posts: 863
Joined: Sat, 21. Feb 09, 12:54
x4

Post by Reflexer »

Beispielprojekt
Last edited by Reflexer on Sun, 25. Nov 12, 20:39, edited 1 time in total.
User avatar
Reflexer
Posts: 863
Joined: Sat, 21. Feb 09, 12:54
x4

Post by Reflexer »

**** reserved****
Einheit 101
Posts: 660
Joined: Sat, 3. Jan 09, 22:57
x3tc

Post by Einheit 101 »

Gefällt mir!
Viel erfolg! Mit Scripts von X3 hab ich mich nur so weit abgegeben, wie es unbedingt sein musste. Ein graus war das, aber mittlerweile funktioniert alles weitgehend :D
Das hier wird sicher von großem Nutzen sein.
User avatar
Reflexer
Posts: 863
Joined: Sat, 21. Feb 09, 12:54
x4

Post by Reflexer »

Hey Leute,

nachdem ich gestern die ersten Inhalte eingefügt habe, hätte ich gerne ein kleines Feedback.

Geht das Tutorial in die richtige Richtung? Sind die Erklärungen und Beispiele ausreichend? Ist es zu oberflächlich, oder zu detailliert? Ich kann das sehr schwer einschätzen und der Spagat den Neuling nicht zu erschlagen und für den Fortgeschrittenen nicht zu langatmig zu sein ist relativ schwierig.

Die Beschreibungen und Erklärungen sind noch nicht vollständig und an der einen oder anderen Stelle noch nicht 100%ig rund, da es erst der erste Wurf ist. Es wird also noch dran gefeilt ;)

Der Startpost ist selbstverständlich noch weit entfernt von seiner Fertigstellung ;)

Es wäre trotzdem super wenn ein paar Leute mal kurz drüberfliegen könnten um mir ein grundsätzliches Feedback zu geben, ob es in die richtige Richtung geht.

Danke
Reflexer
South.Side
Posts: 66
Joined: Fri, 9. Dec 11, 15:43
x4

Post by South.Side »

moin,

also für mich ist das ganze durchaus nachvollziehbar und verständlich - genau richtig würde ich sagen und mit scripten hab ich selbst ja nicht wirklich was am hut. aber jeder der den script editor schon mal aktiviert hat und sich ein paar der scripte angeschaut hat, wird damit was anfangen können.

vielen dank für deinen einsatz!

grüße
south
User avatar
Killjaeden
Posts: 5366
Joined: Sun, 3. Sep 06, 18:19
x3tc

Post by Killjaeden »

Find ich ne gute Sache, hab aber noch keine Zeit es genauer durchzulesen. Ein Hinweis auf die MSCI Referenz habe ich aber noch nicht gesehen (oder übersehen) - das wäre warscheinlich noch ganz hilfreich.
[ external image ]
X-Tended TC Mod Team Veteran.
Modeller of X3AP Split Acinonyx, Split Drake, Argon Lotan, Teladi Tern. My current work:
Image
User avatar
Reflexer
Posts: 863
Joined: Sat, 21. Feb 09, 12:54
x4

Post by Reflexer »

die msci-links zu allen themen mache ich wenn der erste wurf komplett ist. das jeweils rauszusuchen wird einige zeit in anspruch nehmen.

ich hatte es aber bereits auf der todo-liste
User avatar
Killjaeden
Posts: 5366
Joined: Sun, 3. Sep 06, 18:19
x3tc

Post by Killjaeden »

zu allen themen? Ich würde es eher bei einem eigenständigen Unterpunkt belassen, der eklärt das dort viele Befehle erklärt sind mit kleinem Beispiel. Jeden Befehl da extra zu verlinken halte ich für unsinnig.
[ external image ]
X-Tended TC Mod Team Veteran.
Modeller of X3AP Split Acinonyx, Split Drake, Argon Lotan, Teladi Tern. My current work:
Image
UniTrader
Moderator (Script&Mod)
Moderator (Script&Mod)
Posts: 14571
Joined: Sun, 20. Nov 05, 22:45
x4

Post by UniTrader »

zum Teil über Arrays solltest du evtl. die Eigenheit von Pointern etwas besser hervorheben, z.B. anhand eines kleinen Code-Beispiels, das nicht funktioniert wie erwartet, denn sowas kommt ziemlich häufig als fehler vor ^^

Code: Select all

$Array2D=array alloc: size=0
$tmpArray = create new array, arguments='00', '01', '02', '03', '04'
append $tmpArray to array $Array2D 
$tmpArray[0] = '10'
$tmpArray[0] = '11'
$tmpArray[0] = '12'
$tmpArray[0] = '13'
$tmpArray[0] = '14'
$WasKommtHierRaus = $Array2D[0][1]
*NICHT wie erwartet '01'
Spoiler
Show
sondern '11', da das Array beim Abfragen folgende Werte enthält (der übersicht halber auf mehrere Zeilen verteilt):
ARRAY(
ARRAY( '10' , '11' , '12' , '13' , '14' ) ,
ARRAY( '10' , '11' , '12' , '13' , '14' )
)

um genau zu sein handelt es sich beim 2. und 3. ARRAY um DASSELBE ARRAY, sprich wenn man
$ARRAY2D[1][2] = '44'
ausführt bekomt man jetzt sowohl für $ARRAY2D[0][2] als auch [1][2]44 raus. korrekt funktionieren würde der Code so:
$Array2D=array alloc: size=0
$tmpArray = create new array, arguments='00', '01', '02', '03', '04'
append $tmpArray to array $Array2D
$tmpArray=array alloc: size=5
$tmpArray[0] = '10'
$tmpArray[0] = '11'
$tmpArray[0] = '12'
$tmpArray[0] = '13'
$tmpArray[0] = '14'
$WasKommtHierRaus = $Array2D[0][1]
*nun wie erwartet '01' ;)
(nach nochmaligem Lesen bin ich zu dem schluss gekommen dass das ein wenig zu lang geworden ist, aber besser kann ich es nicht erklären……



zum Thema Menüs: man kann meines Wissens Menüs durchaus dynamisch machen - es sind schließlich einfach nur Arrays und Arrays sind Pointer (s.o.) ;) START:e einfach nen zweites Script, welches das Menü ändert und übergib ihm das Menü-Array bevor du das Menü öffnest ;) (gibt noch ein paar Möglichkeiten mehr, aber das ist die simpelste ^^) - bedarf näher auf diese fortgeschrittene Möglichkeit einzugehen sehe ich nicht, aber zumindest solltest du erwähnen dass die Möglichkeit existiert ;)
User avatar
Reflexer
Posts: 863
Joined: Sat, 21. Feb 09, 12:54
x4

Post by Reflexer »

UniTrader wrote:zum Teil über Arrays solltest du evtl. die Eigenheit von Pointern etwas besser hervorheben, z.B. anhand eines kleinen Code-Beispiels, das nicht funktioniert wie erwartet, denn sowas kommt ziemlich häufig als fehler vor
*hust*

lasst mich doch erstmal meinen ersten Wurf des Tutorials fertig machen ;) aktuell ist kein Bereich soweit fertig, dass ich sagen würde "das lass ich jetzt erstmal so" ;) dies trifft auch für den array-bereich zu ;)

trotzdem danke, dass du dir die mühe gemacht hast - zumindest liest es überhaupt jemand :D

beim punkt der menüs möchte ich dir widersprechen. einen work-around für alles mögliche zu finden geht meistens... meine aussage war, dass der refresh des menüs nicht wie bei modernen programmiersprachen funktioniert... und ich denke dem wird wohl jeder zustimmen....

ob ich noch einen bereich "best practises" aufnehme, in den sowas reinkommt, weiß ich noch nicht.... mal sehen

Return to “X³: Terran Conflict / Albion Prelude - Scripts und Modding”