Wenn man ein grösseres Feature untereinander oder in der Modderdatenbank teilen will, ist es meist mit viel Aufwand verbunden, es in eine (modifizierte) Gothic Installation einzubinden. In viele Skripte müssen einige Zeilen hinzugefügt oder geändert werden und das beansprucht Zeit und Arbeit.
FeatShare (Feature+Share) hilft da aus. FeatShare integriert die Skripte an die richtigen Stellen, unabhängig davon, wie weit diese von den originalen abweichen. Das ganze kann man sich als eine Art Merge-Tool vorstellen, allerdings werden nicht zwei komplette Gothic Installationen abgeglichen, sondern lediglich ausgewählte Teile in eine andere Gothic Installation eingepflegt. Dabei ist es auch möglich alle Art von Dateien (inkl. Binärdateien) zu kopieren, zu löschen und zu überschreiben. Natürlich wird vorher von allen zu verändernden Dateien ein Backup angelegt. Es ist keine Überraschung, dass das nicht alles von selbst geht (woher soll FeatShare wissen, in welche Datei ein Feature in welche Zeile eingetragen muss). Die "Arbeit" das festzulegen liegt am Vertreiber der Features.
Mittels regulären Ausdrücken* legt man die möglichst generellsten Anhaltspunkte in Skripten fest und bestimmt so, wo FeatShare die Skriptstücke einfügt. Bei solchen Aufgaben wird man aber nicht allein gelassen. Ich habe einige Hilfstools dazu geschrieben und eine umfangreiche Dokumentation verfasst.
(*) Diese habe ich noch erweitert (geschachtelte Klammerzuordnung, nter Treffer von vorne oder hinten)
Felper.exe ist ein kleines Tool, mit dem man ein bisschen mit regulären Ausdrücken anhand seiner Skripte herumspielen kann, so wie es FeatShare tut.
BuildSetup.exe ist ein Tool, dass die erstellten FeatShare-Konfigurationen und die Features (inkl. jeglicher Binärdateien) in ein Setup (bestehend aus einer Datei) zusammenpackt und mit LZMA komprimiert.
FeatShare.chm ist eine (Compiled HTML Help) Hilfe-Datei mit Referenz, Einleitung und Beispielen.
FeatShare.exe selbst steckt in BuildSetup.exe mit drin und kommt zum Einsatz, wenn jemand das Feature installiert.
In FeatShare.exe stecken noch weitere Tools, wie z.B. ein Diff-Utility-artiges Tool, dass die Änderungen anzeigen kann bevor man die Änderungen übernimmt.
FeatShare hat sehr viele Möglichkeiten zur Anpassung und ist sehr flexibel. Die Einstellungen werden über JSON-Dateien vorgenommen mit denen man mir alles erdenkliche anpassen kann. Das Programm ist sehr generell - also nicht Gothic-spezifisch. Deshalb ist die Dokumentation auf Englisch. Ich weiss nicht genau, ob es im Gothic Modden nützlich sein wird, da es verhältnismässig komplex ist. Es ist eher eine Verlagerung als die Abnahme von Arbeit; um das Einzupflegen kümmert sich der Ersteller anstatt der Empfänger.
Für die Modderdatenbank finde ich es sehr gut, siehe das Zauberpaket.
Für Skriptpakete wie LeGo, die gelegentlich Updates hervorbringen ist es besonders geeignet, da man auch Einfüge-Konditionen ("Ist das Feature schon drin/auf dem neusten Stand?") einbauen kann. Wer interessiert ist, kann sich hier beispielhaft anhand von LeGo anschauen, wie so etwas aussieht: [Source][Setup].
könnte man das programm nicht einfacher gestallten indem man einfach eine .d auswählt.. zb
die meleewaepon.d die dann alle instanzen auflistet welche man ähnlich dem installer an oder abwählen kann.
hat man seine wahl getroffen wird dann automatisch alle passenden daten dazu aus der installation gesaugt?
könnte man das programm nicht einfacher gestallten indem man einfach eine .d auswählt.. zb
die meleewaepon.d die dann alle instanzen auflistet welche man ähnlich dem installer an oder abwählen kann.
hat man seine wahl getroffen wird dann automatisch alle passenden daten dazu aus der installation gesaugt?
quasi ähnlich deinem installer
Das würde sicherlich das Erstellen eines solchen Setups vereinfachen, allerdings sind da viele Probleme mit verbunden.
Das "automatisch aus der Installation saugen" wird zum Problem, sobald es sich nicht um ein normales Feature handelt. Man könnte tatsächlich für alle standardmässigen Erweiterungen eine Automatisierung einbauen. Beispielsweise so.
Neue Waffe: Hole Itemscript, 3ds-Datei, Einträge aus der Text.d, ...
Neue Animation: Hole ASC-Datei, Einträge aus der Humans.mds, ...
Neuer Zauber: Hole Itemscript, Spell script, Einträge aus der Text.d, Einträge aus der Constants.d, ...
Was ist aber nun, wenn mein Zauber auch eine neue Animation beinhaltet, oder Einträge in der AI_Constants.d vornimmt oder komplexere Mechaniken implementiert, die in weiteren Dateien liegen. Das kann so ein SetupErsteller unmöglich wissen und das komplette Tool wird nutzlos bei komplexeren Features, für genau die FeatShare entwickelt wurde.
Ein weiteres Problem, dass ich damit sehe, ist, dass man so möglicherweise die passenden Daten aus der Installation holen könnte, allerdings bleibt immer noch völlig offen wie das Setup diese dann Integrieren soll. Einen solchen regulären Ausdruck zu automatisieren ist schier unmöglich - das muss manuell geschehen.
Nur ein Mensch mit Verständnis von Deadalus weiss, dass
Code:
const int KONSTANTE = 0;
Code:
const INT Konstante /* gut gesetztes Kommentar */ = 2 ; // Noch eins
Code:
CONST int KoNSTanTE = 60 ;
funktional die selben Zeilen sind. Es gibt im Grunde unendlich viele Möglichkeiten diese Zeile zu schreiben und ein herkömmliches Programm kann dafür keinen passenden regulären Ausdruck finden.
FeatShare ist unglaublich mächtig und flexibel, bezahlt dafür aber mit dem Preis ziemlich komplex zu sein.
Eine gute Arbeitsweise neue Features in Gothic zu implementieren, ist möglichst immer eigene Dateien zu erstellen und vermeiden bestehende zu erweitern. In LeGo ist das ziemlich gut gelungen: Zum Installieren von LeGo braucht man nur einen einzigen Eintrag in der Gothic.src vor zu nehmen. Der Rest des Codes ist in eigenen Dateien, die man einfach nur kopieren braucht. Daher eignet sich LeGo extrem gut für FeatShare. Alles was FeatShare tut (siehe die Datei LeGo_2_3_6Install.feat in [Source]), ist alle Dateien zu kopieren und den Eintrag in der Gothic.src vorzunehmen (zusätzlich wird es auch noch in der Startup.d initialisiert).
Wer so arbeitet, hat mit FeatShare nicht mehr so viel Arbeit. FeatShare lohnt sich daher vielleicht kaum für LeGo.
Wenn man sich aber das Zauberpaket anschaut, sieht man, dass einige Sachen nicht ausgelagert werden können (siehe Constants.d, Text.d, Spell_ProcessMana.d, ...). Dort ist FeatShare eine gute Sache.
Am besten schreibe ich noch einmal einen Post, wie man im Gothic Modding FeatShare benutzen kann. Das Tool hier einfach reinzuklatschen und zu sagen "Lest doch die Dokumentation" ist nicht gerade sehr ansprechend. So etwas folgt in Kürze.
Auch könnte ich vielleicht gerade solche Fälle von standardmässigen Erweiterungen, wie ich sie oben genannt habe, einige Templates (bei FeatShare sind das Anchors) erstellen, die man dann leicht verwenden kann, wenn man z.B. einen neuen Zauber in die Modderdatenbank stellen will.
Das Tool klingt gut, aber du hast es ja selber gesagt: LeGo ist so einfach zu installieren, da braucht es wirklich kein Programm für
Die einzige Anwendungsmöglichkeit die ich sehe, ist um zu verhindern, dass bereits vorhandene Änderungen in den LeGo-Dateien verloren gehen.
Allerdings sollten sowieso nur wenige Dateien in LeGo überhaupt vom Nutzer geändert werden (eigentlich nur die Userconst.d und die Focusnames.d, wobei ich letzteres schon länger mal auslagern wollte), insofern ist der Nutzen im Vergleich zum Aufwand auch hier fraglich. Außerdem würde man im Endeffekt auch nur ein VersionControlSystem neu implementieren, das erscheint mir irgendwie unnötig.
Zauber sind ein gutes Beispiel, weil dafür viele kleine Änderungen in verschiedenen Dateien vorgenommen werden müssen. Wir haben also einen Zauber "Manasucht". Der ist also ein Feature, dass wir gern in der Modderdatenbank bereitstellen wollen.
Für so ein Feature erstellen wir eine Feature-Datei. Wie die aussieht ist in der Konfiguration von FeatShare komplett anpassbar, aber wir nehmen jetzt einmal die Defaulteinstellungen. Eine solche Feature-Datei besteht ausschliesslich aus Schlüsselwort-Wert Paaren. Es gibt drei Schlüsselworte, die immer verfügbar sind.
Das einfachste Schlüsselwort ist infoText. Der dazugehörige Wert bestimmt, was im Setup als Beschreibung steht. Das entspricht diesem Screenshot aus dem Einleitungspost.
Dann gibt es noch copyFiles. Damit kann man auflisten, welche Dateien beim Installieren in die Gothic Installation kopiert werden.
Und deleteFiles listet alle Dateien auf, die aus der Gothic Installation gelöscht werden sollen. Das ist nicht häufig notwendig, es sei dann man möchte bereits kompilierte Animationen/Texturen o.ä. löschen.
Nehmen wir unser Beispiel mit dem Manasucht-Zauber, sieht der Anfang der Feature-Datei so aus:
Code:
### infoText ###
Manasucht. Der Spieler tauscht sein Leben gegen Mana.
Dabei kann der Spieler durch gedrückthalten des Zaubers bestimmen wie viel er tauscht.
### END ###
Die Syntax hier ist ziemlich einfach. Ein Schlüsselwort wird durch einschliessende # gekennzeichnet, der dazu gehörige Wert folgt anschliessend bis zum nächsten Schlüsselwort. Schlüsselwörter, die nicht definiert sind werden ignoriert, wie hier END. Das dient dazu um den Wert einzufassen, sonst ist das Schlüsselwort-Wert Paar unvollständig und wird ignoriert. Zu beachten ist hier, dass die Syntax in der Konfiguration von FeatShare geändert werden kann. Das ist wichtig, denn es kann ja sein dass "###" in meinem Code drin vorkommt.
Neben den erwähnten drei Schlüsselwörtern kann man nun in sogenannten Anchors weitere definieren. Ein Anchor enthält alle Informationen wie man einen bestimmten Wert aus einem Feature einpflegen soll (welche Datei, welche Position in der Datei, usw.). Für Zauber könnte man z.B. ein Schlüsselwort "spltext" definieren, dessen Wert von einem Anchor dann in die Text.d eingepflegt werden soll und den Namen/Item-Beschreibung des Zaubers enthält.
Ein Anchor kann von mehreren Features verwendet werden. D.h. wenn ich jetzt einen weiteren Zauber hinzufügen wollte, brauche ich nicht alle Anchors zweimal.
Alle Schlüsselwörter die ich für einen Zauber definiert habe, habe ich hier aufgelistet:
spl:
Die SpellID in Constants.d,
z.B. "Sleep" für SPL_Sleep.
splinst:
Die Spell Instanz in Constants.d (spellFxInstanceNames),
z.B. "Sleep" für Spell_Sleep.
splani:
Die Animationskürzel in Constants.d (spellFxAniLetters),
z.B. "SLE".
spltext:
Die Zauberbeschreibung in Text.d (TXT_SPELLS),
z.B. "Schlaf".
valueItRu:
Runenkaufwert in It_Runen.d (Value_Ru_Light),
z.B. "500".
scriptItRu:
Die Runen-Instanz in It_Scrolls.d,
z.B. "INSTANCE ItRu_Light (C_Item) { ... }".
valueItSc:
Schriftrollenkaufwert in ItRu.d (Value_Sc_Light),
z.B. "10".
scriptItSc:
Die Schriftrollen-Instanz in It_Scrolls.d,
z.B. "instance ItSc_Light (C_Item) { ... }".
scriptPFX:
Die PFX-Instanz in PfxInstMagic.d,
z.B. "INSTANCE MFX_LIGHT_INIT (C_PARTICLEFX) { ... }".
scriptVisFx:
Die VFX-Instanz in VisualFxInst.d,
z.B. "INSTANCE spellFX_Light(CFx_Base_Proto) { ... }".
scriptSFX:
Die SFX-Instanz (falls der Zauber keinen Sound hat, kann man das Schlüsselwort weglassen) in SfxInst.d
processManaRelease:
Wenn dieses Schlüsselwort existiert (Wert egal), wird ein Eintrag in die Spell_Processmana_Release.d vorgenommen.
humansmds:
Die entsprechenden Zeilen in Humans.mds, falls der Zauber neue Animationen hat.
Diese Informationen muss ich mir jetzt zusammensuchen und kann sie dann in eine Feature-Datei mit den Schlüsselwörtern eintragen. In etwa so (ganz am Ende wir auch eine List von zu kopierenden Dateien anglegt):
Spoiler:(zum lesen bitte Text markieren)
Code:
### infoText ###
Manasucht. Der Spieler tauscht sein Leben gegen Mana. Dabei kann der Spieler durch gedrückthalten des Zaubers bestimmen wie viel er tauscht. Ikarus wird nicht benötigt.
### spl ###
ManaForLife
### splinst ###
ManaForLife
### splani ###
SAC
### spltext ###
Manasucht
### valueItRu ###
1500
### scriptItRu ###
/*******************************************************************************************/
INSTANCE ItRu_ManaForLife (C_Item)
{
name = NAME_Rune;
mainflag = ITEM_KAT_RUNE;
flags = 0;
value = Value_Ru_ManaForLife;
visual = "ItRu_ManaForLife.3DS";
material = MAT_STONE;
spell = SPL_ManaForLife;
mag_circle = 3;
wear = WEAR_EFFECT;
effect = "SPELLFX_WEAKGLIMMER_RED";
description = NAME_SPL_ManaForLife;
TEXT [0] = NAME_Mag_Circle;
COUNT [0] = mag_circle;
TEXT [1] = NAME_Spell_Invest;
TEXT [2] = ConcatStrings(PRINT_Beliarshitpoints_MAX, "1");
TEXT [3] = ConcatStrings(PRINT_LearnMANA_MAX,
IntToString(SPL_ManaForLife_RELATION));
TEXT [5] = NAME_Value;
COUNT [5] = value;
};
/*******************************************************************************************/
### valueItSc ###
200
### scriptItSc ###
/*******************************************************************************************/
INSTANCE ItSc_ManaForLife (C_Item)
{
name = NAME_Spruchrolle;
mainflag = ITEM_KAT_RUNE;
flags = ITEM_MULTI;
value = Value_Sc_ManaForLife;
visual = "ItSc_ManaForLife.3DS";
material = MAT_LEATHER;
spell = SPL_ManaForLife;
wear = WEAR_EFFECT;
effect = "SPELLFX_WEAKGLIMMER_RED";
description = NAME_SPL_ManaForLife;
TEXT [0] = Name_MageScroll;
TEXT [1] = NAME_Spell_Invest;
TEXT [2] = ConcatStrings(PRINT_Beliarshitpoints_MAX, "1");
TEXT [3] = ConcatStrings(PRINT_LearnMANA_MAX,
IntToString(SPL_ManaForLife_RELATION));
TEXT [5] = NAME_Value;
COUNT [5] = value;
};
/*******************************************************************************************/
### scriptPFX ###
/// XXXXXXXXXXXXXXXXXXXXXXXXXXXXX ///
/// XX M A N A F O R L I F E XX ///
/// XXXXXXXXXXXXXXXXXXXXXXXXXXXXX ///
/// mud-freak ///
/* MFX_MANAFORLIFE_16BIT_TRIPOLY.TGA ist die gleiche Textur wie
* MFX_MASTEROFDISASTER_AURA_16BIT.TGA, allerdings als Tri-Poly. Damit spar
* ich mir die Option vistexisquadpoly, die doppelt so rechenaufwendig ist. */
INSTANCE MFX_ManaForLife_INIT (C_PARTICLEFX)
{
ppsvalue = 75.000000000;
ppsscalekeys_s = "1";
shptype_s = "POINT";
shpfor_s = "object";
shpoffsetvec_s = "15 0 0";
dirmode_s = "NONE";
dirfor_s = "object";
dirmodetargetfor_s = "OBJECT";
velavg = 0;
lsppartavg = 150.000000000;
flygravity_s = "0 0 0";
flycolldet_b = 0;
visname_s = "MFX_MANAFORLIFE_16BIT_TRIPOLY.TGA";
vistexisquadpoly = 0;
visorientation_s = "NONE";
vistexcolorstart_s = "100 100 255";
vistexcolorend_s = "0 0 250";
vissizestart_s = "10 10";
vissizeendscale = 1;
visalphafunc_s = "ADD";
visalphastart = 255.000000000;
visalphaend = 255.000000000;
trlFadeSpeed = 1;
trltexture_s = "HEAVENLIGHT.TGA";
trlwidth = 1;
useemittersfor = 1;
};
INSTANCE MFX_ManaForLife_INIT2 (C_PARTICLEFX)
{
ppsvalue = 75.000000000;
ppsscalekeys_s = "1";
shptype_s = "POINT";
shpfor_s = "object";
shpoffsetvec_s = "0 15 0";
dirmode_s = "NONE";
dirfor_s = "object";
dirmodetargetfor_s = "OBJECT";
velavg = 0;
lsppartavg = 150.000000000;
flygravity_s = "0 0 0";
flycolldet_b = 0;
visname_s = "MFX_MANAFORLIFE_16BIT_TRIPOLY.TGA";
vistexisquadpoly = 0;
visorientation_s = "NONE";
vistexcolorstart_s = "255 100 100";
vistexcolorend_s = "250 0 0";
vissizestart_s = "10 10";
vissizeendscale = 1;
visalphafunc_s = "ADD";
visalphastart = 255.000000000;
visalphaend = 255.000000000;
trlFadeSpeed = 1;
trltexture_s = "HELLLIGHT.TGA";
trlwidth = 1;
useemittersfor = 1;
};
// Wir bedienen uns dem Inflatezauber und passen den Spritz-Effekt an
INSTANCE MFX_ManaForLife_SPATTER_0 (MFX_INFLATE_FOUNTAIN)
{
ppsscalekeys_s = "2"; // Wir loopen den Effekt, weil er unter-
ppsIsLooping = 1; // schiedlich lang sein kann.
diranglehead = 0; // Spritzwinkel
velavg = 0.200000024; // Spritzstärke
vistexcolorstart_s = "98 0 0"; // In Blutrot
vistexcolorend_s = "191 28 28";
};
INSTANCE MFX_ManaForLife_SPATTER_90 (MFX_INFLATE_FOUNTAIN)
{
ppsscalekeys_s = "2";
ppsIsLooping = 1;
diranglehead = 170;
dirAngleElev = 30;
velavg = 0.200000024;
vistexcolorstart_s = "98 0 0";
vistexcolorend_s = "191 28 28";
};
INSTANCE MFX_ManaForLife_SPATTER_180 (MFX_INFLATE_FOUNTAIN)
{
ppsscalekeys_s = "2";
ppsIsLooping = 1;
diranglehead = 160;
dirAngleElev = -20;
velavg = 0.200000024;
vistexcolorstart_s = "98 0 0";
vistexcolorend_s = "191 28 28";
};
INSTANCE MFX_ManaForLife_RING_LARGE1 (C_PARTICLEFX)
{
ppsvalue = 250.000000000;
ppsscalekeys_s = "1";
shptype_s = "LINE";
shpfor_s = "OBJECT";
shpoffsetvec_s = "70 -30 0";
shpDistribType_S = "WALK";
shpDistribWalkSpeed = 0.0008;
shpDim_S = "100";
dirmode_s = "NONE";
velavg = 0;
lsppartavg = 600.000000000;
flygravity_s = "0 0 0";
flycolldet_b = 0;
visname_s = "MFX_MANAFORLIFE_16BIT_TRIPOLY.TGA";
vistexisquadpoly = 0;
visorientation_s = "NONE";
vistexcolorstart_s = "100 100 255"; // "0 0 255"; // Lichterkettenartefakt
vistexcolorend_s = "0 0 255";
vissizestart_s = "25 25";
vissizeendscale = 2;
visalphafunc_s = "ADD";
visalphastart = 255.000000000;
visalphaend = 255.000000000;
};
INSTANCE MFX_ManaForLife_RING_LARGE2 (C_PARTICLEFX)
{
ppsvalue = 250.000000000;
ppsscalekeys_s = "1";
shptype_s = "LINE";
shpfor_s = "OBJECT";
shpoffsetvec_s = "-70 -30 0";
shpDistribType_S = "WALK";
shpDistribWalkSpeed = 0.0008;
shpDim_S = "100";
dirmode_s = "NONE";
velavg = 0;
lsppartavg = 600.000000000;
flygravity_s = "0 0 0";
flycolldet_b = 0;
visname_s = "MFX_MANAFORLIFE_16BIT_TRIPOLY.TGA";
vistexisquadpoly = 0;
visorientation_s = "NONE";
vistexcolorstart_s = "255 100 100"; // "255 0 0"; // Lichterkettenartefakt
vistexcolorend_s = "255 0 0";
vissizestart_s = "25 25";
vissizeendscale = 2;
visalphafunc_s = "ADD";
visalphastart = 255.000000000;
visalphaend = 255.000000000;
};
/* Kopie von MFX_Fear_ORIGIN aber mit mehr Partikeln (ppsvalue), wichtig!
* Mit zu wenig pps weigert sich Gothic unter Umständen den FX zu rendern. */
INSTANCE MFX_ManaForLife_ORIGIN (C_PARTICLEFX)
{
ppsvalue = 75;
ppsscalekeys_s = "1 1 1 1 1 1 1 1";
ppsissmooth = 1;
ppsfps = 2;
shptype_s = "CIRCLE";
shpfor_s = "object";
shpoffsetvec_s = "0 -120 0";
shpdistribtype_s = "RAND";
shpdim_s = "20";
shpscalekeys_s = "1";
dirmode_s = "DIR";
dirfor_s = "world";
dirmodetargetfor_s = "OBJECT";
dirangleheadvar = 180;
dirangleelev = 90;
velavg = 0.00999999978;
lsppartavg = 1000;
lsppartvar = 150;
flygravity_s = "0 0 0";
visname_s = "HEAVENLIGHT.TGA";
visorientation_s = "VELO";
vistexisquadpoly = 1;
vistexcolorstart_s = "255 0 0";
vistexcolorend_s = "255 0 50";
vissizestart_s = "3 18";
vissizeendscale = 20;
visalphafunc_s = "ADD";
visalphastart = 255;
};
### scriptSFX ###
// ManaForLife
INSTANCE MFX_MANAFORLIFE_HEARTBEAT (C_SFX_DEF) {file= "MFX_HEARTBEAT.WAV"; vol = 127; loop=1; };
### scriptVisFx ###
/// XXXXXXXXXXXXXXXXXXXXXXXXXXXXX ///
/// XX M A N A F O R L I F E XX ///
/// XXXXXXXXXXXXXXXXXXXXXXXXXXXXX ///
/// mud-freak ///
// Blaues "Elekron" auf der Hand
INSTANCE spellFX_ManaForLife (CFx_Base_Proto)
{
visname_S = "MFX_ManaForLife_INIT";
emtrjoriginnode = "ZS_RIGHTHAND";
emtrjmode_s = "FIXED";
emtrjloopmode_s = "NONE";
emtrjeasefunc_s = "LINEAR";
emSelfRotVel_S = "0 500 -250";
emtrjdynupdatedelay = 0.;
emFXCreate_S = "spellFX_ManaForLife_2OBJ";
emFXInvestOrigin_S = "spellFX_ManaForLife_INVEST"; // SPL_NEXTLEVEL
};
INSTANCE spellFX_ManaForLife_KEY_CAST (C_ParticleFXEmitKey)
{
visname_S = "MFX_ManaForLife_ORIGIN";
sfxid = "SFX_HealObsession";
sfxIsAmbient = 1;
};
// Rotes "Elekron" auf der Hand
INSTANCE spellFX_ManaForLife_2OBJ (CFx_Base_Proto)
{
visname_S = "MFX_ManaForLife_INIT2";
emtrjoriginnode = "ZS_RIGHTHAND";
emtrjmode_s = "FIXED";
emtrjloopmode_s = "NONE";
emtrjeasefunc_s = "LINEAR";
emSelfRotVel_S = "500 0 250";
emtrjdynupdatedelay = 0.;
};
INSTANCE spellFX_ManaForLife_2OBJ_KEY_INIT (C_ParticleFXEmitKey)
{
pfx_ppsisloopingChg = 1; // Beenden sonst bleibt es nach Zauber
};
INSTANCE spellFX_ManaForLife_2OBJ_KEY_CAST (C_ParticleFXEmitKey)
{
pfx_ppsisloopingChg = 1; // Beenden sonst bleibt es nach Zauber
};
// Investphase
INSTANCE spellFX_ManaForLife_INVEST (CFx_Base_Proto)
{
visname_S = "MFX_ManaForLife_SPATTER_0";
emTrjOriginNode = "BIP01 Spine2";
emFXCreatedOwnTrj = 1;
sfxid = "MFX_MANAFORLIFE_HEARTBEAT";
sfxIsAmbient = 1;
lightPresetname = "AURA";
emFXCreate_S = "spellFX_ManaForLife_SPATTER_90";
emFXLifeSpan = 30; // Kindersicherung: Falls es hängen bleibt.
};
INSTANCE spellFX_ManaForLife_SPATTER_90 (CFx_Base_Proto)
{
visname_S = "MFX_ManaForLife_SPATTER_90";
emTrjOriginNode = "BIP01 Spine2";
emFXCreatedOwnTrj = 1;
emFXCreate_S = "spellFX_ManaForLife_SPATTER_180";
};
INSTANCE spellFX_ManaForLife_SPATTER_180 (CFx_Base_Proto)
{
visname_S = "MFX_ManaForLife_SPATTER_180";
emTrjOriginNode = "BIP01 Pelvis";
emFXCreatedOwnTrj = 1;
emFXCreate_S = "spellFX_ManaForLife_SPIRAL";
};
// Spirale in HP-Rot und Mana-Blau um den Caster
INSTANCE spellFX_ManaForLife_SPIRAL (CFx_Base_Proto)
{
visname_S = "MFX_ManaForLife_RING_LARGE1";
emTrjOriginNode = "Bip01";
emtrjmode_s = "FIXED";
emSelfRotVel_S = "0 400 0";
emtrjdynupdatedelay = 0;
emFXCreatedOwnTrj = 1;
emFXCreate_S = "spellFX_ManaForLife_SPIRAL2";
};
INSTANCE spellFX_ManaForLife_SPIRAL2 (CFx_Base_Proto)
{
visname_S = "MFX_ManaForLife_RING_LARGE2";
emTrjOriginNode = "Bip01";
emtrjmode_s = "FIXED";
emSelfRotVel_S = "0 400 0";
emtrjdynupdatedelay = 0;
emFXCreatedOwnTrj = 0;
emFXCreate_S = "DEMENTOR_FX";
};
### processManaRelease ###
TRUE
### humansmds ###
// Sacrifice (Modifiziert von Hum_SuckEnergy_Victim_K01.asc)
ani ("t_MagRun_2_SacCast" 1 "s_SacCast" 0.2 0.0 M. "HUM_MAGSAC_1_225.ASC" F 1 18)
ani ("s_SacCast" 1 "s_SacCast" 1.0 1.0 M. "HUM_MAGSAC_1_225.ASC" F 19 120 FPS:25)
ani ("t_SacCast_2_Stand" 1 "" 0.5 0.2 M. "HUM_MAGSAC_1_225.ASC" R 1 18 FPS:25)
ani ("t_SacCast_2_SacShoot" 1 "s_SacShoot" 0.2 0.0 M. "HUM_MAGSAC_1_225.ASC" F 121 156 FPS:35)
{
*eventSFX (151 "M_FALL_SMALL" EMPTY_SLOT )
}
ani ("s_SacShoot" 1 "s_SacShoot" 0.0 0.0 M. "HUM_MAGSAC_1_225.ASC" F 157 157 FPS:25)
ani ("t_SacShoot_2_Stand" 1 "" 0.0 0.1 M. "HUM_MAGSAC_1_225.ASC" F 158 225 FPS:16)
### copyFiles ###
SPL_ManaForLife\Spell_ManaForLife.d|_work\data\Scripts\Content\AI\Magic\Spells\
SPL_ManaForLife\TexturesItems\ItArRune_ZP.tga|_work\data\Textures\Items\
SPL_ManaForLife\TexturesItems\ItAr_Scroll_ZP.tga|_work\data\Textures\Items\
SPL_ManaForLife\MeshesItems\ItRu_ManaForLife.3ds|_work\data\Meshes\Items\IT_Runen\
SPL_ManaForLife\MeshesItems\ItSc_ManaForLife.3ds|_work\data\Meshes\Items\IT_Scrolls\
SPL_ManaForLife\Anims\HUM_MAGSAC_1_225.ASC|_work\data\Anims\
SPL_ManaForLife\SoundSFX\MFX_HEARTBEAT.WAV|_work\data\Sound\SFX\
SPL_ManaForLife\TexturesEffectsMagic\HellLight.tga|_work\data\Textures\Effects\Magic\
SPL_ManaForLife\TexturesEffectsMagic\MFX_ManaForLife_16bit_tripoly.tga|_work\data\Textures\Effects\Magic\
### END ###
Und hier sind die Anchor, die die Schlüsselwörter definieren und referenzieren (wie diese definiert sind, ist etwas umfangreicher und darauf werde ich hier nicht weiter eingehen. Dazu ist die Dokumentation hilfreich).
Nun bedarf es nur noch einer Konfigurationsdatei. Hier mal eine Minimalversion (dort kann man aber optional noch vieles mehr einstellen):
Spoiler:(zum lesen bitte Text markieren)
Code:
{
"title": "Manasucht Installation",
"globalHeader": "// Die folgende(n) Zeile(n) wurden für Manasucht erstellt",
"installInstruction": "Wähle den Ordner in dem sich Gothic 2 dNdR (modkit) befindet",
"defaultPath": "C:\\Program Files (x86)\\JoWooD\\Gothic II\\",
"diffGUI": true,
"features": {
"filePattern": ".*\\.feat"
},
"anchors": {
"": "^anchors.*\\.json$"
}
}
Jetzt haben wir drei Dateien (+ die zu kopierenden Dateien):
config.json (die Konfigurationsdatei)
anchors.json (die Datei mit allen Anchors)
Manasucht.feat (die Feature-Datei für unseren Zauber)
Jetzt startet man BuildSetup.exe und fügt die drei Dateien (+ die zu kopierenden Dateien) per drag-and-drop hinzu und lässt sich sein Setup erstellen.
Hier noch mal alles zusammen, um es besser zu inspizieren. Sowohl die Source-Dateien, also auch ein fertig-erstelltes Setup: manasucht_src.zip manasucht_setup.zip
Die Fragwürdigkeiten und Bedenken, die Lehona anspricht sind richtig und die hätte ich im Einleitungspost erwähnen sollen:
Es kann nicht gewährleistet werden, dass ein FeatShare Setup in 100% der Fällen funktioniert, da ein RegEx vielleicht bei jemandem einfach nicht greift, da seine Gothic Installation einfach zu sehr abweicht. Im Beispiel vom Zauberpaket ist mir bisher jedoch nur ein Fall bekannt, bei dem ein RegEx nicht funktioniert hat. (Dort lag es daran, dass die Skripte aus dem GothicSource dekompiliert waren und Konstanten fehlten). Es hängt sehr davon ab, wo und wie man etwas einfügen möchte. Die Anchors oben für Zauber sollten im Normalfall immer greifen. Ein RegEx soll hier nicht missverstanden werden: Es soll Deadalus nicht parsen; der Ersteller eines FeatShare Setups sollte sich einfach Gedanken machen, einen festdefinierbaren Anhaltspunkt zu finden, der in Gothic Installationen wenig bis gar nicht abweicht, an dem er sich orientieren und den er als verlässlichen RegEx ausdrücken kann. Wenn jemand eine Konstante tatsächlich so blödsinnig definiert wie im dritten Post, der sollte sich nicht wundern, wenn ein solches Setup bei ihm nicht funktioniert.
Der Sinn von FeatShare ist nicht ein Verson-Control-System zu simulieren oder gar zu ersetzen. Ich fand es nur äussert umständlich die gesamte Gothic Installation in ein solches zu stecken. Leute, die nicht am gleichen Mod-Projekt arbeiten, können sowieso nichts untereinander mergen. FeatShare ist also einfach ein Einpflege-Tool.
Da ich beim Veröffentlichen vom Zauberpaket dieses Tool in seiner ersten (hardgecodeten) Version so nützlich fand, habe ich es generalisiert, um es zu hier zu teilen. Ob es tatsächlich überhaupt von anderen einsetzbar ist, konnte und kann ich nicht sagen. Teilen kann nicht schaden, habe ich mir gedacht. Im schlimmsten Fall, kann mindestens sicher jemand etwas mit dem Sourcecode auf Github etwas anfangen.