To enqueue a normal script function into the AI queue of an NPC, use this. There are other options (such as hardcoding a delay using a trigger), but none are this robust and easy to use
Wow. Literally WOW! Thanks, I can finally do a lot of stuff I always wanted to
In startup.d the function is called every 700 milliseconds. It's supposed to cast a spell when you're waving your staff around (BS_HIT). But it spams the error above, hindering the game. How can I fix it?
It's possible add to oCMag_Book more than 7 spells? Default is 4-0 keys how i can assign 10 spells - possible? And second question: where is saved info about this numbers? [Bild: N755LSX.png]
Hello I have small question about zEngine - How is made moving system? EventManager? I'm trying make shield def look's like Gothic 3, but i don't know how i can make special moving system for this.
Hi, ich habe folgendes Problem. Ich wollte die GothicMod.exe (Gothic 1) analysieren. Ich bin vorgegangen wie ich es auch bei Gothic 2 getan habe, anhand der IDA Anleitung von NicoDE[1]. Allerdings erkennt IDA fast alle Funktionen nicht. Anstatt Code wird alles als Data erkannt, siehe Screenshot (grau). [Bild: idaG1_data.png]
Ich habe mit diesem Python Skript versucht die Bereiche zu undefinieren [U], als Code zu definieren [C] und als Funktion zu erkennen [P]. Allerdings scheint das Skript nichts zu unternehmen (ich muss zugeben ich verstehe das Skript im Detail nicht).
Ich bin mir nicht mehr ganz sicher, aber beim ersten Analysieren[2] wurden von Vorn herein alle Funktionen richtig identifiziert. Nach einem Versehen (ohne Backup) musste ich von die Datenbank neu erstellen und nun klappt es nicht mehr. Gibt es eine Art Cache/temporäre Dateien von IDA, die ich löschen könnte?
[1] Folgendermassen bin ich vorgegangen:
Load GothicMod.exe as Portable executable for 80386 (PE) [pe.ldw]
Processortype: MetaPC (disassemble all opcodes) [metapc]
Analsis: Enabled: Nein, Indicator enabled: Ja
Options: Create FLAT group: Ja, Load resources: Nein, Rename DLL entries: Ja, Manual load: Nein, Create imports segment: Ja List of strings wurde nicht automatisch generiert (habe ich auch nicht manuell gemacht)
Es gab (anders als in der Anleitung) zwei Signuturen: vc32rtf, vc32mfc. Beide habe ich gelöscht
Beim Laden des PDB file habe ich die GothicMod.exe wieder ausgewählt mit der Adresse 0x400000. Es wurden nur 2 Typen und 16811 Symbole geladen.
Anschliessend habe ich die Analyse gestartet: Type library 'vc6win' loaded Types applied to 37 names
Dann erhalte ich die Datenbank wie im Screenshot, mit fast allen Funktionen nicht erkannt. [2] Beim allerersten Mal, bei dem es scheinbar klappte, habe ich wenn ich mich recht erinnere versehentlich direkt zu Beginn analysiert (ohne die Signaturen zu löschen). Bei erneutem Versuchen hat das aber nicht mehr geklappt.
Weiss da jemand Rat? Ich kenne mich nicht genug mit den Einstellung von IDA aus.
EDIT: Es hat jetzt geklappt, als ich die Signaturen nicht gelöscht hab.
@NicoDE: Wenn du möchtest könntest du deinen Post um die Adressen für Gothic 1 erweitern. Ich bin durch Forensuchen immer auf deinen Post gestossen, das wäre es vielleicht nicht schlecht Folgendes noch dazu zu schreiben (vorausgesetzt ich irre mich nicht):
Abweichung für die GothicMod.exe:
Bei GothicMod.exe den Schritt mit den Sigatures überspringen (die Signatures nicht löschen)
Manuelle Bearbeitung für GothicMod.exe (unterer Teil der List):
Ich habe gerade einen kleinen grossen Fehler in den Ikarus Skripten für Gothic 1 gefunden. Ich weiss nicht, ob der bisher bekannt war. Die zTimer Adresse ist falsch:
Code:
--- old/Ikarus_Const_G1.d
+++ new/Ikarus_Const_G1.d
@@ -52,1 +52,1 @@
- const int MEMINT_zTimer_Address = 9236972;//0x8CF1EC
+ const int MEMINT_zTimer_Address = 9236968;//0x8CF1E8
Generell hatte das keine Auswirkungen (LeGo benutzt zTimer nicht direkt, sondern MEM_GetSystemTime()), nur wenn man die Instanz MEM_Timer direkt angesprochen hat.
EDIT: Da es für Ikarus wohl keine weiteren Updates mehr geben wird, kann man fürs Gothic 1 modden aus der INIT_WORLD() (das Gothic1-Äquivalent zu INIT_Global()) einfach folgende Funktion nach Ikarus aufrufen. Damit wird auch bei allen zukünftigen Aufrufen von MEM_Init() MEM_Timer richtig initialisiert.
Code:
func void zTimerFix_G1(){// Fix zTimer for Gothic 1 (the address in Ikarus is wrong)
if (GOTHIC_BASE_VERSION == 1){
MEMINT_zTimer_Address = 9236968; //0x8CF1E8
MEM_Timer = _^(MEMINT_zTimer_Address);
};
};
Was mich an Gothic 2 immer gestört hat ist, dass die Menu-Musik weiterläuft, wenn zu Spielstart das Intro startet. (In Gothic 1 ist das nicht der Fall). Ich habe eine Funktion geschrieben, die alle aktiven Sounds sofort stoppt (Musik ist nicht betroffen, die Menu-Musik ist ein "Sound", keine Musik). Man kann die Funktion also aus der Startup kurz vor starten des Intro-Videos aufrufen, um das Problem zu beheben und einem Intro vernünftig zu lauschen:
Beispiel:
Code:
// ------ World -------
FUNC VOID STARTUP_NewWorld(){// ------ StartUps der Unter-Parts ------
STARTUP_NewWorld_Part_Forest_01();
STARTUP_NewWorld_Part_Pass_To_OW_01();
// ------ INTRO - muss ganz am Ende der Startup stehen ------
Kapitel = 1; //Joly: Kann hier stehen bleiben!
stopAllSounds();PlayVideo("INTRO.BIK");
PlayVideo("Addon_Title.BIK");
};
Auch wenn das Problem nicht bei Gothic 1 auftaucht, der Vollständigkeit halber mit Adressen für Gothic 1 und Gothic 2.
Code:
func void stopAllSounds(){
MEM_InitAll();
const int zsound_G1 = 9236044; //0x8CEE4C
const int zsound_G2 = 10072124; //0x99B03C
const int zCSndSys_MSS__RemoveAllActiveSounds_G1 = 5112224; //0x4E01A0
const int zCSndSys_MSS__RemoveAllActiveSounds_G2 = 5167008; //0x4ED7A0
var int zsoundPtr; zsoundPtr = MEMINT_SwitchG1G2(MEM_ReadInt(zsound_G1), MEM_ReadInt(zsound_G2));
const int call = 0;
if (CALL_Begin(call)){
CALL__thiscall(_@(zsoundPtr), MEMINT_SwitchG1G2(zCSndSys_MSS__RemoveAllActiveSounds_G1,
zCSndSys_MSS__RemoveAllActiveSounds_G2));
call = CALL_End();
};
};
Als kurze Anmerkung: Im Menü wird beim Start von Gothic sowohl eine .wav als auch eine Musik gestartet. Die Musik stoppt beim Laden, die .wav stoppt nicht. Eine (etwas schlechtere) Alternative ist es also, die .wav durch eine Stille Datei zu ersetzen, wobei die Musik zu Beginn dann nicht so laut ist.
Perfekt, vielen Dank mud-freak! Ich wollte dazu sowieso bald wieder mal Milkys Vorschlag "von damals" heraussuchen mit der stillen Datei, weil mich das immer unglaublich nervt.
So hat man gleich noch eine viel einfachere Lösung!
Ich habe gerade einen kleinen grossen Fehler in den Ikarus Skripten für Gothic 1 gefunden. Ich weiss nicht, ob der bisher bekannt war. Die zTimer Adresse ist falsch:
Code:
--- old/Ikarus_Const_G1.d
+++ new/Ikarus_Const_G1.d
@@ -52,1 +52,1 @@
- const int MEMINT_zTimer_Address = 9236972;//0x8CF1EC
+ const int MEMINT_zTimer_Address = 9236968;//0x8CF1E8
Generell hatte das keine Auswirkungen (LeGo benutzt zTimer nicht direkt, sondern MEM_GetSystemTime()), nur wenn man die Instanz MEM_Timer direkt angesprochen hat.
EDIT: Da es für Ikarus wohl keine weiteren Updates mehr geben wird, kann man fürs Gothic 1 modden aus der INIT_WORLD() (das Gothic1-Äquivalent zu INIT_Global()) einfach folgende Funktion nach Ikarus aufrufen. Damit wird auch bei allen zukünftigen Aufrufen von MEM_Init() MEM_Timer richtig initialisiert.
Code:
func void zTimerFix_G1(){// Fix zTimer for Gothic 1 (the address in Ikarus is wrong)
if (GOTHIC_BASE_VERSION == 1){
MEMINT_zTimer_Address = 9236968; //0x8CF1E8
MEM_Timer = _^(MEMINT_zTimer_Address);
};
};
Wäre es vielleicht sinnvoll eine Art Fork von Ikarus zu machen und trotzdem eine gefixte Variante zur Verfügung zu stellen? Der Fix wäre doch in Ikarus besser aufgehoben.
Zitat von mud-freak
Was mich an Gothic 2 immer gestört hat ist, dass die Menu-Musik weiterläuft, wenn zu Spielstart das Intro startet. (In Gothic 1 ist das nicht der Fall). Ich habe eine Funktion geschrieben, die alle aktiven Sounds sofort stoppt (Musik ist nicht betroffen, die Menu-Musik ist ein "Sound", keine Musik). Man kann die Funktion also aus der Startup kurz vor starten des Intro-Videos aufrufen, um das Problem zu beheben und einem Intro vernünftig zu lauschen:
Beispiel:
Code:
// ------ World -------
FUNC VOID STARTUP_NewWorld(){// ------ StartUps der Unter-Parts ------
STARTUP_NewWorld_Part_Forest_01();
STARTUP_NewWorld_Part_Pass_To_OW_01();
// ------ INTRO - muss ganz am Ende der Startup stehen ------
Kapitel = 1; //Joly: Kann hier stehen bleiben!
stopAllSounds();PlayVideo("INTRO.BIK");
PlayVideo("Addon_Title.BIK");
};
Auch wenn das Problem nicht bei Gothic 1 auftaucht, der Vollständigkeit halber mit Adressen für Gothic 1 und Gothic 2.
Code:
func void stopAllSounds(){
MEM_InitAll();
const int zsound_G1 = 9236044; //0x8CEE4C
const int zsound_G2 = 10072124; //0x99B03C
const int zCSndSys_MSS__RemoveAllActiveSounds_G1 = 5112224; //0x4E01A0
const int zCSndSys_MSS__RemoveAllActiveSounds_G2 = 5167008; //0x4ED7A0
var int zsoundPtr; zsoundPtr = MEMINT_SwitchG1G2(MEM_ReadInt(zsound_G1), MEM_ReadInt(zsound_G2));
const int call = 0;
if (CALL_Begin(call)){
CALL__thiscall(_@(zsoundPtr), MEMINT_SwitchG1G2(zCSndSys_MSS__RemoveAllActiveSounds_G1,
zCSndSys_MSS__RemoveAllActiveSounds_G2));
call = CALL_End();
};
};
Klingt nach einer äußerst sinnvollen Sache Werd ich dann gleich mal in XR übernehmen
//Global int
var int m_arrBuffs; //zCArray<CBuff*>
//Function
if(m_arrBuffs) {
if (MEM_ArraySize(m_arrBuffs) <= 0) {
MEM_ArrayClear(m_arrBuffs);
m_arrBuffs = 0;
};
};
The last message in your function is printed, so the crash occurs somewhere else. You're accessing the array somewhere without checking whether it could be null.
I found! Ikarus Arrays don't check value of pointer... If pointer is NULL, function try read value from NULL (MEM_ReadInt(NULL + 8));
So i fixed this (Redefine Ikarus functions):
Code:
func int MEM_ArrayCreate () {
return MEM_Alloc (sizeof_zCArray);
};
func void MEM_ArrayFree(var int zCArray_ptr) {
if(!zCArray_ptr) {
return;
};
var int array; array = MEM_ReadInt (zCArray_ptr);
if (array) {
MEM_Free (array);
};
MEM_Free (zCArray_ptr);
};
func void MEM_ArrayClear (var int zCArray_ptr) {
if(!zCArray_ptr) {
return;
};
var zCArray array;
array = _^(zCArray_ptr);
if (array.array) {
MEM_Free (array.array);
array.array = 0;
};
array.numAlloc = 0;
array.numInArray = 0;
};
func int MEM_ArraySize(var int zCArray_ptr)
{
if(!zCArray_ptr) {
return 0;
};
return MEM_ReadInt(zCArray_ptr + 8);
};
func void MEM_ArrayWrite(var int zCArray_ptr, var int pos, var int value) {
if(!zCArray_ptr) {
return;
};
var zCArray array;
array = _^(zCArray_ptr);
if (pos < 0 || array.numInArray <= pos) {
MEM_Error (ConcatStrings("MEM_ArrayWrite: pos out of bounds: ", IntToString(pos)));
return;
};
MEM_WriteIntArray(array.array, pos, value);
};
func int MEM_ArrayRead(var int zCArray_ptr, var int pos) {
if(!zCArray_ptr) {
return 0;
};
var zCArray array; array = _^(zCArray_ptr);
if (pos < 0 || array.numInArray <= pos) {
MEM_Error (ConcatStrings("MEM_ArrayRead: pos out of bounds: ", IntToString(pos)));
return 0;
};
return MEM_ReadIntArray(array.array, pos);
};
//************************************************
// Insert / Push / Pop / Top
//************************************************
func void MEM_ArrayInsert (var int zCArray_ptr, var int value) {
if(!zCArray_ptr) {
return;
};
var zCArray array;
array = _^(zCArray_ptr);
if (!array.array) {
//Noch gar kein Array angelegt. Erstmals anlegen
array.numAlloc = 16; //Startwert
array.array = MEM_Alloc (array.numAlloc * 4);
} else if (array.numInArray >= array.numAlloc) {
//kein Platz mehr
//nehmen wir mal das doppelte (oder ist das zu gierig? sollte passen):
array.numAlloc = 2 * array.numAlloc;
array.array = MEM_Realloc (array.array, array.numInArray * 4, array.numAlloc * 4);
};
//Jetzt muss Platz sein:
MEM_WriteIntArray (array.array, array.numInArray, value);
array.numInArray += 1;
};
func void MEM_ArrayPush (var int zCArray_ptr, var int value) {
MEM_ArrayInsert(zCArray_ptr, value);
};
func int MEM_ArrayPop(var int zCArray_ptr) {
if (!zCArray_ptr) {
MEM_Error ("MEM_ArrayPop: Invalid address: zCArray_ptr may not be null!");
return 0;
};
var zCArray array;
array = _^(zCArray_ptr);
if (!array.numInArray) {
MEM_Error ("MEM_ArrayPop: Underflow! Cannot pop from empty array.");
return 0;
};
array.numInArray -= 1;
return MEM_ReadIntArray(array.array, array.numInArray);
};
func int MEM_ArrayTop(var int zCArray_ptr) {
if (!zCArray_ptr) {
MEM_Error ("MEM_ArrayTop: Invalid address: zCArray_ptr may not be null!");
return 0;
};
var zCArray array;
array = _^(zCArray_ptr);
if (!array.numInArray) {
MEM_Error ("MEM_ArrayTop: Underflow! Cannot pop from empty array.");
return 0;
};
return MEM_ReadIntArray(array.array, array.numInArray - 1);
};
//************************************************
// IndexOf / RemoveIndex / RemoveValue[Once]
//************************************************
func int MEM_ArrayIndexOf(var int zCArray_ptr, var int value) {
if (!zCArray_ptr) {
MEM_Error ("MEM_ArrayIndexOf: Invalid address: zCArray_ptr may not be null!");
return -1;
};
var zCArray array;
array = _^(zCArray_ptr);
var int i; i = 0;
var int loop; loop = MEM_StackPos.position;
if (i < array.numInArray) {
if (MEM_ReadIntArray(array.array, i) == value) {
return i;
};
i += 1;
MEM_StackPos.position = loop;
};
return -1;
};
func void MEM_ArrayRemoveIndex (var int zCArray_ptr, var int index) {
if (!zCArray_ptr) {
MEM_Error ("MEM_ArrayRemoveIndex: Invalid address: zCArray_ptr may not be null!");
return;
};
var zCArray array;
array = _^(zCArray_ptr);
if (array.numInArray <= index) {
MEM_Error ("MEM_ArrayRemoveIndex: index lies beyond the end of the array!");
return;
};
//letzten Wert in die Lücke schieben
array.numInArray -= 1;
MEM_WriteIntArray (array.array, index, MEM_ReadIntArray (array.array, array.numInArray));
};
var int MEMINT_ArrayRemoveValue_OnlyOnce;
func void MEM_ArrayRemoveValue (var int zCArray_ptr, var int value) {
if (!zCArray_ptr) {
MEM_Error ("MEM_ArrayRemoveValue: Invalid address: zCArray_ptr may not be null!");
return;
};
var zCArray array;
array = _^(zCArray_ptr);
var int i; i = 0;
var int loop; loop = MEM_StackPos.position;
//schon durchgelaufen?
/* while */ if (i < array.numInArray) {
if (MEM_ReadIntArray (array.array, i) == value) {
//dann element entfernen
array.numInArray -= 1;
MEM_WriteIntArray (array.array, i, MEM_ReadIntArray (array.array, array.numInArray));
//weitersuchen?
if (MEMINT_ArrayRemoveValue_OnlyOnce) {
MEMINT_ArrayRemoveValue_OnlyOnce = 2; //geschafft
return;
};
} else {
i += 1;
};
MEM_StackPos.position = loop;
};
};
func void MEM_ArrayRemoveValueOnce (var int zCArray_ptr, var int value) {
MEMINT_ArrayRemoveValue_OnlyOnce = true;
MEM_ArrayRemoveValue (zCArray_ptr, value);
if (MEMINT_ArrayRemoveValue_OnlyOnce != 2) {
MEM_Warn (ConcatStrings ("MEM_ArrayRemoveValueOnce: Could not find value: ", IntToString (value)));
};
MEMINT_ArrayRemoveValue_OnlyOnce = false;
};
That's not a fix. Yes, your code doesn't crash anymore, but you're only suppressing the symptoms of your bug/problem. You should never call MEM_ArrayClear() with a nullptr in the first place, doing so just shows that you don't handle your memory carefully (which might later on lead to memory leaks, segfaults or faulty behaviour).