I noticed that the installer adds extra characters if the plagunlist is empty
Here is an example that was sent to me.
[PLUGINS]
PluginList = ,, Ninja.dll**
AutorunFilter = dll, m3d, src, d
UseVDFS = true
Thanks, I will look into this. As I take it, this does not impact functionality but I understand that this is not so pretty and should be changed. (These NSIS setups are so counter intuitive to write, it never quite turns out correct.)
Oops... Apparently he was in such a hurry that he didn't even notice his own errors.
I'll check later if necessary. (I just corrected the dialog a long time ago, so the error was in another, related to the perceptions of the NPCs)
Thank you for fixing the installer.
edit. I made a similar patch, but only in union, and I noticed that when Ninja is enabled in Pluginlist, when scripts are injected, a crash occurs.Gratt saw that a crash occurs when building ou.bin
Hab' wieder mal eine Idee für einen Patch, der sicher nicht schwierig wäre:
Ein Rezept von dem man lernt, wie man aus [einstellbare Menge in der Gothic.ini] Dunkelpilzen einen Trank zur Erhöhung des permanenten Manavorrats erlernt.
Entweder befindet sich das Rezept wie die Rune aus dem First Mage Kit von Anfang an im Spielerinventar, oder man lässt das Rezept zum Beispiel in Händlerinventaren spawnen, wo sich ein bestimmter Trank oder ein anderes Rezept (Reines Leben, Reines Mana) befinden, wie das z.B. die Spruchrolle Schlösser knacken aus dem First Mage Kit bei Anwesenheit von Dietrichen macht.
edit:
Wegen Bedenken bzgl. des Balancings von Mods: Man könnte neben der Koppelung an die Anwesenheit anderer Dinge in Händlerinventaren alle Zahlen (#Dunkelpilze, #maxMana, #Preis) in der Gothic.ini einstellbar machen.
Hab' wieder mal eine Idee für einen Patch, der sicher nicht schwierig wäre:
Ein Rezept von dem man lernt, wie man aus [einstellbare Menge in der Gothic.ini] Dunkelpilzen einen Trank zur Erhöhung des permanenten Manavorrats erlernt.
Entweder befindet sich das Rezept wie die Rune aus dem First Mage Kit von Anfang an im Spielerinventar, oder man lässt das Rezept zum Beispiel in Händlerinventaren spawnen, wo sich ein bestimmter Trank oder ein anderes Rezept (Reines Leben, Reines Mana) befinden, wie das z.B. die Spruchrolle Schlösser knacken aus dem First Mage Kit bei Anwesenheit von Dietrichen macht.
edit:
Wegen Bedenken bzgl. des Balancings von Mods: Man könnte neben der Koppelung an die Anwesenheit anderer Dinge in Händlerinventaren alle Zahlen (#Dunkelpilze, #maxMana, #Preis) in der Gothic.ini einstellbar machen.
Problem hierbei ist: Einige Mods entfernen z.B. den Mana-Bonus durch Dunkelpilze und ersetzen ihn durch ihre eigenen Rezepte. (Welche zum Teil tausende Goldmünzen kosten)
Problem hierbei ist: Einige Mods entfernen z.B. den Mana-Bonus durch Dunkelpilze und ersetzen ihn durch ihre eigenen Rezepte. (Welche zum Teil tausende Goldmünzen kosten)
Schrieb der Ersteller der Manaregenerationspatches .
Ich gehe davon aus, dass das letztlich die Spieler*innen selbst am besten wissen, ob sie sich die kleine Stärkung gönnen wollen. Ich fände so ein Rezept eine gute, kleine quality of life Ergänzung und in sehr vielen Mods brauchbar.
Hab' wieder mal eine Idee für einen Patch, der sicher nicht schwierig wäre:
Ein Rezept von dem man lernt, wie man aus [einstellbare Menge in der Gothic.ini] Dunkelpilzen einen Trank zur Erhöhung des permanenten Manavorrats erlernt.
wozu? es gibt doch bereits ein Rezept für einen permanenten Manatrank.
I made a similar patch, but only in union, and I noticed that when Ninja is enabled in Pluginlist, when scripts are injected, a crash occurs.Gratt saw that a crash occurs when building ou.bin
He promised to try to fix this moment.
He contacted me and I don't see any collision from Ninja's side. Ninja neither touches zCParser::IsInAdditionalInfo, nor zCParser::DeclareFuncCall, nor zCParser::DeclareAssign. It's also visible that the crash occurs from ZPARSEREXTENDER.DLL, see forth line from the bottom. To me it seems there is some debugging necessary from the side of the plugin to identify the problem - which is honestly not my job.
Zum "Dunkelpilz-Rezept": Ich denke wir brauchen jetzt nicht jede Patch-Idee kurz und klein hacken. Balancing hin- oder her, technische Probleme sollte so ein Patch nicht verursachen. Über den Ansatz/Implementierung lässt sich natürlich streiten, aber das Problem, dass das Essen von zig Dunkelpilzen sehr lange dauert ist doch gerechtfertigt.
Deshalb könnte man etwas genereller ansetzen und vielleicht stattdessen einen Patch erstellen, der das Konsumieren von einer bestimmten Anzahl jegleichen Items erlaubt, anstatt das man zig mal klicken muss. Das könnte ähnlich aussehen, wie einige Mods das bereits fürs Fleischbraten umgesetzt haben, z.B. durch Dialog-Optionen ("5 konsumieren", "10 konsumieren", "Alle konsumieren", usw.). Schönere Möglichkeiten gibt es sicherlich aber auch.
@Kirides can u be so kind to look into problem with compatibility patch called "Picklockhelper" . I hve installed Union 1.0h on G1 and seems to be error with unknown identifier "font_defeault".
Ich versteh auch nicht ganz was du denn genau vorhast. Dein Beispiel von oben mit B_ClearRuneInv sähe ganz einfach so aus:
Code:
func void Patch_ClearRuneInv(var C_Npc slf){// Do something crazy
// Continue with original B_ClearRuneInv (or omit this to act as replacement)
PassArgumentN(slf);
ContinueCall();
};
/*
* Initialization
*/
func void Patch_Init(){// ...
// Hook Daedalus function if it exists (otherwise nothing happens - super clean for a patch)
HookDaedalusFuncS("B_ClearRuneInv", "Patch_ClearRuneInv");
};
Ist doch ganz easy?! Schau dir mal den Mud-Patch an, für weitere Beispiele genau dieser Art.
Hello. What's the best way to do it if the game has this feature, but you need to add something new to it?
For example we have the function B_AssessDamage()
Spoiler:(zum lesen bitte Text markieren)
Code:
// ****************************************************************// B_AssessDamage
// --------------
// wird durchWahrnehmung PERC_AssessDamage ÜBERALL her aufgerufen
// (also auch aus ZS_Attack, AR kann sich allerdings NICHT ändern!)
// ****************************************************************
func void B_AssessDamage ()
{
var C_NPC Quarho; Quarho = Hlp_GetNpc (NONE_ADDON_111_Quarhodron);
var C_NPC Rhadem; Rhadem = Hlp_GetNpc (NONE_ADDON_112_Rhademes);
if ((Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Quarho)))
|| ((Hlp_GetInstanceID(self) == Hlp_GetInstanceID(Rhadem)))
{
B_GhostSpecialDamage (other, self);
return;
};
B_BeliarsWeaponSpecialDamage (other, self);
// AIVARS
if (self.aivar[AIV_ArenaFight] == AF_AFTER)
{
self.aivar[AIV_ArenaFight] = AF_AFTER_PLUS_DAMAGE;
};
if self.aivar [AIV_EnemyOverride] == TRUE
{
var C_NPC RAV; RAV = Hlp_GetNpc(BDT_1090_Addon_Raven);
if (Hlp_GetInstanceID(self) == (Hlp_GetInstanceID(RAV)))
{
self.aivar [AIV_EnemyOverride] = FALSE;
};
};
// ------ Wenn NSC im ZS_Attack ------
if (Npc_IsInState(self,ZS_Attack))
{
// EXIT IF...
// ------ Freunde ignorieren Treffer vom Spieler im Kampf ------
if (Npc_IsPlayer (other))
&& (self.npctype == NPCTYPE_FRIEND)
{
return;
};
// ------ Partymember ignorieren Treffer vom Spieler im Kampf ------
if (Npc_IsPlayer (other))
&& (self.aivar[AIV_PARTYMEMBER] == TRUE)
{
return;
};
// FUNC
// ------ Wenn ich von jemand ANDEREM getroffen werde ------
if (Hlp_GetInstanceID (other) != self.aivar[AIV_LASTTARGET])
{
// ------ NEUER Angreifer ist NPC ODER zum zweiten MAl (von Hero) getroffen ------
if (self.aivar[AIV_HitByOtherNpc] == Hlp_GetInstanceID (other))
|| (Hlp_GetInstanceID(other) != Hlp_GetInstanceID(hero))
{
Npc_SetTarget (self, other); //Ziel wechseln
}
else //NEUER Angreifer ist hero und trifft zum ersten mal
{
self.aivar[AIV_HitByOtherNpc] = Hlp_GetInstanceID (other); //EIN Freischlag
};
};
return;
};
// EXIT IF
// ------ NSC ist ENEMY -----
if (B_AssessEnemy())
{
return;
};
// ------ Egill/Enim NSCs (Story-Angreifer) ------
if (!Npc_IsPlayer(other))
&& (other.aivar[AIV_ATTACKREASON] == AR_NONE)
{
B_Attack (self, other, AR_NONE, 0); //angreifen oder fliehen
return;
};
// ------ Spieler hat mit NK-Waffe angegriffen ------
if (Npc_IsInFightMode (other, FMODE_MELEE))
|| (Npc_IsInFightMode (other, FMODE_FIST))
|| (Npc_IsInFightMode (other, FMODE_NONE)) //oder Waffe wieder weggesteckt (nicht ermittelbar)
{
// ------ NSC ist freundlich ODER npctype_friend ------
if (Npc_GetAttitude (self, other) == ATT_FRIENDLY)
|| ( (self.npctype == NPCTYPE_FRIEND) && Npc_IsPlayer(other) )
{
// ------- nur wenn ich zum ERSTEN Mal geschlagen werde -------
if (!Npc_IsInState(self, ZS_ReactToDamage))
{
Npc_ClearAIQueue (self);
B_ClearPerceptions (self); //schaltet alle Wahrnehmungen ab - so kann keine später priorisierte diesen Stateaufruf verhindern (s. z.B. AssessFightSound + AssessDamage)
AI_StartState (self, ZS_ReactToDamage, 0, "");
return; //im unterern B_Attack wird ein ClearQueue aufgerufen, der den AI_StartState direkt wieder aus der queue löscht
// - sicherheithalber bei allen AI_StartState-Aufrufen return anfügen
};
};
};
// FUNC
B_Attack (self, other, AR_ReactToDamage, 0); //angreifen oder fliehen
return;
};
The mod added a call to the function B_myweaponspecialdamage(other,self)
Even if you do this:
then when replacing the function, the NPCs will stop responding to damage.
Even if we add the full code, the mod may contain its own functions that are called and we will break the mod.
I just have so far tried to translate a small mod in the form of such a patch on Union/Ninja fails.
Gratt can't tell me either.
Edit.
On union, it looks like it will soon be implemented, Gratt said that the replaced functions remain, but are renamed with the _old
I just add a call to the original function and the operability remains.
Example
Hello. What's the best way to do it if the game has this feature, but you need to add something new to it?
Thanks for moving the discussion here. I think this is a good place for this.
Yes this is long possible Ninja thanks to HookDaedalus from LeGo. It allows you to either
Replace a function completely
Add code at the beginning of the function (i.e. hook before)
Add code after the function (i.e. hook after)
Information about this is available in the Ninja documentation here. I suspect that these details are easily lost in the mass of the documentation. Practical info from the side of LeGo can be found here and here.
For your particular example this could look like this:
Code:
func void Ninja_{PatchName}_B_AssessDamage(){// Add new code before B_AssessDamage
B_MyWeaponSpecialDamage(other, self);
// Continue with the original function
ContinueCall();
};
// ...
func void Ninja_{PatchName}_Init(){// ...
// Hook the Daedalus function if it exists
HookDaedalusFuncS("B_AssessDamage", "Ninja_{PatchName}_B_AssessDamage");
};
func void Ninja_Coast_B_AssessDamage()
{
//Ninja_Coast_b_myweaponspecialdamage(other,self);
// Add new code before B_AssessDamage
Print("Test");
//Continue with the original function B_AssessDamage
ContinueCall();
};
ninjainit.d
Spoiler:(zum lesen bitte Text markieren)
Code:
/*
* Initialization function called by Ninja after "Init_Global" (G2) / "Init_<Levelname>" (G1)
*/
func void Ninja_Coast_Init() {
// Initialize Ikarus
MEM_InitAll();
// Hook the Daedalus function if it exists
HookDaedalusFuncS("B_AssessDamage", "Ninja_Coast_B_AssessDamage");
};
You can check if the initialization function is successfully called (add some zSpy messages in Ninja_Coast_Init and check if any warnings show up starting with "HOOKDAEDALUS: ... ").
PS: I don't see the file Ninja_Coast_B_AssessDamage.d in your screenshot. Did you include and enter it into the Gothic.src?
You can check if the initialization function is successfully called (add some zSpy messages in Ninja_Coast_Init and check if any warnings show up starting with "HOOKDAEDALUS: ... ").
PS: I don't see the file Ninja_Coast_B_AssessDamage.d in your screenshot. Did you include and enter it into the Gothic.src?
All right, I'll add the messages later and check if there are any at all.
The file is located in the Content\AI\Human\B_Human folder
Made a bad picture.
Hi. I added a message output to zSpy when the plugin should load and there is no message, it looks like there is no initialization.
Other plugins are loaded.
Code:
/* * Initialization function called by Ninja after "Init_Global" (G2) / "Init_<Levelname>" (G1)
*/
func void Ninja_Coast_Init() {
// Initialize Ikarus
MEM_InitAll();
MEM_Warn("Ninja Coast Initialize");
// Hook the Daedalus function if it exists
HookDaedalusFuncS("B_AssessDamage", "Ninja_Coast_B_AssessDamage");
};
Comparing the order of files, it seems to have collected everything correctly. It looks like I don't see an error somewhere. I'll drop the files if I have time to look (everything Inside is very raw, the scripts will be redone when I figure out why the plugin is not initialized).