Ich habe ein sehr merkwürdiges Verhalten bei den HookEngine-Funktionen von Gothic 1 bemerkt.
In der INIT Funktion meiner Welt rufe ich einmalig HookEngine für das Aufheb-Event auf. Die Funktion, die dafür ausgeführt wird, enthält einen Print, damit ich sehen kann, ob und wie sie aufgerufen wird.
Wenn man aus dem Hauptmenü aus ein neues Spiel startet und etwas aufhebt, kommt der print 1 mal.
Startet man aus diesem laufenden Spiel heraus ein neues Spiel und hebt etwas auf, kommt der print 2 mal. Das ist auch bei geladenen Spielständen noch so.
Starte ich aus dieser Doppel-Aufruf-Session heraus ein neues Spiel, gibts den Print 3 mal. Das schaukelt sich anscheinend bis ins unendliche hoch. Und das ist ein Problem.
Wenn man nämlich HookEngine benutzt, um Gewicht zu einer Variablen hinzuzufügen, wird immer das doppelte, dreifache usw. hinzugerechnet. Das verfälscht das komplette System und lässt sich aktuell nur durch ein beenden und wieder öffnen von Gothic beheben.
"Das erinnert doch sehr erfreulich an das, was man sich als Gothicfan wünscht!"
-Korallenkette
Früher konnte/musste man in einer Konstante speichern ob der hook schon ausgeführt wurde (damit es nur genau einmal pro "session"[Zeitraum zwischen Start und Beenden von Gothic] gemacht wird.
Beispiel:
Was heißt denn hier früher? Mittlerweile crasht es nicht mehr, weil es jetzt ein definiertes Verhalten von mehreren Hooks an der selben Stelle gibt, aber es tritt eben genau das auf, was Bisasam beobachtet hat.
Was heißt denn hier früher? Mittlerweile crasht es nicht mehr, weil es jetzt ein definiertes Verhalten von mehreren Hooks an der selben Stelle gibt, aber es tritt eben genau das auf, was Bisasam beobachtet hat.
Vielleicht verstehe ich dich falsch, aber eine selbe Funktion hookt mittlerweile immer nur einmal. Deshalb ist die "once"-If-Bedinginung wie Cryp18Struct sie beschreibt nicht mehr notwendig.
Naja wie man sieht ist es in meiner LeGo-Fassung noch nicht so, dass es nur einmal Hookt.
Dann muss ich wohl mit ner lokalen Variable arbeiten damit er sich nicht verhookt.
Schon interessant zu sehen, dass man lokale Vars für Session-Prüfungen verwenden kann. Ich dachte die werden im Savegame gespeichert und nicht im ganzen Spiel...
"Das erinnert doch sehr erfreulich an das, was man sich als Gothicfan wünscht!"
-Korallenkette
Naja wie man sieht ist es in meiner LeGo-Fassung noch nicht so, dass es nur einmal Hookt.
Dann muss ich wohl mit ner lokalen Variable arbeiten damit er sich nicht verhookt.
Schon interessant zu sehen, dass man lokale Vars für Session-Prüfungen verwenden kann. Ich dachte die werden im Savegame gespeichert und nicht im ganzen Spiel...
Konstanten, nicht Variablen. Variablen verhalten sich genau so wie du es beschreibst.
Zitat von mud-freak
Vielleicht verstehe ich dich falsch, aber eine selbe Funktion hookt mittlerweile immer nur einmal. Deshalb ist die "once"-If-Bedinginung wie Cryp18Struct sie beschreibt nicht mehr notwendig.
Whoops, richtig. Das definierte Verhalten gab es schon früher. Da war ich einen Schritt zu weit in der Vergangenheit :P
Alternativ funktioniert das ulkigerweise auch mit einer lokalen String Variable. Die wird anscheinend auch irgendwo global gespeichert und bei Spielneustart geleert.
Problem ist jetzt behoben, danke
"Das erinnert doch sehr erfreulich an das, was man sich als Gothicfan wünscht!"
-Korallenkette
Alternativ funktioniert das ulkigerweise auch mit einer lokalen String Variable. Die wird anscheinend auch irgendwo global gespeichert und bei Spielneustart geleert.
Problem ist jetzt behoben, danke
Dass du in string Variablen nichts dauerhaft speichern kannst, war dir hoffentlich bereits bekannt / hast du ansonsten vermieden Das gehört vermutlich zum großen Satz von Daedalus-Wissen, den viele Modder, die hier lange mitlesen, bereits haben, der aber nicht unbedingt gut dokumentiert ist.
Für diesen spezifischen Fall sehe ich erst mal nur Nachteile bei Verwendung von string variablen(verbraucht wahrscheinlich mehr Speicher und Vergleiche dürften langsamer sein).
Warum string?
In the last days I dug myself into the world of script modding for Gothic and I love Lego and Ikarus, which allow me do edit my all time favorite and childhood Game.
I would like to share some of my code and would really appreciate your comments and feedback cause I am still learning and doing a lot by trail n error.
Here a small demo of what I got working so far in terms of my learning progress and my goal to implement a fully dynamic HUD for Gothic =)
/////// XP-Bar //////
//Constants
const int oCNpc__OpenScreen_Status = 7592320; // 0x73D980 // HookLen: 7
prototype B4DI_MyXpBar(Bar){
x = Print_Screen[PS_X] / 2;
y = Print_Screen[PS_Y] -20;
barTop = 6;
barLeft = 12;
width = 256;
height = 32;
backTex = "Bar_Back.tga";
barTex = "Bar_XP.tga";
value = 100;
valueMax = 100;
};
//////
instance B4DI_XpBar(B4DI_MyXpBar){
x = 10+128;
y = 20+16;
};
func void B4DI_xpBar_init(){// call in init_Gobal
HookDaedalusFuncS("B_GivePlayerXP", "B4DI_xpBar_update");
HookEngine(oCNpc__OpenScreen_Status, 7 , "B4DI_xpBar_show");
};
func int B4DI_xpBar_create(){//////////------- Commented parts below were an attempt of using just one xp bar and adjusting the animations etc did not work that well <<<--- suggestions?
////preventing overlapping animations but I fear that the frameFunction still continue
//if(Hlp_IsValidHandle(a8_XpBar)) {
// if (!Anim8_Empty(a8_XpBar)){
// Anim8_Delete(a8_XpBar);
// Bar_Delete(XpBar);
// };
//};
var int XpBar;
//if(!Hlp_IsValidHandle(XpBar)) {
XpBar = Bar_Create(B4DI_XpBar);
//var int text_ptr; text_ptr = Print_Ext(100,100, "New Bar Created", FONT_Screen, RGBA(255,0,0,200),1000);
//};
// ------ XP Setup ------
var int level_last; var int exp_lastLvlUp;
level_last = hero.level-1;
if (level_last<0){
level_last =0;
exp_lastLvlUp=0;
}
else{
exp_lastLvlUp = (500*((level_last+2)/2)*(level_last+1));
};
Bar_SetMax(XpBar, hero.exp_next- exp_lastLvlUp);
Bar_SetValue(XpBar, hero.exp - exp_lastLvlUp);
return XpBar;
};
func void B4DI_XpBar_View_ResizePercent(var int hndl, var int percentage ){// Get the basic Settings for scaling from the original
Print_GetScreenSize();
var int ptr; ptr = create(B4DI_XpBar);
var bar bu; bu = MEM_PtrToInst(ptr);
var int buhh; var int buwh;
var int ah; var int aw;
buhh = bu.height / 2;
if(buhh*2 < bu.height){ah = 1;} else {ah = 0;};
if(buwh*2 < bu.width){aw = 1;} else {aw = 0;};
//------------------
if(!Hlp_IsValidHandle(hndl)){ return; };
var _bar b; b = get(hndl);
var zCView v0_ptr; var zCView v1_ptr;
v1_ptr = View_Get(b.v1);
var int sizex_pre; sizex_pre = Print_ToVirtual(b.val,PS_X);
var int sizey_pre; sizey_pre = Print_ToVirtual((buhh-bu.barTop)*2+ah+1,PS_Y); // +1 to compensate rounding errors?
//var int barValueV; barValueV = ((b.value * 1000) / b.valMax)* b.barW) / 1000); //same as Bar_SetValue->BarSetPromille
//scale on all axis
//View_Resize(b.v1, sizex_pre * percentage /100, sizey_pre * percentage / 100 );
//scale just on y axis
View_Resize(b.v1, sizex_pre , sizey_pre * percentage / 100 );
var int posDifY; posDifY = (v1_ptr.vsizey - sizey_pre)/2;
if (posDifY>0){
posDifY *= -1;
};
var int compenstedTargetY;
compenstedTargetY = Print_ToVirtual(bu.y-buhh+bu.barTop+ah+1, PS_Y) + posDifY; // +1 to compensate rounding errors?
//y -= v.vsizey>>1;//y -= v.vposy;
View_MoveTo(b.v1, v1_ptr.vposx, compenstedTargetY );
free(ptr, B4DI_XpBar);
};
func void B4DI_Bar_fadeOut(var int bar){
var int a8_XpBar; a8_XpBar = Anim8_NewExt(255, Bar_SetAlpha, bar, false);
Anim8_RemoveIfEmpty(a8_XpBar, true);
Anim8_RemoveDataIfEmpty(a8_XpBar, true);
Anim8 (a8_XpBar, 255, 5000, A8_Wait);
Anim8q(a8_XpBar, 0, 2000, A8_SlowEnd);
};
func void B4DI_Bar_pulse(var int bar){
var int a8_XpBar_pulse; a8_XpBar_pulse = Anim8_NewExt(100 , B4DI_XpBar_View_ResizePercent, bar, false); //height input
Anim8_RemoveIfEmpty(a8_XpBar_pulse, true);
Anim8_RemoveDataIfEmpty(a8_XpBar_pulse, false);
Anim8 (a8_XpBar_pulse, 100, 100, A8_Wait);
Anim8q(a8_XpBar_pulse, 150, 200, A8_SlowEnd);
Anim8q(a8_XpBar_pulse, 100, 100, A8_SlowStart);
};
func void B4DI_xpBar_show(){
var int XpBar; XpBar = B4DI_xpBar_create();
B4DI_Bar_fadeOut(XpBar);
Bar_Show(XpBar);
};
func void B4DI_xpBar_update(){
ContinueCall();
var int XpBar; XpBar = B4DI_xpBar_create();
B4DI_Bar_fadeOut(XpBar);
B4DI_Bar_pulse(XpBar);
};
Had to add a new value to the _bar of LeGo's Bars.d
Spoiler:(zum lesen bitte Text markieren)
Code:
/////// bars.d ////////
class _bar {
....
var int val; // <------
.....
};
func void Bar_SetPromille(var int bar, var int pro){
...
b.val = Print_ToPixel((pro * b.barW) / 1000, PS_X); //to keep both valMax | val in the same dim
...
};
Thank you for your Feedback =)
PS: How can I enable Syntax Highlighting in the forums code Block?
Edit: Styled code
Geändert von blood4ng3l (23.07.2019 um 07:27 Uhr)
Grund: removed double code
In the last days I dug myself into the world of script modding for Gothic and I love Lego and Ikarus, which allow me do edit my all time favorite and childhood Game.
I would like to share some of my code and would really appreciate your comments and feedback cause I am still learning and doing a lot by trail n error.
Here a small demo of what I got working so far in terms of my learning progress and my goal to implement a fully dynamic HUD for Gothic =)
/////// XP-Bar //////
//Constants
const int oCNpc__OpenScreen_Status = 7592320; // 0x73D980 // HookLen: 7
prototype B4DI_MyXpBar(Bar) {
x = Print_Screen[PS_X] / 2;
y = Print_Screen[PS_Y] -20;
barTop = 6;
barLeft = 12;
width = 256;
height = 32;
backTex = "Bar_Back.tga";
barTex = "Bar_XP.tga";
value = 100;
valueMax = 100;
};
//////
instance B4DI_XpBar(B4DI_MyXpBar){
x = 10+128;
y = 20+16;
};
func void B4DI_xpBar_init() {
// call in init_Gobal
HookDaedalusFuncS("B_GivePlayerXP", "B4DI_xpBar_update");
HookEngine(oCNpc__OpenScreen_Status, 7 , "B4DI_xpBar_show");
};
func int B4DI_xpBar_create(){
//////////------- Commented parts below were an attempt of using just one xp bar and adjusting the animations etc did not work that well <<<--- suggestions?
////preventing overlapping animations but I fear that the frameFunction still continue
//if(Hlp_IsValidHandle(a8_XpBar)) {
// if (!Anim8_Empty(a8_XpBar)){
// Anim8_Delete(a8_XpBar);
// Bar_Delete(XpBar);
// };
//};
var int XpBar;
//if(!Hlp_IsValidHandle(XpBar)) {
XpBar = Bar_Create(B4DI_XpBar);
//var int text_ptr; text_ptr = Print_Ext(100,100, "New Bar Created", FONT_Screen, RGBA(255,0,0,200),1000);
//};
// ------ XP Setup ------
var int level_last; var int exp_lastLvlUp;
level_last = hero.level-1;
if (level_last<0){
level_last =0;
exp_lastLvlUp=0;
}
else{
exp_lastLvlUp = (500*((level_last+2)/2)*(level_last+1));
};
Bar_SetMax(XpBar, hero.exp_next- exp_lastLvlUp);
Bar_SetValue(XpBar, hero.exp - exp_lastLvlUp);
return XpBar;
};
func void B4DI_XpBar_View_ResizePercent(var int hndl, var int percentage ) {
// Get the basic Settings for scaling from the original
Print_GetScreenSize();
var int ptr; ptr = create(B4DI_XpBar);
var bar bu; bu = MEM_PtrToInst(ptr);
var int buhh; var int buwh;
var int ah; var int aw;
buhh = bu.height / 2;
if(buhh*2 < bu.height) {ah = 1;} else {ah = 0;};
if(buwh*2 < bu.width) {aw = 1;} else {aw = 0;};
//------------------
if(!Hlp_IsValidHandle(hndl)) { return; };
var _bar b; b = get(hndl);
var zCView v0_ptr; var zCView v1_ptr;
v1_ptr = View_Get(b.v1);
var int sizex_pre; sizex_pre = Print_ToVirtual(b.val,PS_X);
var int sizey_pre; sizey_pre = Print_ToVirtual((buhh-bu.barTop)*2+ah+1,PS_Y); // +1 to compensate rounding errors?
//var int barValueV; barValueV = ((b.value * 1000) / b.valMax)* b.barW) / 1000); //same as Bar_SetValue->BarSetPromille
//scale on all axis
//View_Resize(b.v1, sizex_pre * percentage /100, sizey_pre * percentage / 100 );
//scale just on y axis
View_Resize(b.v1, sizex_pre , sizey_pre * percentage / 100 );
var int posDifY; posDifY = (v1_ptr.vsizey - sizey_pre)/2;
if (posDifY>0){
posDifY *= -1;
};
var int compenstedTargetY;
compenstedTargetY = Print_ToVirtual(bu.y-buhh+bu.barTop+ah+1, PS_Y) + posDifY; // +1 to compensate rounding errors?
//y -= v.vsizey>>1;//y -= v.vposy;
View_MoveTo(b.v1, v1_ptr.vposx, compenstedTargetY );
free(ptr, B4DI_XpBar);
};
func void B4DI_Bar_fadeOut(var int bar) {
var int a8_XpBar; a8_XpBar = Anim8_NewExt(255, Bar_SetAlpha, bar, false);
Anim8_RemoveIfEmpty(a8_XpBar, true);
Anim8_RemoveDataIfEmpty(a8_XpBar, true);
Anim8 (a8_XpBar, 255, 5000, A8_Wait);
Anim8q(a8_XpBar, 0, 2000, A8_SlowEnd);
};
func void B4DI_Bar_pulse(var int bar) {
var int a8_XpBar_pulse; a8_XpBar_pulse = Anim8_NewExt(100 , B4DI_XpBar_View_ResizePercent, bar, false); //height input
Anim8_RemoveIfEmpty(a8_XpBar_pulse, true);
Anim8_RemoveDataIfEmpty(a8_XpBar_pulse, false);
Anim8 (a8_XpBar_pulse, 100, 100, A8_Wait);
Anim8q(a8_XpBar_pulse, 150, 200, A8_SlowEnd);
Anim8q(a8_XpBar_pulse, 100, 100, A8_SlowStart);
};
func void B4DI_xpBar_show(){
var int XpBar; XpBar = B4DI_xpBar_create();
B4DI_Bar_fadeOut(XpBar);
Bar_Show(XpBar);
};
func void B4DI_xpBar_update() {
ContinueCall();
var int XpBar; XpBar = B4DI_xpBar_create();
B4DI_Bar_fadeOut(XpBar);
B4DI_Bar_pulse(XpBar);
};
Had to add a new value to the _bar of LeGo's Bars.d
Spoiler:(zum lesen bitte Text markieren)
Code:
/////// bars.d ////////
class _bar {
....
var int val; // <------
.....
};
func void Bar_SetPromille(var int bar, var int pro) {
...
b.val = Print_ToPixel((pro * b.barW) / 1000, PS_X); //to keep both valMax | val in the same dim
...
};
Thank you for your Feedback =)
PS: How can I enable Syntax Highlighting in the forums code Block?
Hello. How can I check whether the NPC is AIV_PARTYMEMBER. I looked that Ikarus has aivscript, but that in it, there is no description. I want to check in the highlighting of the name of the NPS whether he is a member or not.
There is no need for any special Ikarus / LeGo Code to check that.
Code:
if oth.aivar[AIV_PARTYMEMBER] == true
{
// set color
};
(oth is set to be the C_NPC in the hero's focus in the _Focusnames function)
Instead of asking ==true you can also just do
Code:
if oth.aivar[AIV_PARTYMEMBER]
The fact of the matter is that I tried it like that, I got an error that there is no such aivar, or something like that, I checked it for a long time and forgot
The fact of the matter is that I tried it like that, I got an error that there is no such aivar, or something like that, I checked it for a long time and forgot
In LoA at least, we have this in there:
Code:
if (oth.aivar[AIV_PARTYMEMBER])
{
col = Focusnames_Color_Friendly();
};
Maybe your code is missing the definition of other or the definition of AIV_PARTYMEMBER? The latter would be strange, I'm fairly sure that it's part of the original game. You might need to show your code (_Focusnames) and the exact error message unless someone else has a better working crystal ball than I do...
if (oth.aivar[AIV_PARTYMEMBER])
{
col = Focusnames_Color_Friendly();
};
Maybe your code is missing the definition of other or the definition of AIV_PARTYMEMBER? The latter would be strange, I'm fairly sure that it's part of the original game. You might need to show your code (_Focusnames) and the exact error message unless someone else has a better working crystal ball than I do...
Ok, tomorrow I'll show you a piece of code and a zSpy error message.
edit.
The first trial.
Code:
if(Npc_IsInState(oth,ZS_Guide_Player)) {
if(oth.aivar[AIV_PARTYMEMBER] == TRUE)
{
col = Focusnames_Color_Friendly();
}
else
{
col = Focusnames_Color_Neutral();
};
if((_@(other) == _@(hero)) && Npc_IsInState(oth,ZS_Attack))
{
col = Focusnames_Color_Hostile();
};
}
zsPy
00:14 Fatal:-1 U:PAR: Unknown identifier : AIV_PARTYMEMBER ( line 81 ) .... <zParser.cpp,#599>
00:40 Fatal:-1 U:PAR: \_WORK\DATA\SCRIPTS\CONTENT\LEGO\FOCUSNAMES.D: Buuuuhhhh, Pfiffe, Unmut : Array-Index out of Range : C_NPC.AIVAR[ -1 ] .... <zParser.cpp,#599>
Second trial.
Code:
if(Npc_IsInState(oth,ZS_Guide_Player))
{
if(oth.aivar[AIV_PARTYMEMBER])
{
col = Focusnames_Color_Friendly();
}
else
{
col = Focusnames_Color_Neutral();
};
if((_@(other) == _@(hero)) && Npc_IsInState(oth,ZS_Attack))
{
col = Focusnames_Color_Hostile();
};
}
00:12 Fatal:-1 U:PAR: Unknown identifier : AIV_PARTYMEMBER ( line 81 ) .... <zParser.cpp,#599>
00:32 Fatal:-1 U:PAR: \_WORK\DATA\SCRIPTS\CONTENT\LEGO\FOCUSNAMES.D: Buuuuhhhh, Pfiffe, Unmut : Array-Index out of Range : C_NPC.AIVAR[ -1 ] .... <zParser.cpp,#599>
It looks like you are using the constant AIV_PARTYMEMBER before it has been defined.
Check your Gothic.src file, there you need to specify that gothic should parse the file AI_CONSTANTS.d(contains definition of AIV_XXX constants) before parsing the LeGo .d files:
Check your Gothic.src file, there you need to specify that gothic should parse the file AI_CONSTANTS.d(contains definition of AIV_XXX constants) before parsing the LeGo .d files: