Oblivion:CS-Terminologie (Anregungen)

Aus Skript-Wiki
Wechseln zu: Navigation, Suche

Modules (Module), Records (Einträge), FormIDs

Mit dem TESCS zu modden bedeutet, Modul-Dateien zu editieren (i.e. esp- und esm-Dateien). Es gibt weitere Arten, zu „modden“ (erstellen/editieren von Resourcen -- Mesh, Textur, Sound, Sprache --; ini- und xml-Dateien usw.), aber hier werden wir nur über das Editieren von Modul-Dateien sprechen.

Eine Modul-Datei ist eine Sammlung von Records. Verschiedene Record-Typen definieren verschiedene Dinge: Sounds, Rassen, plazierte Objekte („References“), Leveled Lists, etc. Während verschiedene Record-Typen verschiedene Felder haben (z.B. Gewicht eines Pfeils, x-Position eines plazierten Objekts, Sound-ID von einer sich öffnenden Tür), sind sie auf unterster Ebene alle eins -- ein Record.

Alle Records haben eine einzigartige (unique) FormID. Viele (aber nicht alle) Records haben auch EditorIDs- FormIDs sind achtstellige hexadezimal Nummern (z.B. 00030FDC). EditorIDs sind kurze Text-Strings (nur Buchstaben und Zahlen, keine Leer- oder Sonderzeichen). Für Records, die im Objekt-Fenster des CS aufgelistet sind, ist die EditorID in der ersten Spalte und die FormID in der zweiten (per Default ist diese Spalte nicht breit genug, um den Inhalt zu sehen -- du musst den Spaltentrenner nach rechts ziehen, um die FormID zu sehen). Das Gleiche gilt für die beiden Teile im Cell-View-Fenster: der rechte Teil listet die plazierten Objekte auf die gleiche Weise -- mit einem Unterschied -- wenn einer bestimmten Referenz keine EditorID zugewiesen wurde, wird die EditorID des Basisobjekts dieser Referenz angezeigt.


Objekt vs. Objekt

„'Dieser Abschnitt kann sehr verwirrend sein. Du solltest ihn vielleicht überspringen. Der Hauptpunkt ist, dass ich beim Sprechen übers Modden hauptsächlich über "Records" und nicht über Objekte spreche. Meistens. Wie auch immer, es geht los...'“

Es gibt ein großes Problem, wenn man beim Sprechen übers Modden das Wort Objekt nutzt. Das Problem ist, dass es verschiedene völlig offensichtliche, aber untereinander unvereinbare Arten gibt, dieses Wort zu verwenden. Ich werde diese hier ansprechen und versuchen, die Konflikte zu lösen -- zum Teil, indem ich die Standardterminologie missachte.

Beim Spielen tendiert man dazu, an „Objekte“ in der Spielwelt zu denken -- eine Mauer, ein Fels, eine Kreatur, ein Dolch, den man in einer Truhe findet. Wenn man mit dem Render-Fenster des CS arbeitet, tendiert man dazu, auf ganz ähnliche Art an „Objekte“ zu denken, außer dass man wahrscheinlich Nebelbänke, Kollisionszonen etc. auch als „Objekte“ betrachtet. Unglücklicherweise widerspricht dem das CS, indem es diese Dinge „Referenzen“ nennt und „Objekte“ als Dinge definiert, die im „Objekt-Fenster“ auftauchen. Aber aus einer weiteren Sicht, der eines Programmieres, denkt man bei einem „Objekt“ an eine abstrakte Verkettung von Daten -- z.B. wäre die interne Repräsentierung von all diesen Records (Klassen, Faktionen, Basis-Objekte und Referenzen) ein „Objekt“.

Also versuche ich, das Wort „Objekt“ hier so selten wie möglich zu verwenden. Wenn ich es nutze, dann gewöhnlich aus der Sicht eine Spielers -- wie die Dinge in der Spielwelt gesehen und gehandhabt werden. Beim Modden, wenn ich über Datenverkettungen reden werde, sage ich „Records“. Und immer wenn ich über einen bestimmten Type eines Records reden werde (Zelle, Faktion, Wafe, Referenz usw.), kann dies als Abkürzung für „Zell-Rekord“, „Faktion-Rekord“ usw. gesehen werden.

Dem Folgend werde ich mit weiteren existierenden CS-Terminologien brechen:

  • Anstelle „base object“ werde ich „Basis-Rekord“ sagen.
  • Anstelle „reference variable“ werde ich „Rekord-Variable“ sagen.


Referenzen

Eine Referenz basiert auf einem Basis-Rekord (aka „base objects“ im CS). Der Basis-Rekord für eine Referenz ist der Prototype für diese Referenz. Einige Basen können viele Referenzen haben (z.B. Standard-Container), einige haben nur eine (z.B. die meisten Nicht-Kampf-NPCs).

Die meisten Records im Spiel sind Referenzen (dank drüber nach: hunderte Zellen, mit Mauern, Bäumen, Kreaturen etc. -- es addiert sich alles). Auch wenn man jeder dieser Referenzen eine EditorID zuweisen könnte, wird dies zum Glück nicht getan. Daher haben die meisten Records der oblivion.esm keine EditorID.

Neben den Referenzen gibt es eine weitere Gruppe an Records, die keine EditorID haben -- und das sind Basis-Records, die im Spiel erstellt werden. EditorIDs sind wirklich nur nützlich für menschliche Modder, nicht für das CS oder die Spielengine, beide arbeiten mit den FormIDs. Da die Records für Alchemie-Tränke, Zauber, verzauberte Rüstungen und Waffen, die im Spiel erstellt werden, nicht von Menschen angesehen werden, wird ihnen keine Editor ID zugewiesen.

Referenzen können auch „parent“-(Eltern-)-Referenzen haben. Parent-Referenzen werden aus zwei Gründen verwendet: 1) die Verkettung schaltet Stati zwischen den Referenzen an/aus (das Erscheinen von Oblivion-Toren wird durch solche Ketten kontrolliert) und 2) das Verlinken von Referenzen auf eine Art, die Skripts verwenden können (Fallen nutzen das -- eine Trigger-Seil-Referenz könnte als Parent einen fallenden Baumstamm haben -- das Skript für das Seil reagiert auf einen Actor, der damit kollidiert, indem es die Parent-Referenz triggert). Man beachte, dass für das An-/Ausschalten die Parent-Ref tatsächlich als „Parent“ reagiert, aber bei Verwendung in einer Falle oder einer ähnlichen Situation ist der Vorgang andersherum -- das „child“ (Kind) kontrolliert das „parent“.


Base Records (Basis-Records)

Basis-Records dienen auch als „Basis“ für Gegenstände in Containern. Solche Gegenstände in der Spielwelt können komplex sein -- manchmal haben/sind sie Referenzen, aber meistens nicht. Mehr dazu weiter unten.

Denk dran, dass nicht alle Records, die im „Objekt-Fenster“ gelistet werden, Basis-Records sind. Speziell Texturen,- Sounds-, Wasser-Records, die (meistens) von anderen Records verwendet werden, dienen selbst nie als Basis. Es gibt wirklich keinen Unterschied zwischen diesem Record-Typ und dem andern (Zellen, Regionen, Rassen usw.), die nur über Menüs editiert werden. Warum sind sie also im „Objekt-Fenster“? Es gibt keinen guten Grund. Wahrscheinlich teilweise historisch von früheren Spielen bedingt und teilweise persönliche Verlieben von Programmierern und Moddern bei Bethsoft.


Vs. Programmier-Terminologie

Zwischenspiel für verwirrte Programmierer...

Programmierer werden die CS-Terminologie wahrscheinlich teilweise verwirrend finden. Der Nutzen des CS von „Objekt“, „parent“ und „Referenz“ hat meist gar nichts mit der Bedeutung dieser Wörter bei der Programmierung zu tun.

  • beim Programmieren ist eine Referenz hauptsächlich ein Zeiger auf eine andere Datenstruktur, aber im CS ist die „Referenz“ eine komplexe Datenstruktur -- mehr ein „Objekt“ als eine „Referenz“. Die FormID des CS kommt einer Programmier-Referenz noch am nahesten.
  • beim Programmieren ist eine Klasse eine Definition eines Typs oder einer Datenstruktur, die angelegt werden kann. Am ehesten ist ein Basis-„Objekt“ im CS eine Klasse.
  • beim Programmieren ist ein Objekt typischerweise eine angelegte Klasse. Im CS ist das am ehesten ... eine „Referenz“!
  • beim Programmieren ist ein „Parent“ einer Instanz normalerweise die Klasse, auf der die Instanz basiert. Aber im CS ist das „Parent“ einer Referenz eine weitere Referenz -- eher eine Verknüpfung zu einer vorherigen Instanz in einer einfach verketteten Liste.

Also Programmierer, nehmt einfach euer CS-Wissen über Objekte, Referenzen und Parents und haltet euch vorerst daran! Jetzt aber zurück...


Dynamischer Inhalt

Dynamische Records und Gegenstände sind Records und Gegenstände, die während dem Spielverlauf generiert werden. In Vanilla-Oblivion sind das meist dynamische Referenzen (gespawnte Kreaturen) und dynamische Gegenstände (Objekte aus Containern und Actor-Inventaren). Zusätzlich gibt es PC-erstellte Zauber, Tränke und verzauberte Gegenstände sowie ein paar spezielle Records wie der NPC-basierte Record für die Spielerstatue in Kvatch. Davon abgesehen kann OBSE eine Vielzahl dynamischer Basis-Objekte erstellen.

Beispiel: wenn Actors an einem Spawn-Punkt spawnen, wird jeder Actor eine dynamische Referenz sein. Ihre Rüstung, Waffen, Inventar und Gegenstände beim Tod werden dynamische Gegenstände sein.

Beispiel: wenn der Spieler eine verzauberte Rüstung an einem Verzauberungsaltar erstellt, werden zwei dynamische Records erstellt -- einer für die Verzauberung und einer für den Basis-Record der Rüstung. Zusätzlich gibt es einen einzelnen Instanz-Gegenstand, der in das Inventar des Spielers gelegt wird.

Dynamische Records (Referenzen, Verzauber-Records, Zauber-Records usw.) „gehören“ zum Savegame und nicht zu einer Mod und somit starten ihre FormIDs immer mit einem ‚FF’. Wenn man sich ingame die FormID eines Records anschaut und sie mit FF anfängt, ist ein ein dynamischer Record.


Dynamische Gegenstände

Wie vorher erwähnt, sind dynamische Gegenstände nicht permanent mit Referenzen verbunden. Stattdessen haben sie „keine“ Referenz, wenn sie im Inventar liegen und werden jedes Mal einem neuen Record dynamisch zugewiesen, wenn sie in die Welt fallen gelassen werden. Noch einmal: das steht im Kontrast zu nicht dynamischen Gegenständen -- z.B. Gegenstände, die direkt durch eine Mod in die Welt gesetzt werden. Nicht dynamische Gegenstände werden immer durch ihr Original repräsentiert, einer mod-definierten Referenz, unabhängig ihrer Bewegung im Inventar, Containern oder Zellen.

Was geschieht als mit der temporären Referenz eines dynamischen Gegenstands, nachdem man ihn aufgehoben hat? Meistens wird er direkt aus dem Savegame gelöscht. Und tatsächlich (nach dem letzten Patch 1.2.0416) wird die FormID dieser dynamischen Referenz wiederverwendet. Wenn du also in einer isolierten Zelle bist, einen Eisendolch fallen lässt, seine FormID überprüfst, ihn wieder aufhebst und dann ein Eisenschild fallen lässt, wird der dynamischen Referenz höchstwahrscheinlich die FormID zugewiesen, die zuvor durch das Aufheben des Eisendolchs frei wurde.

Jedoch werden diese Referenzen unter bestimmten Umständen „nicht“ vom Savegame entfernt (das Nicht-Entfernen scheint mit Skript-Record-Variablen verbunden zu sein, die entweder mit der Referenz verbunden sind und/oder auf diese zeigen). In dem Fall existiert die Referenz weiterhin im Savegame, ist aber ingame nicht sichtbar und ist nicht länger mit dem Gegenstand, der jetzt im Inventar liegt, verbunden. Wenn der Gegenstand wieder fallen gelassen wird, wird er nicht mehr mit dieser alten Referenz verbunden, sondern ihm wird eine neue dynamische Referenz zugewiesen -- was dann aufs gleiche Problem hinausläuft. Anscheinend werden diese vergessenen Referenzen „nicht“ durch den üblichen Drei-Tages-Respawn-Aufräum-Prozess aus dem Savegame entfernt. Dadurch kann das Savegame, wenn es viele dieser Referenzen gibt, sehr groß werden. Daher werden diese als „bload references“ (aufgeblähte Referenzen) bezeichnet.

Beim Verstehen, wie dynamische Gegenstände zwischen Zellen und Containern und zwischen verschiedenen Containern bewegt werden, sollte man wissen, dass diese nicht wirklich verschoben, sondern kopiert werden. Das ursprüngliche Objekt wird in den neuen Container/die neue Zelle kopiert, zusammen mit seinen Statusinformationen (Rüstung/Waffe, Gesundheit, Verzauberungsladen, lokale SkriptVariablen) und das Original wird gelöscht (oder bei Bload-Referenzen als destroyed (zerstört) markiert).

Dieses Kopieren wird beim Fallenlassen mehrerer Instanzen identischer einfacher Gegenstände offensichtlich (z.B. bei Pfeilen oder unbeschädigten Eisendolchen). Wenn du mehrere solcher einfachen Gegenstände (z.B. 10 Eisenpfeile) fallen lässt, werden sie nicht als 10 separate Pfeile fallen, sondern als einzelner, gestapelter Pfeil (Beim Mouseover erscheint ein „Eisenpfeil (10)“). In dem Fall behandelt das Spiel die 10 Pfeile nicht als 10 verschiedene Pfeile, sondern als 1 Pfeil x 10 (beachte, dass dies nur für einfache, dynamische Gegenstände gültig ist).

Zu einem gewissen Grad ist also die fortlaufende Existenz eines Gegenstands reine Einbildung -- was der Spieler als einzelnen Dolch, der aus einer Kiste herausgenommen und dann in eine andere gelegt wird, wahrnimmt, ist vielmehr eine Abfolge kopierter Objekte. Da dieser Kopier-/Löschvorgang den Status exakt kopiert, kann man aber auch davon sprechen, dass die Gegenstände kontinuierlich existieren, auch wenn ihre unterliegende Repräsentation kommt und geht.


Scripting-Terminologie

Wenn es ums Skripten geht, hat die vorhergehende Diskussion zwei Hauptbedeutungen:

  • Das Verständnis, welche Record-Typen verschiedene Skriptfunktionen erfordern.
  • Das Verständnis von Record-Variablen und was in sie getan werden kann.

Record-Variablen beinhalten Records, oder genauer, sie halten die „FormIDs“ von Records. Da eine FormID genau nur einem Record zugeordnet ist, kann die kurze numerische FormID bei Funktionsaufrufen für den tatsächlichen Record stehen. Du kannst dies in einem Skript durch Verwendung eines Aufrufs wie „message „targetRef value: %X“ targetRef“ prüfen. Das wird die FormID ausgeben, die in targetRef gespeichert ist. Beim Sprechen über Funktionen vernachlässigen wir diesen Punkt gewöhnlich jedoch und sagen anstelle von z.B. „getSelt gibt die FormID der aktuellen Referenz zurück“ eher „getSelf gibt die aktuelle Referenz zurück“. Das Letztere ist leichter zu sagen und impliziert immer das Erstere.

Du wirst oft Record-Variablen sehen, die als „Referenz-Variablen“ bezeichnet werden. Das kommt daher, dass Funktionen in einem nicht OBSE-Oblivion, die Records zurückgeben, „nur“ Referenz-Records zurückgeben. Also obwohl Record-Variablen natürlich Nicht-Referenzen halten könnten, wird nie etwas anderes als eine Referenz darin gespeichert (daher werden Record-Variablen auch mit dem Schlüsselwort ‚ref’ und nicht mit ‚rec’ deklariert). Da OBSE ermöglicht, die volle Bandbreite an Record-Typen darin zu speichern, denken wir ab jetzt am Besten an „Record-Variablen“ (unglücklicherweise sind wir immer noch an die ‚ref’-Syntax für die Deklaration gebunden).

Beim Spezifizieren von Records in Skripts sollte man entweder eine Record-Variable oder eine EditorID nutzen. Beim Kompilieren eines Skripts wird die EditorID in die dazugehörige FormID konvertiert, die die Gameengine dann beim Ausführen des Skripts verwendet. Man beachte, dass es zwar oft möglich ist, die FormID anstelle der EditorID zunutzen, dies aber nicht wünschenswert ist, da: a) es schwerer zu lesen ist und b) wenn je ein zusätzlicher Master hinzugefügt wird, wird die angegebene FormID höchstwahrscheinlich ungültig (man beachte, dass die kompilierte FormID automatisch an ihren neuen Wert angepasst wird, wenn ein Master hinzugefügt wird).

Nun, zurück zur verwirrenden Terminologie. In diesem Wiki werden verschiedene Terminologien an verschiedenen Stellen verwendet. Unser Verständnis ist mit der Zeit gewachsen und OBSE hat grundlegend verändert, wie Records sind und in Skripts verwendet werden können.

Dahingehend werden jetzt die folgenden Änderungen der Praxis vorgeschlagen:

  • In Skripts sollten Record-Variablen so benannt werden, dass sie ihrem Nutzen im Skript entsprechen und „nicht“ ihrer „id“. Ref und/oder base sollten, wenn erforderlich, verwendet werden, um die Natur der Variable zu kennzeichnen. Die Funktionsbeschreibungen sollten jedoch immer anzeigen, ob das Argument eine ref oder eine base benötigt.
  • Wenn die Funktion entweder eine base oder eine ref akzeptiert, nutze bor (ich bin mir noch nicht ganz sicher, ob das eine solche Funktion ist -- die Hinweise, die ich über OBSE-Funktionen sah, schienen zu sagen, dass wenn die base spezifiziert ist, muss sie an anderer Stelle sein wie ein Referenz-Argument).
  • Container als Ref-Variable wird (gewöhnlich/immer?) so verstanden, dass sie für einen Container, NPC und Kreatur-Referenz steht (Ich kenne keine Container-Funktion, die nur für Container funktioniert und nicht bei Kreatur- oder NPC-Referenzen).

Einige Skriptbeispiele: <tesscript>ref companion ref companionCell set companionCell to companion.getParentCell

ref dagger set dagger to player.placeAtMe wrTestDagger 1 100 0 message "Dagger formid: %X" dagger

ref tempRecord set tempRecord to getSelf set tempRecord to tempRecord.getContainer set tempRecord to tempRecord.getParentCell

ref spell set spell to GetPlayerSpell player.Cast spell

ref self ref myBase set self to getSelf set myBase to self.getBaseObject

ref opponent set opponent to getCombatTarget</tesscript>

Bei Funktionen sollten „ref“ (oder „reference“) und „base“ häufiger verwendet werden, um die Natur des Arguments festzulegen.

(opponent) actorRef.getCombatTarget
(value) npcRef.getClothingValue
(bool) npcRef.getIsSex male|female
(actionRef) reference.getActionRef
(bool) [reference.]isFurniture [base]
(bool) [reference.]isLight [base]
(health) [itemRef.]GetObjectHealth [itemBase]

Alternativ könnte man auch den Rückgabewert und den Typ aller Argumente spezifizieren.

(opponent:rec) actorRef:rec.getCombatTarget
(value:short) npcRef:rec.getClothingValue
(bool:short) npcRef:rec.getIsSex male|female
(actionRef:rec) reference:rec.getActionRef
(bool:short) isFurniture base:rec
(bool:short) reference:rec.isFurniture
(bool:short) isLight base:rec
(bool:short) reference:rec.isLight
(health:long) [itemRef:rec.]GetObjectHealth [itemBase:rec]


Scripting-Notizen

Einige Klarstellungen fürs Skripten im Licht der Diskussion von hier.

  • getSelf, angewandt auf einen Mod-basierten Gegenstand, wird immer die Original-FormID des Gegenstands zurückgeben.
  • getSelf auf einen dynamischen Gegenstand wird immer 0 zurückgeben -- sogar, wenn der Gegenstand in einer Zelle ist und eine Referenz hat. Während du vielleicht erwartest, dass die FormID der dynamischen Referenz zurückgegeben wird, wenn es in einer Zelle ist, ist die Funktion so erstellt, dass sie wohl aus Sicherheitsgründen 0 zurückgibt.
  • placeAtMe erstellt eine dynamische Referenz -- „und“ gibt die korrekte FormID zurück (im Gegensatz zu getSelf).


Kurze Wiederholung

  • Record: grundlegende Struktur von Daten
  • Field: Aspekte eines Records (z.B. das Gewicht eines Pfeils, die Reichweite eines Bogens...)
  • Formid: der grundlegende, acht Hexstellen-Bezeichner aller Records
  • EditorID: Der String-Bezeichner, der von vielen (aber nicht allen) Records genutzt wird. * Reference: Ein In-der-Welt-Objekt -- alle Objekte, die in die Welt gesetzt wurden und Objekte in Containern (die meisten Referenzen haben „keine“ EditorIDs, aber alle haben FormIDs).
  • Base Object: Der Record, auf dem eine gegebene Referenz basiert (z.B. „BarrelFoodLow“).
  • Items sind tragbare Objekte, wie der Spieler sie in der Welt wahrnimmt -- sie haben eine wahrgenommene Lebenszeit, die normalerweise größer ist, wie die Lebenszeit der unterliegenden Datenrepräsentation.


Veraltete Begriffe

  • Object: verwirrender Begriff. Verwende wenn möglich einen anderen Begriff. Unglücklicherweise lässt sich das oft schwer vermeiden.
  • True Reference: Unnütz. Nicht verwenden.
  • Hexid: Nutze stattdessen „formid“.