Archiv verlassen und diese Seite im Standarddesign anzeigen : [Skriptpaket] LeGo #3
GiftGrün
26.08.2016, 14:18
Ja, es ist letztlich doch immer "Haben Sie das Gerät schon einmal aus- und wieder angeschaltet?". :D
Danke für die freundliche und schnelle Hilfe! :)
GiftGrün
26.08.2016, 14:38
Es könnte möglicherweise doch ein Bug sein. Kann das natürlich schlecht beurteilen, aber hier mal, was ich jetzt rausgefunden habe:
LeGo_Cursor behebt den Crash, weil in LeGo.d die Zeile Cursor_Event = Event_Create(); steht. Alles andere an Cursor (eigentlich nur der Hook) konnte ich auskommentieren und es startete ohne Probleme. Andererseits crasht es, wenn ich diese eine Zeile auskommentiere, oder wenn ich Cursor durch seine Abhängigkeiten, LeGo_View und LeGo_Interface ersetze. Es startet aber ohne Probleme, wenn ich irgendeine int-Konstante hernehme und sie mit Event_Create(); befülle. Oder wenn ich stattdessen dieselbe Konstante mit new(_Button@); befülle (übrigens ist new(PC_Hero); eine sehr, sehr schlechte Idee). §ugly
Der Crash passiert auch unabhängig davon, ob ich die Einträge meiner Ikarus- und LeGo-Funktionen in der Startup auskommentiere oder nicht.
Da Event_Create() tatsächlich nur ein new() aufruft (Was du sicherlich nachgeschaut hast), ist das höchst verwirrend (und vermutlich Zeuge irgendeines tieferliegendes Problems).
Mit dem Hinweis hab ich allerdings auch den Fehler reproduzieren können: Wenn ich in der Startup keine weiteren Handles befülle, crasht es bei mir mit LeGo_Init(LeGo_FrameFunctions) nämlich ebenfalls. Ich geh dem mal auf den Grund und melde mich hier.
GiftGrün
26.08.2016, 15:53
Ja, ich habe auch noch locals(); und _HT_Create(); auf den beiden Variablen in new();, HandlesPointer und HandlesInstance, ausprobiert und es crasht jedes Mal, an denen sollte es also auch nicht liegen. Aber mehr als eine Art Stacktrace kann ich wahrscheinlich sowieso nicht mehr beitragen, außer dir viel Glück beim Debugging zu wünschen!
piootrek86
26.08.2016, 19:53
Hi,
I know it is unfinished yet but just to give you informations what is going on with it.
I added to startup :
LeGo_Init (LeGo_All | LeGo_Buffs);
then in my skripts added:
instance deadly_poison(lCBuff) {
name = "Tödliches Gift";
bufftype = BUFF_BAD;
durationMS = 10*1000; //10 sekund dlugie
tickMS = 1000; // Jedna sekunda
onTick = SAVE_GetFuncID(deadly_poison_damage);
buffTex = "POISON.TGA";
};
and
func void deadly_poison_damage(var int bh) {
var int ptr; ptr = Buff_GetNpc(bh);
if (!ptr) {
return;
}; // Kann passieren, falls z.B. die Welt gewechselt wurde
var c_npc n; n = _^(ptr);
Npc_ChangeAttribute(n, ATR_HITPOINTS, -3); // 3 Schaden
};
it was all from example on lego wikipedia.
To check if it works I added to Num_1 button :
f(MEM_KeyState(KEY_NUMPAD1)==KEY_PRESSED )
{
Buff_Apply (hero, deadly_poison);
};
but it crash straight away :
00:23 Info: 3 B: OPT: Model-Details: Value=1 .... <oGameManager.cpp,#1294>
00:23 Info: 3 B: OPT: Blood-Details: Value=2 .... <oGameManager.cpp,#1302>
00:23 Info: 3 B: GMAN: Leaving Menu-Section .... <oGameManager.cpp,#1537>
00:23 Info: 4 B: GMAN: Close InitScreen .... <oGameManager.cpp,#849>
00:23 Info: 0 Q: Locals: Install at DELETE
00:23 Info: 0 Q: Offset is 11
00:25 Info: 3 D: MSH: Loading Mesh: MAGICFRONTIER_OUT.MSH .... <zMesh.cpp,#3445>
00:27 Fault: 0 Q: [start of stacktrace]
00:27 Fault: 0 Q: MEM_READINT_() + 13 bytes
00:27 Fault: 0 Q: MEM_READINTARRAY(342245428, 5866) + 34 bytes
00:27 Fault: 0 Q: MEM_SETUSEINSTANCE(339786924) + 21 bytes
00:27 Fault: 0 Q: SAVE_GETFUNCID(DEADLY_POISON_DAMAGE) + 43 bytes
00:27 Fault: 0 Q: INIT_DRAGONISLAND() + 132 bytes
00:27 Fault: 0 Q: [end of stacktrace]
00:27 Fault: 0 Q: Exception handler was invoked. Ikarus tried to print a Daedalus-Stacktrace to zSpy. Gothic will now crash and probably give you a stacktrace of its own.
if I remove OnTick option than game does not crash but apart from having icon on the screen for 10seks nothing is happening.
Any idea why? Am I doing something wrong? :)
Thanks
Thanks for posting here and welcome to our little forum :)
Now that I fixed GiftGrün's bug, I'll have a look.
Ja, ich habe auch noch locals(); und _HT_Create(); auf den beiden Variablen in new();, HandlesPointer und HandlesInstance, ausprobiert und es crasht jedes Mal, an denen sollte es also auch nicht liegen. Aber mehr als eine Art Stacktrace kann ich wahrscheinlich sowieso nicht mehr beitragen, außer dir viel Glück beim Debugging zu wünschen!
Habe den Bug jetzt gefunden und behoben - er trat auf, wenn man im Zuge der Initialisierung kein einziges Handle erzeugt hat. In der nächsten Version werden die entsprechenden Sachen dann auch ohne new() richtig initialisiert.
LeGo 2.3.6 (https://subversion.assembla.com/svn/lego2/Release/LeGo_2_3_6.7z)
Fixed an error that prevented parsing in Names.d
Fixed a bug concerning initialization order (happened if you never created a handle during startup)
Fixed a small bug in View.d (thanks to mud-freak)
Changed MEM_SetUseInstance() to expect a zCPar_Symbol* instead of a symbolID.
Note: Although I fixed a bug with the buffs, both Buffs.d and Render.d are not yet included in LeGo_All.
@Piootrek86: Now your scripts should work :)
piootrek86
31.08.2016, 19:51
LeGo 2.3.6 (https://subversion.assembla.com/svn/lego2/Release/LeGo_2_3_6.7z)
Fixed an error that prevented parsing in Names.d
Fixed a bug concerning initialization order (happened if you never created a handle during startup)
Fixed a small bug in View.d (thanks to mud-freak)
Changed MEM_SetUseInstance() to expect a zCPar_Symbol* instead of a symbolID.
Note: Although I fixed a bug with the buffs, both Buffs.d and Render.d are not yet included in LeGo_All.
@Piootrek86: Now your scripts should work :)
Thank you Lehona :)
piootrek86
01.09.2016, 14:22
Hi it is me again :D I am having problem with hidding my bar same as hp bar and mp bar hide when you press esc or c. this is bar script:
instance Bar_ST (GothicBar) { x = Print_Screen[PS_X]-920 ; //lewo prawo 100
y = Print_Screen[PS_Y]-85 ; //gora dol hp ma 580 - 540
} ;
func void Bar_ST_Loop ( ) {
// Example_1 könnte damit zB. in Init_Global aufgerufen werden
FF_ApplyOnce (Loop_1) ;
} ;
func void Loop_1 ( ) {
// Example_1 bringt diese Schleife zum laufen. Hier soll die Bar einmalig konstruiert und dann an die EXP des Helden angepasst werden:
var int MyBar_ST ;
if ( ! Hlp_IsValidHandle (MyBar_ST) ) {
MyBar_ST = Bar_Create (Bar_ST) ; // Unsere Bar_1
} ;
// Der Rest ist wohl selbsterklärend:
Bar_SetMax (MyBar_ST , stamina_max ) ;
Bar_SetValue (MyBar_ST , stamina ) ;
} ;
func void HideBar_ST ()
{
if (MEM_Game.pause_screen || !InfoManager_HasFinished())
{
Bar_Delete (Bar_ST);
Bar_Hide (Bar_ST);
}
else
{
Loop_1();
};
};
but it doesn't work as my bar stays on the screen when I pause a game :( Can someone help me please? What I do wrong? :D
mud-freak
01.09.2016, 14:29
If I understood your code correctly (the formatting makes the code hard to read), you never call HideBar_ST but always call Loop_1 directly. This little fix should help.
instance Bar_ST (GothicBar) { x = Print_Screen[PS_X]-920 ; //lewo prawo 100
y = Print_Screen[PS_Y]-85 ; //gora dol hp ma 580 - 540
} ;
func void Bar_ST_Loop ( ) {
// Example_1 könnte damit zB. in Init_Global aufgerufen werden
FF_ApplyOnce (Loop_1HideBar_ST) ;
} ;
func void Loop_1 ( ) {
// Example_1 bringt diese Schleife zum laufen. Hier soll die Bar einmalig konstruiert und dann an die EXP des Helden angepasst werden:
var int MyBar_ST ;
if ( ! Hlp_IsValidHandle (MyBar_ST) ) {
MyBar_ST = Bar_Create (Bar_ST) ; // Unsere Bar_1
} ;
// Der Rest ist wohl selbsterklärend:
Bar_SetMax (MyBar_ST , stamina_max ) ;
Bar_SetValue (MyBar_ST , stamina ) ;
} ;
func void HideBar_ST ()
{
if (MEM_Game.pause_screen || !InfoManager_HasFinished())
{
Bar_Delete (Bar_ST);
Bar_Hide (Bar_ST);
}
else
{
Loop_1();
};
};
piootrek86
01.09.2016, 14:39
If I understood your code correctly (the formatting makes the code hard to read), you never call HideBar_ST but always call Loop_1 directly. This little fix should help.
instance Bar_ST (GothicBar) { x = Print_Screen[PS_X]-920 ; //lewo prawo 100
y = Print_Screen[PS_Y]-85 ; //gora dol hp ma 580 - 540
} ;
func void Bar_ST_Loop ( ) {
// Example_1 könnte damit zB. in Init_Global aufgerufen werden
FF_ApplyOnce (Loop_1HideBar_ST) ;
} ;
func void Loop_1 ( ) {
// Example_1 bringt diese Schleife zum laufen. Hier soll die Bar einmalig konstruiert und dann an die EXP des Helden angepasst werden:
var int MyBar_ST ;
if ( ! Hlp_IsValidHandle (MyBar_ST) ) {
MyBar_ST = Bar_Create (Bar_ST) ; // Unsere Bar_1
} ;
// Der Rest ist wohl selbsterklärend:
Bar_SetMax (MyBar_ST , stamina_max ) ;
Bar_SetValue (MyBar_ST , stamina ) ;
} ;
func void HideBar_ST ()
{
if (MEM_Game.pause_screen || !InfoManager_HasFinished())
{
Bar_Delete (Bar_ST);
Bar_Hide (Bar_ST);
}
else
{
Loop_1();
};
};
I've changed it but didn't help my bar still is not hiding. I did try to call HideBar_ST function by adding FF to startup.d and call it every 1sec but without success :(
mud-freak
01.09.2016, 14:54
I've changed it but didn't help my bar still is not hiding. I did try to call HideBar_ST function by adding FF to startup.d and call it every 1sec but without success :(
I have never worked with LeGo Bars, but Bar_Delete and Bar_Hide seem to expect handles like Bar_SetMax and Bar_SetValue do (MyBar_ST).
EDIT: Try it like this (untested).
instance Bar_ST(GothicBar) {
x = Print_Screen[PS_X]-920 ; //lewo prawo 100
y = Print_Screen[PS_Y]-85 ; //gora dol hp ma 580 - 540
};
func void Bar_ST_Loop() {
// Example_1 könnte damit zB. in Init_Global aufgerufen werden
FF_ApplyOnce(Loop_1);
};
func void Loop_1() {
// Example_1 bringt diese Schleife zum laufen. Hier soll die Bar einmalig konstruiert und dann an die EXP des Helden angepasst werden:
var int MyBar_ST ;
if (!Hlp_IsValidHandle(MyBar_ST)) {
MyBar_ST = Bar_Create(Bar_ST); // Unsere Bar_1
};
// Der Rest ist wohl selbsterklärend:
Bar_SetMax(MyBar_ST, stamina_max);
Bar_SetValue(MyBar_ST, stamina);
if (MEM_Game.pause_screen || !InfoManager_HasFinished()) {
Bar_Delete(MyBar_ST);
Bar_Hide(MyBar_ST);
};
};
func void HideBar_ST() {
if (MEM_Game.pause_screen || !InfoManager_HasFinished()) {
Bar_Delete (Bar_ST);
Bar_Hide (Bar_ST);
} else {
Loop_1();
};
};
This is not the most optimal way to do it. But it should work.
Btw: I don't think you need to delete AND hide the bar.
piootrek86
01.09.2016, 15:12
I have never worked with LeGo Bars, but Bar_Delete and Bar_Hide seem to expect handles like Bar_SetMax and Bar_SetValue do (MyBar_ST).
EDIT: Try it like this (untested).
instance Bar_ST(GothicBar) {
x = Print_Screen[PS_X]-920 ; //lewo prawo 100
y = Print_Screen[PS_Y]-85 ; //gora dol hp ma 580 - 540
};
func void Bar_ST_Loop() {
// Example_1 könnte damit zB. in Init_Global aufgerufen werden
FF_ApplyOnce(Loop_1);
};
func void Loop_1() {
// Example_1 bringt diese Schleife zum laufen. Hier soll die Bar einmalig konstruiert und dann an die EXP des Helden angepasst werden:
var int MyBar_ST ;
if (!Hlp_IsValidHandle(MyBar_ST)) {
MyBar_ST = Bar_Create(Bar_ST); // Unsere Bar_1
};
// Der Rest ist wohl selbsterklärend:
Bar_SetMax(MyBar_ST, stamina_max);
Bar_SetValue(MyBar_ST, stamina);
if (MEM_Game.pause_screen || !InfoManager_HasFinished()) {
Bar_Delete(MyBar_ST);
Bar_Hide(MyBar_ST);
};
};
func void HideBar_ST() {
if (MEM_Game.pause_screen || !InfoManager_HasFinished()) {
Bar_Delete (Bar_ST);
Bar_Hide (Bar_ST);
} else {
Loop_1();
};
};
This is not the most optimal way to do it. But it should work.
Btw: I don't think you need to delete AND hide the bar.
Brilliant mate :) You are awsome! It does work now :) Thank you
Since update 2.3.5 packet Render doesn't work. Unless I messed something...
var int render; render = Render_AddItem (ItMw_1H_Sld_Sword, 2048, 2048, 4096, 4096);
Render_OpenView (render);
Since update 2.3.5 packet Render doesn't work. Unless I messed something...
var int render; render = Render_AddItem (ItMw_1H_Sld_Sword, 2048, 2048, 4096, 4096);
Render_OpenView (render);
Since 2.3.5 render.d needs to be initialized explicitly (i.e. LeGo_Init(LeGo_All | LeGo_Render)), which is probably causing the problem. The same is true for buffs, by the way.
The reason for this is that they're not tested properly yet, so they can be considered "experimental".
So that's it. Thank you for reply :gratz
mud-freak
03.09.2016, 16:46
...
The engine should do this exactly like this. Maybe you can find the engine function where it scales the mouse input when turning the player model for example.
...
Regarding the frame lock for the cursor: I found an engine function where Gothic implements a frame lock. (I am now using that function for free aming.)
If someone is interested in implementing a frame lock for the cursor, check out this address: 0x6AE84F
If I am not mistaken, one could work with MEM_Timer.factorMotion.
In my opinion a frame lock for the cursor would be a good idea; the fps fluctuate a lot in newer mods that are using the grafic enhancements.
I guess that is what Siemekk was experiencing here.
Wegen des Framelocks für den Cursor: Ich habe eine Enginefunktion gefunden in der Gothic einen Framelock implementiert. (Die Funtkion benutze ich jetzt für mein freies Zielen.)
Wenn sich jemand dafür interessiert den Cursor mit einem Framelock zu versehen sollte bei folgender Adresse vorbeischauen: 0x6AE84F
Wenn ich mich nicht verlesen habe, kann man sich dazu an MEM_Timer.factorMotion orientieren.
Meiner Meinung nach, wäre ein Framelock für den Cursor eine gute Idee; die FPS schwanken bei den neueren Mods wegen der Grafik-mods mehr.
Ich denke das war das Problem von dem Siemekk hier berichtete.
GiftGrün
03.09.2016, 19:03
Was genau meinst du mit Framelock? Dass der Cursor innerhalb eines vorgegebenen Rahmens bleibt? Dass die FPS-Zahl beschränkt wird? Habs gegoogled und nur Einhandmesser und nVidia frame synchronisation gefunden.
mud-freak
03.09.2016, 19:14
Was genau meinst du mit Framelock? Dass der Cursor innerhalb eines vorgegebenen Rahmens bleibt? Dass die FPS-Zahl beschränkt wird? Habs gegoogled und nur Einhandmesser und nVidia frame synchronisation gefunden.
Damit meine ich, dass der Cursor sich immer gleich schnell bewegt unabhängig von den FPS.
Siemekk hatte scheinbar das Problem, dass seine Welt (an einigen Stellen) so voll war und die FPS deshalb so tief gesunken sind, dass sich sein Cursor verhältnismässig langsam bewegte.
Das gleiche Problem hatte ich mit meiner alternativen Rotation des Spielers beim freien Zielen. Waren die FPS hoch drehte sich das Spielermodel wesentlich schneller, als wenn die FPS tief waren. Ich selbst habe dann einfach direkt die "offizielle" Enginefunktion zum Drehen des Spielers benutzt.
Beim Cursor besteht das Problem weiterhin.
GiftGrün
04.09.2016, 11:10
Mit Framefunctions und Timer könnte man ja den Kehrwert der FPS, die ms/Frame bestimmen, und damit dann auch eine Näherung an die aktuellen FPS, oder?
Dann könnte man den multiplier aus deinem vorigen Beitrag ja variabel von den FPS abhängig machen, nicht?
Mit Framefunctions und Timer könnte man ja den Kehrwert der FPS, die ms/Frame bestimmen, und damit dann auch eine Näherung an die aktuellen FPS, oder?
Dann könnte man den multiplier aus deinem vorigen Beitrag ja variabel von den FPS abhängig machen, nicht?
Das ist viel zu kompliziert, Gothic kennt ja die Zeit zwischen zwei Frames. Es existiert MEM_Timer.frameTimeFloat, da muss man nichts selber berechnen ;)
Ich habe das Problem mit dem Cursor zwar noch nicht gehabt, allerdings sind unsere Testumgebungen typischerweise auch eher karg, da geht kein Rechner an seine Grenzen :p Ich werde mir das in Zukunft mal anschauen.
GiftGrün
04.09.2016, 15:50
Okay, ein seltsamer Bug:
LeGo_Init(LeGo_Cursor); macht AI_PrintScreen kaputt (es printet nichts), PrintScreen und Print funktionieren weiterhin, mit LeGo_Init(LeGo_All); funktioniert hingegen alles super.
Z.B. steht bei Ignaz dann nicht mehr "Spruchrolle gegeben", oder beim Charakterhelper bei der Option "zeig mal dingens" steht auch nichts.
Ohne LeGo_Init oder ohne LeGo_Cursor zu initialisieren funktioniert auch alles super.
Okay, ein seltsamer Bug:
LeGo_Init(LeGo_Cursor); macht AI_PrintScreen kaputt (es printet nichts), PrintScreen und Print funktionieren weiterhin, mit LeGo_Init(LeGo_All); funktioniert hingegen alles super.
Z.B. steht bei Ignaz dann nicht mehr "Spruchrolle gegeben", oder beim Charakterhelper bei der Option "zeig mal dingens" steht auch nichts.
Ohne LeGo_Init oder ohne LeGo_Cursor zu initialisieren funktioniert auch alles super.
Good catch ;)
In der LeGo.d Zeile 68 durch folgendes ersetzen:
if(f & LeGo_Interface) { f = f | LeGo_PermMem | LeGo_AI_Function; };
Kannst natürlich auch einfach LeGo_Init(LeGo_Cursor | LeGo_AI_Function); aufrufen (anstatt nur den Cursor zu intialisieren).
Ist dann im nächsten Release enthalten.
Hello §wink
It is me, again :)
I try move view with Anim8. Problem is it that view was not moving :I
I bet that problem is CALL to this adres:
8025824 [decimal]
How i can move this view with Anim8?
My old code look like this:
var int BoxAnim;
BoxAnim = Anim8_NewExt(4000, MsgBoxMove, view, false);
Anim8_RemoveIfEmpty(BoxAnim, true);
Anim8_RemoveDataIfEmpty(BoxAnim, true);
Anim8 (BoxAnim, 4000, time*15, A8_SlowStart);
Anim8q (BoxAnim, 4500, time*15, A8_Wait);
Anim8q (BoxAnim, 5500, time*15, A8_SlowEnd);
func void MsgBoxMove(var int hndl, var int value)
{
var zCView view; view = get(hndl);
view.vposy = value;
};
You should use View_Move() or View_MoveTo() to move a view, simply setting coordinate values is not enough (apparently).
func void MsgBoxMove(var int hndl, var int value)
{
var zCView view; view = get(hndl);
View_MoveTo(hndl, view.vposx, value);
};
As I just noticed, you can also use "-1" instead of view.vposx to not move the view along that coordinate, a nice timesaver ;)
Your code doesn't seem to make very much sense, by the way.
For the first time*15 youre interpolating from 4000 to 4000 (so effectilvely, not at all). Then you go up to 4500, but use A8_Wait, which does nothing and ignores the target value (i.e. it stays at 4000).
Hello. Recently I ported newest LeGo to Gothic 1. While everything seems to work just fine, I get this message on new game (did not test anything else)
This should never happen! If it does anyway, please report to Lehona on WorldOfGothic.
Anyway, I wasn't in the mood to check why it happens and kind of hoped for quick answer here. :D Is there something I should be worried about ?
Hello. Recently I ported newest LeGo to Gothic 1. While everything seems to work just fine, I get this message on new game (did not test anything else)
Anyway, I wasn't in the mood to check why it happens and kind of hoped for quick answer here. :D Is there something I should be worried about ?
Before 2.3.5, PermMem initialized itself when new() was called, thus having to check every time whether it has been initialized already. That's a performance cost and generally rather "untidy" code. It also meant that PermMem was in an invalid state when no new() has been called, which affected the frame functions and caused a crash on the first frame of the game if there was no new() during startup.
I moved the initialization into LeGo_Init() and put that warning into place to make sure I covered every initialization case (it's surprisingly tricky).
I didn't get any warnings at all during testing (at least with the final build), so I assume you've made a mistake with your port?
Right, I was porting while on the train, and now I remembered, I skipped whole Lego.d file because I was lazy to do some IDA work. :p So there was the whole new inicialization thing to be ported. :D Hopefully it will work now. Anyway, is there any official G1 version, or you do not wish to mess with that :D .
Hello §wink
I have a question . It is possible to insert NPC giving the trafo-positions ?
Example:
Wld_InsertNpcTrafo(int instance, int x, int y, int z) ?
Although I remember a function like this, apparently it's not in LeGo and I don't know where else to search :p
You can do it yourself, though. Use MEM_GetAnyWPptr() (LeGo) to get a pointer to a WayPoint, move it to the desired coordinates and use WLD_InsertNpc().
You should probably move the WP back, too :)
mud-freak
22.09.2016, 18:26
Die Möglichkeit neue Konsolenbefehle hinzuzufügen gibt es ja schon seit Version 1.1.2 von Ikarus (siehe hier). Leider wurde mit der Implementierung immer zurückgegeben, der Befehl sei unbekannt (Unknown Command : NEUERBEFEHL) und Autovervollständigung war dort auch nicht unterstützt. Zu dem war es vor allem sehr umständlich einzelne neue Konsolenbefehle hinzuzufügen.
Mir gefällt der Aufbau und die simple Funktionsweise der FrameFunctions sehr. Da ich für eigene Zwecke einige Konsolenbefehle eingebaut und dabei eine hübschere Variante entwickelt habe (inkl. Autovervollständigung und anpassbarer Rückgabe auf der Konsole), habe ich mir gedacht, sie wie die FrameFunctions einzubauen.
Ich finde es sehr praktisch, da es mir das Debugging extrem vereinfacht, denn ein neuer Konsolenbefehl ist (wie bei den FrameFunctions) mit einer Zeile eingebaut.
Vielleicht findet das Skript hier seinen Weg in die nächste LeGo Version. (Mit dem im Hinterkopf habe ich das Skript dem Format des FrameFunctions-Skripts sehr angeglichen).
Hier zwei Beispiele und Screenshots:
Einfaches Beispiel:
44816
44817
Nützliches Beispiel:
44818
///////////////
// EXAMPLE 1 //
///////////////
CC_Register(regConsoleFunc01, "are you ", "answers a simple question");
/* This function is called when the command is entered */
func string regConsoleFunc01(var string command) { // command holds anything entered after the registered command prefix
if (Hlp_StrCmp(command, STR_Upper("cool"))) {
return "Yes, I am.";
} else {
return "No, I am cool.";
};
};
///////////////
// EXAMPLE 2 //
///////////////
CC_Register(regConsoleFunc, "debug weakspot", "turn debug visualization on/off");
/* Console function to enable/disable weak spot debug output */
func string regConsoleFunc(var string command) {
FREEAIM_DEBUG_WEAKSPOT = !FREEAIM_DEBUG_WEAKSPOT;
if (FREEAIM_DEBUG_WEAKSPOT) { return "Debug weak spot on."; } else { return "Debug weak spot off."; };
};
Das dritte Argument ist für den Help-Output - nicht ganz so wichtig.
Hier das Skript
Das Skript ist ab Version 2.4.0 in LeGo enthalten.
Hmmm can you look for this ?
func void Inert_NpcAndMove(var int npc, var int x, var int y, var int z)
{
var oCNpc slf; slf = Hlp_GetNpc(npc);
WldInsertNpc(npc,"TOT");
slf._zCVob_trafoObjToWorld[3] = mkf(x);
slf._zCVob_trafoObjToWorld[7] = mkf(y);
slf._zCVob_trafoObjToWorld[11] = mkf(z);
};
I need this function witchout bugs , because your respawn ( by PermMem ) inert NPC in CITY xD
Abuyin Sharidi
22.09.2016, 21:26
Die Möglichkeit neue Konsolenbefehle hinzuzufügen gibt es ja schon seit Version 1.1.2 von Ikarus (siehe hier). Leider wurde mit der Implementierung immer zurückgegeben, der Befehl sei unbekannt (Unknown Command : NEUERBEFEHL) und Autovervollständigung war dort auch nicht unterstützt. Zu dem war es vor allem sehr umständlich einzelne neue Konsolenbefehle hinzuzufügen.
Mir gefällt der Aufbau und die simple Funktionsweise der FrameFunctions sehr. Da ich für eigene Zwecke einige Konsolenbefehle eingebaut und dabei eine hübschere Variante entwickelt habe (inkl. Autovervollständigung und anpassbarer Rückgabe auf der Konsole), habe ich mir gedacht, sie wie die FrameFunctions einzubauen.
Ich finde es sehr praktisch, da es mir das Debugging extrem vereinfacht, denn ein neuer Konsolenbefehl ist (wie bei den FrameFunctions) mit einer Zeile eingebaut.
Vielleicht findet das Skript hier seinen Weg in die nächste LeGo Version. (Mit dem im Hinterkopf habe ich das Skript dem Format des FrameFunctions-Skripts sehr angeglichen). Einen (Un-)Archiver für PermMem gibt es nicht, da ein Konsolenbefehl eh einmal pro Session gestartet werden sollte (zweimaliges Definieren wird automatisch abgefangen).
Hier zwei Beispiele und Screenshots:
Einfaches Beispiel:
44816
44817
Nützliches Beispiel:
44818
///////////////
// EXAMPLE 1 //
///////////////
CC_Register(regConsoleFunc01, "are you ", "answers a simple question");
/* This function is called when the command is entered */
func string regConsoleFunc01(var string command) { // command holds anything entered after the registered command prefix
if (Hlp_StrCmp(command, STR_Upper("cool"))) {
return "Yes, I am.";
} else {
return "No, I am cool.";
};
};
///////////////
// EXAMPLE 2 //
///////////////
CC_Register(regConsoleFunc, "debug weakspot", "turn debug visualization on/off");
/* Console function to enable/disable weak spot debug output */
func string regConsoleFunc(var string command) {
FREEAIM_DEBUG_WEAKSPOT = !FREEAIM_DEBUG_WEAKSPOT;
if (FREEAIM_DEBUG_WEAKSPOT) { return "Debug weak spot on."; } else { return "Debug weak spot off."; };
};
Das dritte Argument ist für den Help-Output - nicht ganz so wichtig. Ich habe noch eine Konstante CC_Enable eingebaut mit der man alle Konsolenbefehle deaktivieren kann. Das ist extrem Hilfreich für den finalen Build der Mod (damit niemand cheatet), da muss man nicht alle Befehle per Hand deaktivieren.
Das Skript:
/***********************************\
CONSOLECOMMANDS
\***********************************/
const int zCConsole__Register = 7875296; //0x782AE0
const int zCConsoleOutputOverwriteAddr = 7142904; //0x6CFDF8 // Hook length 9
const int CC_Enable = 1; // Set to zero to disable all console commands (e.g. for a final mod build)
//========================================
// [internal] Class
//========================================
class CCItem {
var int fncID;
var string cmd;
};
instance CCItem@(CCItem);
var int _CC_Symbol;
var string _CC_command;
//========================================
// Check if command is registered
//========================================
func int CC_Active(var func function) {
_CC_Symbol = MEM_GetFuncPtr(function);
foreachHndl(CCItem@, _CC_Active);
return !_CC_Symbol;
};
func int _CC_Active(var int hndl) {
if(MEM_ReadInt(getPtr(hndl)) != _CC_Symbol) {
return continue;
};
_CC_Symbol = 0;
return break;
};
//========================================
// Register a new command for the console
//========================================
func void CC_Register(var func function, var string commandPrefix, var string description) {
const int hook = 0;
if (!hook) {
HookEngineF(zCConsoleOutputOverwriteAddr, 9, _CC_Hook);
hook = 1;
};
// Only add if not already present
if(CC_Active(function)) {
return;
};
commandPrefix = STR_Upper(commandPrefix);
// Register auto-completion
var int descPtr; descPtr = _@s(description);
var int comPtr; comPtr = _@s(commandPrefix);
const int call = 0;
if (CALL_Begin(call)) {
CALL_PtrParam(_@(descPtr));
CALL_PtrParam(_@(comPtr));
CALL__thiscall(_@(zcon_address), zCConsole__Register);
call = CALL_End();
};
// Add function
var int hndl; hndl = new(CCItem@);
var CCItem itm; itm = get(hndl);
itm.fncID = MEM_GetFuncPtr(function);
itm.cmd = commandPrefix;
};
//========================================
// Remove command
//========================================
func void CC_Remove(var func function) {
_CC_Symbol = MEM_GetFuncPtr(function);
foreachHndl(CCItem@, _CC_RemoveL);
};
func int _CC_RemoveL(var int hndl) {
if(MEM_ReadInt(getPtr(hndl)) != _CC_Symbol) {
return continue;
};
delete(hndl);
return break;
};
//========================================
// [internal] Engine hook
//========================================
func void _CC_Hook() {
_CC_command = MEM_ReadString(MEM_ReadInt(ESP+1064)); // esp+424h+4h
if (!CC_Enable) {
return;
};
MEM_PushIntParam(CCItem@);
MEM_GetFuncID(ConsoleCommand);
MEM_StackPos.position = foreachHndl_ptr;
};
func int ConsoleCommand(var int hndl) {
var CCItem itm; itm = get(hndl);
var int cmdLen; cmdLen = STR_Len(itm.cmd);
if (STR_Len(_CC_command) >= cmdLen) {
if (Hlp_StrCmp(STR_Prefix(_CC_command, cmdLen), itm.cmd)) {
MEM_PushStringParam(STR_SubStr(_CC_command, cmdLen, STR_Len(_CC_command)-cmdLen));
MEM_CallByPtr(itm.fncID);
var string ret; ret = MEM_PopStringResult();
if (!Hlp_StrCmp(ret, "")) {
MEM_WriteString(EAX, ret);
return rBreak;
};
};
};
return rContinue;
};
Is is possible to manipulate console entirely with your script? I mean, is it possible to prevent the console of putting normal commands like cheat god (ignoring them) and using own commands like yours?
Is is possible to manipulate console entirely with your script? I mean, is it possible to prevent the console of putting normal commands like cheat god (ignoring them) and using own commands like yours?
You can't disable the normal console commands with that script. I wrote a script to disallow cheat god and one to prevent the console from being opened (search for them in the LeGo-threads).
However, I do not recommend disabling the console in most cases. It's reasonable to assume that all mods have bugs and at least some of them can be overcome via using the console.
mud-freak
22.09.2016, 21:37
Is is possible to manipulate console entirely with your script? I mean, is it possible to prevent the console of putting normal commands like cheat god (ignoring them) and using own commands like yours?
No, this script is only for adding new commands. Existing ones cannot be disabled with this script.
To disable existing commands, I found this to be the easiest way:
This will not recognize any command that starts with "cheat".
const int strCheat = 9121900; //0x8B306C
MEM_WriteInt(strCheat, 0);
Edit: Whoops, too late.
Abuyin Sharidi
22.09.2016, 21:41
Well I'd like to disable the old console and make my own or at least disable all existing commands and add mine. Anyway, I'm gonna look for it myself (I haven't started it yet, just saw mud-freak's post and I got the idea of doing that).
Hello §wink It's again me §cry
I write a QuickBar, but i have one problem. Why render is under View ? I don't see my items in bar.
How i can make to Render it was above View ?
Hello §wink It's again me §cry
I write a QuickBar, but i have one problem. Why render is under View ? I don't see my items in bar.
How i can make to Render it was above View ?
Read the documentation. (http://lego.worldofplayers.de/?Render)
I always wanted to tell someone to rtfm.
Ich hab mir das übrigens gerade nochmal angeschaut, die oberen beiden Fehlermeldungen scheinen harmlos zu sein, lassen sich aber leicht vermeiden wenn man kleine Änderungen im Lego-Code vornimmt. Die Funktionalität wird dadurch scheinbar nicht beeinflusst.
[...]
Das nützt zwar nichts in Sachen Stabilität, hilft aber bei der Fehlersuche, da man nicht erst durch diese Warnungen fehlgeleitet wird.
Es hat etwas gedauert, aber diese Änderungen haben mittlerweile ihren Weg in die Scripte gefunden :gratz
Zusammen mit einigen anderen Änderungen ist das Teil einer kleinen Überraschung, an der ich mit Mark56 arbeite ;)
Abuyin Sharidi
09.10.2016, 16:39
I just have a question about the deprecated package Projectile.d. What was it supposed to do?
LeGo 2.3.6 hat in der LeGo.d noch 2.3.5 stehen. §wink
Grade das Buffs-Paket entdeckt. Sehr nett! :A
Gibt es eine Möglichkeit zu definieren, was am Ende des Buffs geschehen soll?
Grade das Buffs-Paket entdeckt. Sehr nett! :A
Gibt es eine Möglichkeit zu definieren, was am Ende des Buffs geschehen soll?
In den zugehörigen Beispielen (http://lego.worldofplayers.de/?Beispiele_Buffs) steht die Klasse, die verwendet wird, um so einen Buff zu definieren.
Da sieht man auch die Eigenschaft "OnRemoved", die genau das macht. Ist vielleicht nicht ganz eindeutig, die dort eingetragene Funktion wird aber aufgerufen, wenn der Buff abgelaufen ist oder manuell entfernt wird. Da sollte ich wohl noch eine Möglichkeit anbieten, das zu unterscheiden.
Denk dran, dass du die Buffs noch manuell intialisieren musst, wobei die Grundfunktionalität tatsächlich auch ohne Initialisierung funktioniert :p Wenn man sie allerdings initialisiert (z.B. LeGo_Init(LeGo_All | LeGo_Buffs)), werden Buffs, die auf dem Helden aktiv sind, noch in einer Leiste unten am Rand angezeigt. Solltest du das testen, kannst du gleich mal berichten, ob das bei dir okay aussieht.
Edt: Habe ich im Beispiel nicht explizit erwähnt, aber natürlich muss für jede der drei möglichen Funktionen, die man eintragen kann, anstatt MEM_GetFuncID() besser SAVE_GetFuncID() eingesetzt werden. Doofer Parser.
Tatsache... Die Eigenschaft hab ich wohl übersehen :scared:
Okay, danke :)
Ich werd's heute Abend mal testen, wenn ich zu Hause bin. Auf der Arbeit scripten geht, testen ist aber immer so die Sache §ugly
Edit: Verstehe ich das richtig, dass die Eigenschaft onApply eine Funktion aufruft, beim Wirken des Buffs und onTick eine Funktion regelmäßig aufruft?
Also bräuchte ich für einen statischen Buff "nur" die Funktion onApply und onRemove definieren, onTick kann ich aber außen vor lassen?
So ist es gedacht, ja.
Vielleicht kann ich in Zukunft noch mehr Eigenschaften hinzufügen, so dass man die typischsten Buffs komplett über Eigenschaften definieren kann und keine eigenen Funktionen braucht.
Hab's doch jetzt schon getestet (Ich spielkind... §ugly)
Buff funktioniert soweit problemlos, Anzeige gibbet leider keine :(
Hier mal der Code, vielleicht hab ich ja nen Denkfehler gemacht :)
instance maxManaBuff(lCBuff)
{
name = "Mana-Erhöhung";
bufftype = BUFF_GOOD;
durationMS = 300000;
tickMS = 1000;
onApply = SAVE_GetFuncID(maxManaBuff_Apply);
onRemoved = SAVE_GetFuncID(maxManaBuff_Remove);
};
func void maxManaBuff_Apply(var int bh)
{
normalMana = hero.attribute[ATR_MANA_MAX];
var int ptr; ptr = Buff_GetNpc(bh);
if (!ptr) { return; };
var c_npc n; n = _^(ptr);
if (n.attribute[ATR_MANA_MAX] < 100)
{
n.attribute[ATR_MANA_MAX] = 100;
n.attribute[ATR_MANA] = 100;
}
else
{
return;
};
};
func void maxManaBuff_Remove (var int bh)
{
hero.attribute[ATR_MANA_MAX] = normalMana;
hero.attribute[ATR_MANA] = normalMana;
};
Na du hast ja auch keine Textur dafür eingetragen, wie soll denn da auch was angezeigt werden? :p
Wundert mich ein bisschen, dass Gothic nicht einfach die DEFAULT.TGA nimmt, aber das liegt vermutlich daran, dass gar kein String eingetragen wurde, anstatt einer nicht-existenten Textur (Eigenschafft buffTex).
tickMS kannst du übrigens weglassen (bzw. auf 0 setzen, was äquivalent zu "nicht eintragen" ist), das ist natürlich nur wichtig, wenn dein Buff auch einen periodischen Effekt hat ;)
Das sollte die Performance noch verbessern (auch wenn die bei einem Aufruf pro Sekunde kein Problem sein sollte).
Oha, der tickMS ist noch übrig geblieben :D
Dann versuch ichs mal mit 'ner Standard-Textur :)
Edit: Tatsächlich, mit Standard-Textur funktionierts :) Okay, ist grade halt nur ein stein-graues Quadrat auf dem Bildschirm. Ich persönlich hätte es etwas weiter nach rechts positioniert, in meinem Test-Fenster (640*480) klebt es komplett am linken Rand. Ich such mir nachher mal 'ne schicke Textur raus und mach mal ein Foto :) Ansonsten: Top-Funktion! :gratz
Verzeiht den Doppelpost, aber...:
http://upload.worldofplayers.de/files10/workingBuffs.png
Funktioniert!
Wie gesagt, klebt am linken Bildschirmrand fest.
Kann ich die Position noch anpassen? Ist leider früh am morgen und ehrlich gesagt steige ich nicht ganz durch das Script durch §ugly
In Zeile 34-37:
repeat(k, BUFFLIST_SIZE);
v = View_Create(((100+xsize)*k),7000, (100+xsize)*k+xsize, 7500);
MEM_WriteStatArr(bufflist_views, k, v);
end;
(Die werden aus Performance-Gründen nur einmal erzeugt und dann recyclet). Falls du Abstand zum linken Rand möchtest, musst du einfach eine Konstante (in virtuellen Pixeln) auf den ersten und dritten Paramter draufaddieren.
Tatsache, hatte bisher nur versucht den Wert direkt abzuändern §enton
Jetzt funktionierts bestens :) Wie gesagt, top Arbeit! :A :gratz
Edit: Falls jemand neugierig ist. So sieht es mit mehreren Buffs gleichzeitig aus:
http://upload.worldofplayers.de/files10/workingBuffs2.png
Neconspictor
10.11.2016, 11:29
@Dada: Die Buttons schauen schick aus. Hast du die selbst gemacht? :)
Jain...
Nach Schritt-für-Schritt-Anleitung zusammengebastelt :D Hier (http://www.2deegameart.com/2013/06/game-ui-design-mana-orb-inkscape-tutorial.html) der Link dazu :)
Neconspictor
10.11.2016, 11:46
Cool, danke §wink
Wenn ich dran denk wie viel Arbeit es früher war solche Dinge umzusetzen §cry
Es ist alles eine Frage der Werkzeuge ;)
Mit PermMem und der syntaktischen Unterstützung von Ikarus kann man sowas eben in ~200 Zeilen runterschreiben :p
In LeGo or Ikarus is function which can save a new string varriables? This is a big problem :I I have a new functions (all used strings, and oCNPC archivers) Examples:
const int oCNpc__GetModel = 7571232;
const int zCModel__SearchNode = 5758960;
const int zCModel__SetNodeVisual = 5739168;
func int oCNpc_GetModel(var int npc)
{
CALL__thiscall(npc, oCNpc__GetModel);
return CALL_RetValAsInt();
};
func int zCModel_SearchNode(var int model, var string node)
{
CALL_zStringPtrParam(node);
CALL__Thiscall(model, zCModel__SearchNode);
return CALL_RetValAsInt();
};
func void zCModel_SetNodeVisual(var int model, var int modelNodeInst, var int Visual, var int prm3)
{
CALL_IntParam(prm3);
CALL_PtrParam(Visual);
CALL_PtrParam(modelNodeInst);
CALL__Thiscall(model, zCModel__SetNodeVisual);
};
func void PutInNode(var int npc, var string node, var string visual)
{
var int npcModel; var int modelNodeInst; var int vob;
npcModel = oCNpc_GetModel(npc);
vob = MEM_InsertVob(visual, MEM_GetAnyWP());
modelNodeInst = zCModel_SearchNode(npcModel, node);
zCModel_SetNodeVisual(npcModel, modelNodeInst, MEM_ReadInt(vob+200), 1);
};
func void Ext_PutInSlot(var c_npc npc, var string node, var string visual)
{
PutInNode(_@(npc),node,ConcatStrings(visual,".3ds"));
};
When game has loaded node visual not saved. How i can save this? (Without aivars)
Abuyin Sharidi
24.11.2016, 14:01
If you wanna do it only for your character (PC_Hero), just make a function that checks if the .3ds is in your node and if not, reapply it.
Saving a string is easy:
var int string_handle; string_handle = new(string@);
var _string str; str = get(string_handle);
str.s = "mySavedString";
// later on to use it:
var _string str; str = get(string_handle);
print(str.s);
(There are some structs/classes defined in PermMem_structs.d for convenience)
This will, however, not magically re-apply your changes, you will have to do that yourself.
Some recommendations: Save the Npc alongside the string via an ID (http://lego.worldofplayers.de/?Talents) (keep in mind that these IDs will only yield a real NPC when he actually exists (i.e. IDs will stop working cross-worlds). If no such NPC exists in the current world, -1 will be returned).
Whenever the game is loaded or a levelchange occurs, loop through your saved strings/Npcs and re-apply your changes.
To do this at the right time, use the Gamestate (http://lego.worldofplayers.de/?Gamestate)-packet (and pray that the NPC list is already constructed at that time - if not, use FrameFunctions to delay the execution of your function a couple of frames).
Thanks Lehona. Quivers i make by Aivars, but at this time i have only 6 free aivars... (3 Aivars for Quivers...), and i have next ideas: Scabbards, Duals, and so on...(All using nodes...)
Yesterday i wrote a message to AST creator "Gratt" he is gave me reply "Edit oCNpc archiver" It's possible by Ikarus? This function is protected...
virtual void __thiscall oCNpc::Archive(zCArchiver &) 0x00746470 0 6 protected: virtual void __thiscall oCNpc::Archive(zCArchiver &)
Limited AIVars are no longer a problem in times of PermMem, but because PM may be a bit intimidating to get used to, I wrote a frontend for it: http://lego.worldofplayers.de/?Talents
I haven't had time to write some examples, but here's how you use it:
var int myTalent; // A global variable, like the constant used in AIVar expressions
// Create the talents once, e.g. in Startup_Global or Init_Global
func void Init_Global() {
...
var int talents_initialised;
if (!talents_initialised) {
myTalent = TAL_Create();
talents_initialised = 1; // You mustn't create them more than once
};
};
// Later on, you'll want to store a value for this talent
TAL_SetValue(myNpc, myTalent, 123456); // Whatever value you want, really
// And even later on, you'll want to read that value!
Print(IntToString(TAL_GetValue(myNpc, myTalent)); // Prints '123456'
You can only store ints, not strings, but that just means that you have to save a string handle (like in post #309) instead of the string itself.
For reapplying your changes, just loop over MEM_World.voblist_npcs. You can do this easily by using List_ForFS (http://lego.worldofplayers.de/?List#List_ForF).
PS: I don't think the 'protected' actually means that you can't change the assembly ;) But there's no need to manually edit the archiver, after all that is what PermMem is for.
PPS: The package 'talents' requires only 1 AIVar per NPC, which is defaulted to 89 currently, but you can set it yourself in Userconst.d(line 115):
const int AIV_TALENT = 89; // Genutzte AI-Var
Thanks ;)
PermMem save variables? Example:
void blablabla()
{
PM_SaveString("saved_string","This is working!");
[...] Load Game
Print(PM_LoadString("saved_String"));
}
No, PermMem/Talents take care of that for you.
Besides: Anything created with new() will always be saved! You don't have to do it yourself.
LeGo 2.4.0 (https://subversion.assembla.com/svn/lego2/Release/LeGo_2_4_0.7z)
The constant LeGo_Version has been updated to reflect the version number (rare enough to be worth a mention!)
Various "magic numbers" have been moved to EngineAdr.d
Fixed two warnings that would pop up during save/load
Minor changes to increase portability to G1
ConsoleCommands (http://forum.worldofplayers.de/forum/threads/1408430-Skriptpaket-LeGo-3/page8?p=25098420&viewfull=1#post25098420)! (Wiki update will follow shortly)
Buffs and Render still experimental.
Frank-95
28.11.2016, 09:40
Great work!
LeGo 2.4.0 (https://subversion.assembla.com/svn/lego2/Release/LeGo_2_4_0.7z)
The constant LeGo_Version has been updated to reflect the version number (rare enough to be worth a mention!)
Various "magic numbers" have been moved to EngineAdr.d
Fixed two warnings that would pop up during save/load
Minor changes to increase portability to G1
ConsoleCommands (http://forum.worldofplayers.de/forum/threads/1408430-Skriptpaket-LeGo-3/page8?p=25098420&viewfull=1#post25098420)! (Wiki update will follow shortly)
Buffs and Render still experimental.
You should edit the first post in this thread. I was lead to believe that I had the newest Lego Version which was not the case. :o
You should edit the first post in this thread. I was lead to believe that I had the newest Lego Version which was not the case. :o
Das kommt davon, wenn man Nachts noch 'n Release zusammenschnürt, weil mud-freak es braucht :o
TheEternal
07.12.2016, 19:16
habe die version 2.3.6 installiert. Jetzt kommt beim Start und beim Laden eines Spiels:
MEM_InfoBox("This should never happen! If it does anyway, please report to Lehona on WorldOfGothic.");
habe die version 2.3.6 installiert. Jetzt kommt beim Start und beim Laden eines Spiels:
MEM_InfoBox("This should never happen! If it does anyway, please report to Lehona on WorldOfGothic.");
Warum nicht gleich 2.4.0? :p
Aber warum auch immer dieser Bug auftritt: Das wird in 2.4.0 vermutlich nicht gefixt worden sein.
Mit welchen Parametern initialisierst du LeGo? Tust du zufälligerweise irgendwas mit LeGo "sehr früh"? z.B. in der Startup_Global() oder in irgendwelchen Instanzen?
Wenn diese Meldung kommt, ist PermMem noch nicht initialisiert, aber irgendwo wird versucht, es zu benutzen.
TheEternal
07.12.2016, 21:33
Ja, Neconspictor hat hier das DII (http://forum.worldofplayers.de/forum/threads/1449090-Release-DynItemInst?highlight=DII)vor LeGo initialisiert in der init_global() und es muss wohl vorher passieren weil sonst paar hooks nicht funktionieren.
func void startup_global(){
SYSVAR_Poison_Strength = 100;
Game_InitGerman ();
};
func void init_global()
{
Game_InitGerman ();
// Ikarus
MEM_InitAll ();
//MEM_SetShowDebug(FALSE);
// Has to be initialized before LeGo (some hooks otherwise don't work!)
DII_Init();
// LeGo
LeGo_Init (LeGo_All & ~LeGo_Bloodsplats /*& ~LeGo_Focusnames*/);
...
Kann man das Problem nicht über ein Singleton lösen?
Vorm Gebraucht wird das PermMem singleton angefragt und initialisiert, wenn noch nicht vorhanden.
Müsste natürlich die Struktur von LeGo etwas anpassen evtl.
Ja, Neconspictor hat hier das DII (http://forum.worldofplayers.de/forum/threads/1449090-Release-DynItemInst?highlight=DII)vor LeGo initialisiert in der init_global() und es muss wohl vorher passieren weil sonst paar hooks nicht funktionieren.
func void startup_global(){
SYSVAR_Poison_Strength = 100;
Game_InitGerman ();
};
func void init_global()
{
Game_InitGerman ();
// Ikarus
MEM_InitAll ();
//MEM_SetShowDebug(FALSE);
// Has to be initialized before LeGo (some hooks otherwise don't work!)
DII_Init();
// LeGo
LeGo_Init (LeGo_All & ~LeGo_Bloodsplats /*& ~LeGo_Focusnames*/);
...
Kann man das Problem nicht über ein Singleton lösen?
Vorm Gebraucht wird das PermMem singleton angefragt und initialisiert, wenn noch nicht vorhanden.
Müsste natürlich die Struktur von LeGo etwas anpassen evtl.
Das wundert mich zwar, aber genausowenig sollte DII mit LeGo interferieren und da Probleme machen.
Ich schau mal, ob ich das reproduzieren kann, ansonsten muss ich mir vielleicht mal mehr Skripte von euch anschauen...
So war das ursprünglich gelöst (mehr oder weniger), aber um das wirklich konsequent zu machen fehlen mir in Daedalus leider die Möglichkeiten (bzw. man müsste LeGo verhältnismäßig stark überarbeiten). Eigentlich spricht ja auch nichts dagegen, das einfach einmal zu initialisieren (auch wenn das anscheinend gar nicht so einfach ist?).
In meinen Augen dürfte das aber sowieso eher ein Symptom eines tiefer liegenden Problems in deinem Code sein (sollte ich keinen offensichtlichen Fehler gemacht haben).
Hab am Freitag noch ein kleines Puzzlespiel in XR mit LeGo eingebaut:
https://youtu.be/HlPVpisU-Po
War echt easy und funktioniert super. Hab nach dem Video noch ein wenig optimiert, aber es zeigt ganz gut, wie es funktioniert.
Sehr cool!
Es würden sich bestimmt einige Leute (mich eingeschlossen) freuen, wenn du den Code hier veröffentlichst (soweit er denn eigenständig genug ist), es kann nie genug Beispiele geben ;)
Aber vielleicht erst, wenn du Xeres Rache auch released hast... :p
P.S.: Du veröffentlichst ja munter Testversionen; gibt es keine mysteriösen Bugs mehr, die (eventuell) von LeGo verursacht werden? :)
Sehr cool!
Es würden sich bestimmt einige Leute (mich eingeschlossen) freuen, wenn du den Code hier veröffentlichst (soweit er denn eigenständig genug ist), es kann nie genug Beispiele geben ;)
// create a new button somewhere on the screen and assign onClick function "DragButton_Click"
const int BUTTON_WIDTH = 50;
const int BUTTON_HEIGHT = 50;
var int btnHndl; var int btnHndl = Button_CreatePxl(r_Max(Print_Screen[PS_X] - BUTTON_WIDTH), r_Max(Print_Screen[PS_Y] - BUTTON_HEIGHT), BUTTON_WIDTH, BUTTON_HEIGHT, "BUTTON.TGA", Button_Null, Button_Null, DragButton_Click);
Button_Show(btnHndl);
// some helper variables
// active button handle
var int DragButton_ActiveHndl;
// last mouse position to check if update necessary
var int DragButton_LastMouseX;
var int DragButton_LastMouseY;
// diff from mouse necessary to place button correctly
var int DragButton_DiffFromMouseX;
var int DragButton_DiffFromMouseY;
// set if clicks on multiple shall be ignored (stops on first one)
var int DragButton_Active;
// The function handling the drag and drop registration... clicking once on a button => dragging => clicking on it again => dropping
FUNC VOID DragButton_Click(var int hndl) {
if (DragButton_ActiveHndl != 0) {
if (DragButton_ActiveHndl == hndl) {
DragButton_ActiveHndl = 0;
};
DragButton_Active = TRUE;
return;
};
if (DragButton_Active) {
return;
};
DragButton_ActiveHndl = hndl;
var _Button btn; btn = get(DragButton_ActiveHndl);
DragButton_LastMouseX = Cursor_X;
DragButton_LastMouseY = Cursor_Y;
DragButton_DiffFromMouseX = Cursor_X - Print_ToPixel(btn.posX, PS_X);
DragButton_DiffFromMouseY = Cursor_Y - Print_ToPixel(btn.posY, PS_Y);
};
// frame function moving the button
FUNC VOID Puzzle_Tick() {
DragButton_Active = FALSE;
if (DragButton_ActiveHndl != 0) {
if (Cursor_X != DragButton_LastMouseX || Cursor_Y != DragButton_LastMouseY) {
var _Button btn; btn = get(DragButton_ActiveHndl);
Button_Move(DragButton_ActiveHndl, Cursor_X - DragButton_DiffFromMouseX, Cursor_Y - DragButton_DiffFromMouseY);
DragButton_LastMouseX = Cursor_X;
DragButton_LastMouseY = Cursor_Y;
};
};
};
Hab mal rausgezogen und gekürzt, sollte so funktionieren. Ist jetzt einfach nur das Drag'n'Drop. Das Akzeptieren des Puzzles ist dann ja nur noch ein Positionsvergleich.
Aber vielleicht erst, wenn du Xeres Rache auch released hast... :p
Xeres' Rückkehr! §cry :D
P.S.: Du veröffentlichst ja munter Testversionen; gibt es keine mysteriösen Bugs mehr, die (eventuell) von LeGo verursacht werden? :)
Ich hoffe nicht :D Die letzten Änderungen haben mich etwas hoffen lassen, also gab doch da vor 'ner Weile Fixes von Crashes beim Levelchange. Und glaub irgendwas anderes hab ich auch noch gefunden. Ob es läuft, wird sich noch zeigen. Maximale Spielzeit bei der Testern ist grad bei 9 Stunden (von dem, was ich gehört hab), da lässt sich noch nichts konkretes sagen.
gladi1994
16.12.2016, 11:46
Ich hätte zum Skriptpaket LeGo mal eine (vermutlich dumme) Frage:
Ich hab das jetzt runtergeladen und alle Skripte schön säuberlich in einem Ordner. Wie bringe ich die genau in die vorhandenen Skripte unter? Gibts da irgendwelche Abhängigkeiten, muss da was bei der Reihenfolge beim parsen beachtet werden?
Ich habe leider nirgends eine Dokumentation oder Readme zu LeGo gefunden, nur immer zu Ikarus (und das hab ich ja schon erledigt^^)
Danke, gladi
Edit: Habe nach langem Suchen einen Beitrag von Lehona gefunden (vom 15.05.2013), ist das noch aktuell?
Also das Wiki funktioniert momentan bei mir. Wir benutzen seit kurzem eine Gratis Datenbank, kann sein, dass dort die Uptime nicht so toll ist... Aber es sollte jetzt wieder funktionieren.
Eigentlich musst du bloß die Scripte einpflegen (d.h. irgendwo in den Scripts-Ordner kopieren und LeGo\Header.src in die Gothic.src eintragen, nach Ikarus und den Floats) und entsprechend initialisieren. Wenn du nur Schleifen brauchst, ist LeGo_Init(LeGo_FrameFunctions); in der Init_Global() (in Startup.d) völlig ausreichend.
Danach kannst du eine ein-sekündige Schleife mit FF_ApplyExtOnce(myFunc, -1, 1000); hinzufügen (einfach direkt nach LeGo_Init() aufrufen). Näheres zu den FrameFunctions findest du hier (http://gottfried.milgo.de/LeGo/de/?FrameFunctions).
Scheinbar gibt es zu LeGo kein Wiki? Oder bin ich nur zu dusselig, das zu finden??
P.P.S.: Warum kann ich ältere Posts nicht korrekt zitieren?
Augen auf im Straßenverkehr :p Die bereits verlinkte Website http://lego.worldofplayers.de ist das LeGo-Wiki ;) Eine ReadMe findet man da unter "Sonstiges": http://lego.worldofplayers.de/?ReadMe
Der Post ist in der Tat noch aktuell. Du kannst ihn nicht zitieren, weil der Thread geschlossen ist. Etwas nervig, ist aber so :)
gladi1994
16.12.2016, 13:10
Augen auf im Straßenverkehr :p Die bereits verlinkte Website http://lego.worldofplayers.de ist das LeGo-Wiki ;) Eine ReadMe findet man da unter "Sonstiges": http://lego.worldofplayers.de/?ReadMe
Der Post ist in der Tat noch aktuell. Du kannst ihn nicht zitieren, weil der Thread geschlossen ist. Etwas nervig, ist aber so :)
Die ReadMe hab ich tatsächlich nicht gefunden, tut mir echt leid. :confused: Naja, danke für den Link!
Hab inzwischen alle LeGo-Skripte nach _work\Data\Scripts\Content\LeGo gepackt (wie es ja dann auch in der ReadMe sogar steht) und mit dem GothicStarter geparst.
Allerdings stößt mir das hier auf (steht so in der ReadMe und lese ich auch nicht zum ersten Mal):
"Um LeGo verwenden zu können musst du in deiner Gothic.src unterhalb von Ikarus die LeGo\Header.src eintragen."
Ich hab die Skripte von Ikarus und LeGo der Reihenfolge nach in die Ordnerstruktur eingepflegt und immer Stück für Stück mit dem GothicSourcer geparst. (Nach einem Video von Marcello: https://www.youtube.com/watch?v=ASzA-xHVwCg)
Dabei ist in der Gothic.src allerdings kein Eintrag zu Ikarus oder LeGo entstanden, deshalb hab ich da auch bisher nie weiter was eingetragen... Den Zusammenhang zwischen dem parsen mit GS und der Gothic.src verstehe ich absolut nicht... sorry:dnuhr:
Scheinbar bin ich ein hoffnungsloser Fall §cry
Ich hoffe, das kann mir jemand erklären? Und hab ich denn bisher trotzdem alles richtig gemacht?
Gladi
Die Gothic.src regelt, welche Skripte von Gothic in welcher Reihenfolge gelesen werden. Wenn ein Skript dort nicht eingetragen ist (entweder explizit oder über sowas wie "Dialoge\*.d"), ignoriert Gothic es einfach - als wäre es gar nicht vorhanden. Wenn du LeGo also benutzen willst, musst du es in der Gothic.src eintragen. Dazu schreibst du am besten unter die Float.d (glaube so heißt die Datei - war beim Download von Ikarus mit dabei und hoffentlich hast du sie ebenfalls in die Gothic.src eingetragen) einfach folgendes:
LeGo\Header.src
Wobei das davon ausgehen würde, dass sich die Scripts im Scripts\Content\LeGo-Ordner befinden, also es existiert z.B. die Datei Scripts\Content\LeGo\LeGo.d.
Da LeGo viele einzelne Dateien verwendet und die Reihenfolge durchaus wichtig ist, wollten wir es dem Benutzer nicht zumuten, alle Dateien einzeln einzupflegen. Stattdessen haben wir die richtige Reihenfolge einfach selber in der Header.src festgelegt, somit braucht der User nur noch diese einzelne Zeile einzufügen ;)
Zu dem Video... Ich bin mir sicher, es ist nett gemeint von Marcello, aber die Ausschnitte, die ich mir gerade angeschaut habe, zeugen eher von Unwissen. Um Ikarus zu installieren, braucht es auch keine 45 Minuten, sondern vielleicht 2 :dnuhr: Mal abgesehen davon, dass Ikarus, anders als der Titel vermuten lässt, zwar ein Scriptpaket, aber sicherlich keine Scriptsprache ist.
Stück für Stück irgendwas zu parsen ist nicht nötig. Du musst alle Dateien (Ikarus und LeGo) irgendwo in den Scripts\Content-Ordner kopieren (wohin ist relativ egal, nur wiederfinden solltest du es) und alle nötigen Dateien in die Gothic.src eintragen (das geschieht nicht irgendwie auf magische Weise (würde auch keinen Sinn ergeben), das musst du schon selber machen!).
Für Ikarus und LeGo könnte der Eintrag (einfach relativ weit oben, hauptsache unter die Classes.d) wie folgt aussehen:
Ikarus\Ikarus_Const_G2.d
Ikarus\EngineClasses_G2\*.d
Ikarus\Ikarus.d
Ikarus\Float.d
LeGo\Header.src
Anmerkung: Ikarus befindet sich jetzt im Scripts\Content\Ikarus-Ordner und LeGo im Scripts\Content\LeGo-Ordner. Falls du diese Dateien woanders speichern möchtest, musst du natürlich die Einträge anpassen.
Es könnte außerdem sein, dass die Float.d stattdessen Floats.d heißt... Ich bin mir sicher, dass wirst du ggf. anpassen können.
Du bist kein hoffnungsloser Fall! Du liest einfach nur Anleitungen, die meistens Voraussetzen, dass man zumindest grobe Kenntnisse im Modding für Gothic hat, d.h. insbesondere dass so Sachen wie die Ordnerstruktur oder "wie bekomme ich meine Scriptänderungen ins Spiel?" bekannt sind.
Falls noch irgendwas unklar ist, einfach weiterhin nachfragen. Ich bin mir sicher, irgendwann macht es "Klick" :)
It is possible synchronize mud-freak turn script and cursor? I have new menu, but cursor is very slow, and i do not understand how i can make a cursor with mud-freaks idea ...(Page 13-14)
Is that just because you have low FPS (otherwise it wouldn't help)?
Have you tried increasing mouse sensitivity in the Gothic menu (not 100% sure whether that actually helps)? Also try this when you show the cursor:
Cursor_NoEngine = true;
and obviously set it back to false when you hide the cursor. This should stop the engine from registering any mouse movement (e.g. turn the camera) and I think that sometimes made the cursor appear to be slow.
Hello, I've read LeGo wiki but whenever I try to add buffs to my modification they cause a ctd. I don't have the report copied since I've abandoned them, but maybe I overlooked something (they're really useful!!)
Here's the code I was using. I've merely changed something from the guide in the wiki (http://lego.worldofplayers.de/?Beispiele_Buffs).
instance crit_bleed(lCBuff) {
name = "Critical Bleed";
bufftype = BUFF_BAD;
durationMS = 10*1000;
tickMS = 1000;
onTick = SAVE_GetFuncID(crit_bleed_damage);
};
func void crit_bleed_damage(var int bh) {
var int ptr; ptr = Buff_GetNpc(bh);
if (!ptr) { return; };
var int hstr; hstr = hero.attribute[ATR_STRENGTH]/10;
var c_npc n; n = _^(ptr);
Npc_ChangeAttribute(n, ATR_HITPOINTS,-hstr);
};
Is there something wrong? I'm clueless =(
GiftGrün
16.12.2016, 23:56
Have you got buffs initialized? I think they're not part of LeGo_All because they are still new and therefore like an open beta version.
Replace LeGo_Init(LeGo_All)); (or whatever you have there) with LeGo_Init(LeGo_All | LeGo_Buffs)); .
Are you sure you have the newest version of LeGo?
Have you got buffs initialized? I think they're not part of LeGo_All because they are still new and therefore like an open beta version.
Replace LeGo_Init(LeGo_All)); (or whatever you have there) with LeGo_Init(LeGo_All | LeGo_Buffs)); .
They should work regardless, although I agree, it's probably good to make sure.
I have problem :/ I try get attitude npc to hero. If npc is in party or is friendly. I can hit he only when he is her.focus_vob. This is my code:
func int HeroDamage_CanDoHit_CNPCPass(var c_npc npc)
{
var c_npc trgt;
var ocnpc her; her = hlp_getnpc(hero);
trgt = _^(her.focus_vob);
if(npc.aivar[AIV_DAMAGEID]==HeroDamage_DamageID+1)
{
return 0;
};
if(Npc_IsInState(npc,ZS_Dead))||(Npc_IsInState(npc,ZS_Unconscious))
||(Npc_IsInState(npc,ZS_MagicSleep))
{
return 0;
}
else if((Npc_GetAttitude(npc,hero)==ATT_FRIENDLY)||(npc.flags == NPC_FLAG_FRIEND)||(npc.aivar[AIV_PARTYMEMBER]==true))
{
if(Hlp_GetInstanceID(trgt))//został znaleziony
{
if(Hlp_Is_oCNpc(trgt)) //jest npc
{
if(Hlp_GetInstanceID(trgt)==Hlp_GetInstanceID(npc))
{
Print("Target focus - friend, but ATTACK!");
return 1; //Namierzony i uderzony!!!
};
};
}
else //Nie został namierzony
{
Print("Target nofocus - friend, no ATTACK!");
return 0; //więc nie atakuj
};
}
else if(Npc_GetAttitude(npc,hero)==ATT_NEUTRAL)&&(HeroDamage_HostileEnemies!=0)&&(HeroDamage_TimeHeroAttackedNeutralFocus==0)
{
trgt = _^(her.focus_vob);
if(Hlp_Is_oCNpc(trgt))
{
if (Hlp_GetInstanceID(npc) == Hlp_GetInstanceID(trgt))
{
Print("Target focus - Attack!");
return 1; //Namierzony to uderzony!!!
}
else
{
Print("Target nofocus - don't attack!");
return 0; //Walczysz z 10? Żaden problem :D
};
};
}
else
{
return 1;
};
};
I don't know why i have crashes :/
I don't know why i have crashes :/
Well, maybe you should try debugging your code, then. It gets very exhausting to spend up to hours testing and fixing someone else's code when they don't appear to put in any effort themselves.
You know, besides all the stuff I have probably said about a thousand times on what information to include in a post when trying to find help.
PS: Your script doesn't have anything to do with LeGo, why would you post in this thread when the title clearly says LeGo?
PPS: You still don't seem to understand the difference between instances and pointers. Stop treating them as equivalent.
Milky-Way
21.12.2016, 19:39
Wir hatten bei LoA einen Absturz, der theoretisch mit LeGo zusammenhängen könnte, ich habe aber leider keine Zeit, das weiter zu verfolgen.
Wir hatten einen Npc, bei dem kein Visual eingetragen war, also keine Zeile wie
B_SetNpcVisual (self, MALE, "Hum_Head_Fighter", Face_N_Normal16, BodyTex_N, itar_kae_m);
in der Instanz.
Wenn man nun im Spiel direkt ein neues Spiel startet, dann gibt es noch kein Problem. Starte ich dann gleich noch mal ein neues Spiel, dann stürzt das Spiel mit einem Access Violation Fehler ab direkt zu Beginn des Ladevorgangs.
Der Fehler könnte auch mit anderen Mod-spezifischen Skripten zusammenhängen oder vielleicht auch ohne Ikarus und LeGo auftreten. Ich habe leider keine Zeit, das weiter zu testen. Aber vielleicht habt ihr ja eine Installation, bei der ihr das schnell ausprobieren könnt, und falls es ein generelles Problem ist, das auch bei euch auftritt, vielleicht den möglichen Fehler finden.
Ein Screenshot der AV wäre noch ganz nett. Aber ich kann mir gut vorstellen, dass Gothic einfach das Visual freen möchte (bzw. den refCtr dekrementieren), ohne das überhaupt auf Vorhandensein zu checken.
Der MEM_Helper von Ikarus besitzt ebenfalls ein Visual, obwohl das Script ansonsten minimal gehalten ist:
INSTANCE MEM_HELPER_INST (C_NPC)
{
name = MEM_HELPER_NAME;
id = 42;
/* unsterblich: */
flags = 2;
attribute [ATR_HITPOINTS_MAX] = 2;
attribute [ATR_HITPOINTS] = 2;
/* irgendein Visual: */
Mdl_SetVisual (self, "Meatbug.mds");
};
Ich geh mal davon aus, dass das irgendein Problem von Gothic ist.
Edit: Hab jetzt mal auf meiner Installation den LeGo-Kram aus der Startup auskommentiert und einen NPC ohne Visual einfügen lassen... Bei mir crasht es direkt beim Starten eines neuen Spiels, irgendwo tief in der Physik (?).
Wir hatten bei LoA einen Absturz, der theoretisch mit LeGo zusammenhängen könnte, ich habe aber leider keine Zeit, das weiter zu verfolgen.
Wir hatten einen Npc, bei dem kein Visual eingetragen war, also keine Zeile wie
B_SetNpcVisual (self, MALE, "Hum_Head_Fighter", Face_N_Normal16, BodyTex_N, itar_kae_m);
in der Instanz.
Wenn man nun im Spiel direkt ein neues Spiel startet, dann gibt es noch kein Problem. Starte ich dann gleich noch mal ein neues Spiel, dann stürzt das Spiel mit einem Access Violation Fehler ab direkt zu Beginn des Ladevorgangs.
Der Fehler könnte auch mit anderen Mod-spezifischen Skripten zusammenhängen oder vielleicht auch ohne Ikarus und LeGo auftreten. Ich habe leider keine Zeit, das weiter zu testen. Aber vielleicht habt ihr ja eine Installation, bei der ihr das schnell ausprobieren könnt, und falls es ein generelles Problem ist, das auch bei euch auftritt, vielleicht den möglichen Fehler finden.
Ich glaub nicht, dass das was mit LeGo oder Ikarus zu tun hat. Schon bevor es die zwei Pakete gab, ist XR paar Mal abgeschmiert, weil irgendwo mal ein NPC-Visual vergessen wurde. Afaik crasht es
1. Wenn man in die KI-Glocke des entsprechenden NPCs kommt
2. Die Welt wechselt/Savegame lädt => Das ist böse, weil man so schwerer rausfindet, welcher NPC Schuld ist
Milky-Way
22.12.2016, 09:26
Ich glaub nicht, dass das was mit LeGo oder Ikarus zu tun hat. Schon bevor es die zwei Pakete gab, ist XR paar Mal abgeschmiert, weil irgendwo mal ein NPC-Visual vergessen wurde. Afaik crasht es
1. Wenn man in die KI-Glocke des entsprechenden NPCs kommt
2. Die Welt wechselt/Savegame lädt => Das ist böse, weil man so schwerer rausfindet, welcher NPC Schuld ist
Punkt 2 in etwa dürfte bei uns der Fall gewesen sein. Mein Gedanke an LeGo und Ikarus kam, weil es nicht direkt beim ersten Start passierte und mir dieses Verhalten ansonst von Gothic bisher nicht bekannt war. (Mit LeGo und Ikarus konnte ich noch eher nachvollziehen, dass da etwas beim zweiten Start abstürzen könnte, was beim ersten noch "funktioniert")
Danke euch beiden fürs Ausprobieren. Ich wollte, wie gesagt, nur ggf. die Möglichkeit geben, zu schauen, ob es ein LeGo-Problem ist - scheint es nicht zu sein, also alles gut :)
I have problem with Lego 2.3.0b ported in Gothic 1 by Hatrez pl.
FrameFunctions cause following bugs:
- most of dialogs can not be skipped by mouse' right clicking (50%);
- some dialogs of hero are displayed like SVM talk (5%);
- some creatures like scavengers are walking in attack mode(100%);
I already copied FrameFunc.d from actual Lego 2.4.0, but problem still exists.
I make few nice things for G1 by FF like 2 colored bars, colored dialoge choices and really do not want leave that not used. I also turned off all functions with FF, but problem occurs till HookEngine(oCGame__Render, 7, "_FF_HOOK"); is active
Will anyone help?
I have problem with Lego 2.3.0b ported in Gothic 1 by Hatrez pl.
FrameFunctions cause following bugs:
- most of dialogs can not be skipped by mouse' right clicking;
- some dialogs of hero are displayed SVM talk;
- some creatures like scavengers are walking in attack mode;
I already copied FrameFunc.d from actual Lego 2.4.0, but problem still exists.
I make few nice things for G1 by FF like 2 colored bars, colored dialoge choices and really do not want leave that not used.
Will anyone help?
I don't have G1 around and therefore can't help you :dnuhr:
But if you post a minimal example (i.e. just the code required to get the bugs), maybe someone will have a look at it?
PS: I'm pretty sure all animals always walk in attack mode, they don't even have other animations registered.
Edit: I just saw your messages at TheModders.org, I don't think there are such problems in G2.
first of all Wish to everything the best in 2017 for all Gothic fans!
@Lehona,
what I can do is just to copy the engine_hooks.d file from Hatrez.Beyond ported adressess looks like a bit diffirent than yours - maybe u see something weird? Hatrez aka chicken is no longer avalaible on themodders.org. Hope u will develop Lego on g1 too. He has ported only 2 adressess, in G2nk there is 6 of them !
const int ASMINT_OP_movESItoEAX = 61577; //0xF089 ev. 50827
const int ASMINT_OP_movEAXtoEDI = 51081; //0xC789
/***********************************\
HOOKENGINE
\***********************************/
//-------------------
// OPCODES
//-------------------
// /* 1 Byte */
const int ASMINT_OP_movESItoEAX = 61577; //0xF089 ev. 50827
const int ASMINT_OP_movEAXtoEDI = 51081; //0xC789
//-------------------
// Registervariablen
//-------------------
var int EAX;
var int ECX;
var int ESP;
var int EBX;
var int EBP;
var int EDI;
var int ESI;
//========================================
// Engine hooken
//========================================
func void HookEngineI(var int address, var int oldInstr, var int function) {
var int SymbID; // Symbolindex von 'function'
var int ptr; // Pointer auf den Zwischenspeicher der alten Anweisung
var int relAdr; // Relative Addresse zum neuen Assemblercode, ausgehend von 'address'
// ----- Sicherheitsabfragen -----
if(oldInstr < 5) {
PrintDebug("HOOKENGINE: oldInstr ist zu kurz. Es werden mindestens 5 Bytes erwartet.");
return;
};
SymbID = function;
if(SymbID == -1) {
PrintDebug("HOOKENGINE: Die gegebene Daedalusfunktion kann nicht gefunden werden.");
return;
};
MemoryProtectionOverride (address, oldInstr+3);
// ----- Eventuell geschützen Speicher behandeln -----
if (MEM_ReadByte(address) == 233) { // Hook schon vorhanden
HookEngineI(MEM_ReadInt(address+1)+address+5+96, oldInstr, function);
return;
};
// ----- Die alte Anweisung sichern -----
ptr = MEM_Alloc(oldInstr);
MEM_CopyBytes(address, ptr, oldInstr);
// ----- Einen neuen Stream für den Assemblercode anlegen -----
ASM_Open(200 + oldInstr); // Play it safe.
// ----- Jump aus der Enginefunktion in den neuen Code einfügen -----
relAdr = ASMINT_CurrRun-address-5;
MEM_WriteInt(address + 0, 233);
MEM_WriteInt(address + 1, relAdr);
// ----- Neuen Assemblercode verfassen -----
// Alle Register sichern
// EAX in Daedalus Variable sichern
ASM_2(ASMINT_OP_movEAXToMem);
ASM_4(_@(EAX));
ASM_1(ASMINT_OP_pusha);
// ECX in Daedalus Variable sichern
ASM_2(ASMINT_OP_movECXtoEAX);
ASM_2(ASMINT_OP_movEAXToMem);
ASM_4(_@(ECX));
// ESP in Daedalus Variable sichern
ASM_2(ASMINT_OP_movESPtoEAX);
ASM_2(ASMINT_OP_addImToEAX);
ASM_1(4*8); // Wegen pushad [Danke an Sektenspinner]
ASM_2(ASMINT_OP_movEAXToMem);
ASM_4(_@(ESP));
// EBX in Daedalus Variable sichern
ASM_2(ASMINT_OP_movEBXtoEAX);
ASM_2(ASMINT_OP_movEAXtoMem);
ASM_4(_@(EBX));
// EBP in Daedalus Variable sichern
ASM_2(ASMINT_OP_movEBPtoEAX);
ASM_2(ASMINT_OP_movEAXtoMem);
ASM_4(_@(EBP));
// EDI in Daedalus Variable sichern
ASM_2(ASMINT_OP_movEDItoEAX);
ASM_2(ASMINT_OP_movEAXtoMem);
ASM_4(_@(EDI));
// ESI in Daedalus Variable sichern
ASM_2(ASMINT_OP_movESItoEAX);
ASM_2(ASMINT_OP_movEAXtoMem);
ASM_4(_@(ESI));
// --- Daedalusfunktion aufrufen ---
ASM_1(ASMINT_OP_pushIm);
ASM_4(SymbID);
ASM_1(ASMINT_OP_pushIm);
ASM_4(parser);
ASM_1(ASMINT_OP_call);
ASM_4(zParser__CallFunc-ASM_Here()-4);
ASM_2(ASMINT_OP_addImToESP);
ASM_1(8);
ASM_1(ASMINT_OP_popa);
ASM_1(ASMINT_OP_movMemToEAX);
ASM_4(_@(ECX));
ASM_2(ASMINT_OP_movEAXtoECX);
ASM_1(ASMINT_OP_movMemToEax);
ASM_4(_@(EDI));
ASM_2(ASMINT_OP_movEAXtoEDI);
ASM_1(ASMINT_OP_movMemToEAX);
ASM_4(_@(EAX));
// Alte Anweisung wieder einfügen
MEM_CopyBytes(ptr, ASMINT_Cursor, oldInstr);
MEM_Free(ptr);
ASMINT_Cursor += oldInstr;
// Zur Enginefunktion zurückkehren
ASM_1(ASMINT_OP_pushIm);
ASM_4(address + oldInstr);
ASM_1(ASMINT_OP_retn);
var int i; i = ASM_Close();
};
func void HookEngineF(var int address, var int oldInstr, var func function) {
HookEngineI(address, oldInstr, MEM_GetFuncID(function));
};
func void HookEngine(var int address, var int oldInstr, var string function) {
HookEngineI(address, oldInstr, MEM_FindParserSymbol(STR_Upper(function)));
};
first of all Wish to everything the best in 2017 for all Gothic fans!
Agreed!
what I can do is just to copy the engine_hooks.d file from Hatrez.Beyond ported adressess looks like a bit diffirent than yours - maybe u see something weird? Hatrez aka chicken is no longer avalaible on themodders.org. Hope u will develop Lego on g1 too. He has ported only 2 adressess, in G2nk there is 6 of them !
const int ASMINT_OP_movESItoEAX = 61577; //0xF089 ev. 50827
const int ASMINT_OP_movEAXtoEDI = 51081; //0xC789
Those are not addresses :p They're opcodes, i.e. short commands in machine code (in Assembly they'd be mov eax, esi and mov edi, eax). Hence they don't need to be ported.
I will have a look at it later, but if there are any problems they will probably involve subtleties that are hard to spot without thorough debugging.
Mark-56 has been working on porting a lot of the necessary addresses for G1 and he sent me the files (if only I remembered where I put them... I assure you they're somehwere though!). There seems to be interest in this, even if it's very unstable, so maybe I can spend some time next year and put together a semi-working version for G1 :)
Hallöchen, ich mal wieder :)
Habe mich mal der EXP-Bar angenommen, die im Wiki als Beispiel aufgeführt ist. Allerdings schleicht sich hier ein Problem ein, dass ich leider nicht in den Griff bekomme.
Folgender Code wird zur Generierung der Bar aufgerufen:
instance expBar(GothicBar) {
x = 100;
y = 20;
barTex = "Bar_Exp.tga";
};
func void expBarLoop() {
var int myBar;
if(!Hlp_IsValidHandle(myBar)) {
myBar = Bar_Create(expBar);
};
Bar_SetMax(MyBar, expMax2);
Bar_SetValue(MyBar, expNow2);
};
func void barLoop() {
expBarLoop();
/*expMax2 = hero.exp_next;
expNow2 = hero.exp;*/
};
Soweit eigentlich nichts anderes als das Beispiel §ugly
Die Funktion barLoop() wird dann in der Startup aufgerufen, als letztes. Zur Sicherheit habe ich die beiden EXP-Variablen dort auch noch einmal gesetzt, da so früh scheinbar nicht auf hero.exp & hero.exp_next zugegriffen werden kann.
[...]
Wld_InsertNPC(HofWolf2, "NW_BIGFARM_FOREST_03_NAVIGATION");
Wld_InsertNPC(HofWolf3, "NW_BIGFARM_FOREST_03_NAVIGATION");
expMax2 = 500;
expNow2 = 0;
FF_ApplyOnce(barLoop);
Sobald ich starte, fliege ich mit einer Access Violation raus. Ich meine mich allerdings zu entsinnen, dass ich schon desöfteren FF's in der Startup registriert habe.
Woran liegt's denn nu: Den Bars oder den FrameFunctions? Oder hab' ich grundsätzlich was dummes übersehen/falsch gemacht?
Falls es hilft, die letzten 100 Zeilen des zSpy, incl. Access Violation:
03:43 Info: 5 C: SND: Creating Sound Instance VWHEEL_TURN (alternative: 0) .... <zSndMss.cpp,#1297>
03:43 Info: 5 N: MSB: numAnis: 4, numMAN: 3 .... <zModelProto.cpp,#4050>
03:43 Info: 5 N: MSB: fpsRates min, max, avg: 25, 25, 25 .... <zModelProto.cpp,#4052>
03:43 Info: 5 C: SND: Creating Sound Instance OW_WINDMEDIUM (alternative: 0) .... <zSndMss.cpp,#1297>
03:43 Info: 5 D: MDL: Loading Model-MDL 'SHEEP_BODY.ASC' .... <zModelProto.cpp,#5019>
03:43 Info: 3 B: ---------- 43% ---------- .... <zViewProgressbar.cpp,#142>
03:43 Info: 3 B: ---------- 44% ---------- .... <zViewProgressbar.cpp,#142>
03:44 Info: 2 N: MSB: Loading Model-Script (binary) 'DOOR_NW_NORMAL_01.MDS' .... <zModelProto.cpp,#4849>
03:44 Info: 5 D: MDL: Loading Model-Hierarchy: DOOR_NW_NORMAL_01.MDH .... <zModelProto.cpp,#1665>
03:44 Info: 3 D: MDL: Loading Model-Mesh: DOOR_NW_NORMAL_01.MDM .... <zModelProto.cpp,#5333>
03:44 Info: 5 N: MSB: numAnis: 7, numMAN: 6 .... <zModelProto.cpp,#4050>
03:44 Info: 5 N: MSB: fpsRates min, max, avg: 25, 25, 25 .... <zModelProto.cpp,#4052>
03:44 Info: 5 C: SND: Creating Sound Instance AMBIENTCRAWLERSCREAM (alternative: 0) .... <zSndMss.cpp,#1297>
03:44 Info: 5 C: SND: Creating Sound Instance AMBIENTSCREAM_2 (alternative: 0) .... <zSndMss.cpp,#1297>
03:44 Info: 3 B: ---------- 45% ---------- .... <zViewProgressbar.cpp,#142>
03:44 Info: 3 D: MORPH: Loading MorphMesh: ITRW_CROSSBOW_L_01.mmb .... <zMorphMesh.cpp,#376>
03:44 Info: 3 B: ---------- 46% ---------- .... <zViewProgressbar.cpp,#142>
03:44 Info: 5 D: MDL: Loading Model-MDL 'ORE_GROUND_REDERZ.ASC' .... <zModelProto.cpp,#5019>
03:44 Info: 5 C: SND: Creating Sound Instance WOOD_DAY1 (alternative: 0) .... <zSndMss.cpp,#1297>
03:44 Info: 3 B: ---------- 47% ---------- .... <zViewProgressbar.cpp,#142>
03:44 Info: 3 B: ---------- 48% ---------- .... <zViewProgressbar.cpp,#142>
03:44 Info: 5 C: SND: Creating Sound Instance OW_BIRD4 (alternative: 0) .... <zSndMss.cpp,#1297>
03:44 Info: 3 D: MORPH: Loading MorphMesh: NW_MORPH_FISH_01.mmb .... <zMorphMesh.cpp,#376>
03:44 Info: 3 D: MORPH: Loading MorphMesh: NW_MORPH_SKATE_01.mmb .... <zMorphMesh.cpp,#376>
03:44 Info: 5 C: SND: Creating Sound Instance ENV_WATER_COAST (alternative: 0) .... <zSndMss.cpp,#1297>
03:44 Info: 3 B: ---------- 49% ---------- .... <zViewProgressbar.cpp,#142>
03:44 Info: 5 C: SND: Creating Sound Instance STONE_SMALL_LOOP (alternative: 0) .... <zSndMss.cpp,#1297>
03:44 Info: 5 D: MDL: Loading Model-MDL 'BENCH_NW_CITY_02.ASC' .... <zModelProto.cpp,#5019>
03:44 Info: 5 D: MDL: Loading Model-MDL 'BEDHIGH_NW_EDEL_01.ASC' .... <zModelProto.cpp,#5019>
03:44 Info: 3 B: ---------- 50% ---------- .... <zViewProgressbar.cpp,#142>
03:44 Info: 3 B: ---------- 51% ---------- .... <zViewProgressbar.cpp,#142>
03:44 Info: 3 B: ---------- 52% ---------- .... <zViewProgressbar.cpp,#142>
03:44 Info: 5 D: MDL: Loading Model-MDL 'SMOKE_WATERPIPE.ASC' .... <zModelProto.cpp,#5019>
03:44 Info: 3 B: ---------- 53% ---------- .... <zViewProgressbar.cpp,#142>
03:44 Info: 5 D: MDL: Loading Model-MDL 'FIREPLACE_NW_LAMP_01.ASC' .... <zModelProto.cpp,#5019>
03:44 Info: 3 B: ---------- 54% ---------- .... <zViewProgressbar.cpp,#142>
03:44 Info: 3 B: ---------- 55% ---------- .... <zViewProgressbar.cpp,#142>
03:45 Info: 5 D: MDL: Loading Model-MDL 'SNA_BODY.ASC' .... <zModelProto.cpp,#5019>
03:45 Info: 5 D: MDL: Loading Model-MDL 'BLO_BODY.ASC' .... <zModelProto.cpp,#5019>
03:45 Info: 3 B: ---------- 56% ---------- .... <zViewProgressbar.cpp,#142>
03:45 Info: 5 C: SND: Creating Sound Instance OW_BIRD11 (alternative: 0) .... <zSndMss.cpp,#1297>
03:45 Info: 5 C: SND: Creating Sound Instance OW_BIRD11 (alternative: 1) .... <zSndMss.cpp,#1297>
03:45 Info: 5 C: SND: Creating Sound Instance OW_BIRD11 (alternative: 2) .... <zSndMss.cpp,#1297>
03:45 Info: 5 C: SND: Creating Sound Instance DRAWSOUND_ME (alternative: 0) .... <zSndMss.cpp,#1297>
03:45 Info: 5 C: SND: Creating Sound Instance DRAWSOUND_ME (alternative: 1) .... <zSndMss.cpp,#1297>
03:45 Info: 5 C: SND: Creating Sound Instance ENV_WATER_WIDEOCEAN (alternative: 0) .... <zSndMss.cpp,#1297>
03:45 Info: 3 B: ---------- 57% ---------- .... <zViewProgressbar.cpp,#142>
03:45 Info: 5 U: WAY: Reading Waynet. .... <zWaynet.cpp,#1607>
03:45 Info: 5 U: WAY: Waynet ok. .... <zWaynet.cpp,#1661>
03:45 Info: 3 B: ---------- 70% ---------- .... <zViewProgressbar.cpp,#142>
03:45 Info: 5 D: ARC: .. Closing (44253 objects) .... <zError.cpp,#465>
03:45 Info: 3 U: GAM: Loading startup-data "Mahlendur\MAHLENDUR.zen" finished. .... <oGame.cpp,#3131>
03:45 Info: 3 B: ---------- 71% ---------- .... <zViewProgressbar.cpp,#142>
03:45 Info: 3 U: GAM: Cleaning world... .... <oGame.cpp,#3138>
03:45 Info: 3 U: GAM: .. finished .... <oGame.cpp,#3140>
03:45 Info: 3 B: ---------- 73% ---------- .... <zViewProgressbar.cpp,#142>
03:45 Info: 3 U: GAM: Calling Startup-Script ... .... <oGame.cpp,#862>
03:45 Info: 3 D: MORPH: Loading MorphMesh: ITRW_BOW_H_01.mmb .... <zMorphMesh.cpp,#376>
03:45 Info: 3 D: MORPH: Loading MorphMesh: ITRW_MIL_CROSSBOW.mmb .... <zMorphMesh.cpp,#376>
03:45 Info: 3 D: MORPH: Loading MorphMesh: ITRW_BOW_M_02.mmb .... <zMorphMesh.cpp,#376>
03:45 Warn: 0 U: SPAWN: Spawnpoint KDF_1405_Ulian not found. Npc KDF_1405_ULIAN cannot be spawned. .... <oSpawn.cpp,#450>
03:45 Warn: 0 U: SPAWN: Spawnpoint NW_FARM04_LARIA not found. Npc BAU_201_LARIA cannot be spawned. .... <oSpawn.cpp,#450>
03:45 Info: 5 C: Shutting down MSS .... <zSndMss.cpp,#629>
03:45 Info: 5 X: EmergencyExit: Releasing all DirectX-Objects ... .... <zRndD3D_Render.cpp,#283>
03:45 Info: 5 X: EmergencyExit: D3DXUninitialize done .... <zRndD3D_Render.cpp,#301>
03:45 Warn: 0 X: [RND3D-Destructor]: Can't uninitialize D3DX Utility Library ! Error: D3DXERR_D3DXNOTSTARTEDYET .... <zRndD3D.h,#127>
03:45 Info: 5 X: [RND3D-Destructor]: D3DXUninitialize done .... <zRndD3D_Render.cpp,#309>
03:45 Warn: 0 == ===================================== UNHANDLED EXCEPTION OCCURED ====================================================== .... <zError.cpp,#474>
03:45 Warn: 0 == ============================================ CRASH INFOS: ============================================================== .... <zError.cpp,#474>
03:45 Warn: 0 Go thic II - 2.6 (fix), Parser Version: 50 .... <zError.cpp,#474>
03:45 Warn: 0 Us er: blender, CPUType: 586, Mem: 2048 MB total, 1149 MB free .... <zError.cpp,#474>
03:45 Warn: 0 St artup Options: .... <zWin32.cpp,#2976>
03:45 Warn: 0 -g ame:mahlendur.ini -zreparse -zwindow -zlog:5,s -zmaxframerate:50 -testmode
.... <zWin32.cpp,#2977>
03:45 Warn: 0 == ============================================= CALLSTACK : ============================================================== .... <zError.cpp,#474>
03:45 Warn: 0 00 23:00791B68 (0x00000235 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+520 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1369+12 byte(s) .... <zError.cpp,#474>
03:45 Warn: 0 00 23:00792504 (0x00000235 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00000373 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00000BC8 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00000C86 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00000CD4 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00012217 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00012298 0x11508A48 0x00AB4118 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x000122CF 0x008B23B3 0x0082E6F0 0x0A5823D4) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792CBF (0x00AB40C0 0x00004900 0x0135FA9C 0x0135FAA0) Gothic2.exe, zCParser::CallFunc()+719 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 15510023:006C92DE (0xFFFFFFFE 0x0135FB00 0x0089078B 0x0135FCA0) Gothic2.exe, oCGame::LoadWorld()+558 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGame.cpp, line 2902 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:00792504 (0x00000373 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00000BC8 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00000C86 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00000CD4 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00012217 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00012298 0x11508A48 0x00AB4118 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x000122CF 0x008B23B3 0x0082E6F0 0x0A5823D4) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792CBF (0x00AB40C0 0x00004900 0x0135FA9C 0x0135FAA0) Gothic2.exe, zCParser::CallFunc()+719 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 15510023:006C92DE (0xFFFFFFFE 0x0135FB00 0x0089078B 0x0135FCA0) Gothic2.exe, oCGame::LoadWorld()+558 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGame.cpp, line 2902 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:00792504 (0x0000070A 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 1415 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:00792504 (0x00000BC8 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00000C86 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00000CD4 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00012217 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00012298 0x11508A48 0x00AB4118 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x000122CF 0x008B23B3 0x0082E6F0 0x0A5823D4) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792CBF (0x00AB40C0 0x00004900 0x0135FA9C 0x0135FAA0) Gothic2.exe, zCParser::CallFunc()+719 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 15510023:006C92DE (0xFFFFFFFE 0x0135FB00 0x0089078B 0x0135FCA0) Gothic2.exe, oCGame::LoadWorld()+558 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGame.cpp, line 2902 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:00792504 (0x00000C86 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00000CD4 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00012217 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00012298 0x11508A48 0x00AB4118 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x000122CF 0x008B23B3 0x0082E6F0 0x0A5823D4) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792CBF (0x00AB40C0 0x00004900 0x0135FA9C 0x0135FAA0) Gothic2.exe, zCParser::CallFunc()+719 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 15510023:006C92DE (0xFFFFFFFE 0x0135FB00 0x0089078B 0x0135FCA0) Gothic2.exe, oCGame::LoadWorld()+558 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGame.cpp, line 2902 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:00792504 (0x00000CD4 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00012217 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00012298 0x11508A48 0x00AB4118 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x000122CF 0x008B23B3 0x0082E6F0 0x0A5823D4) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792CBF (0x00AB40C0 0x00004900 0x0135FA9C 0x0135FAA0) Gothic2.exe, zCParser::CallFunc()+719 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 15510023:006C92DE (0xFFFFFFFE 0x0135FB00 0x0089078B 0x0135FCA0) Gothic2.exe, oCGame::LoadWorld()+558 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGame.cpp, line 2902 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:00792504 (0x00012217 0x00AB4108 0x00000000 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x00012298 0x11508A48 0x00AB4118 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x000122CF 0x008B23B3 0x0082E6F0 0x0A5823D4) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792CBF (0x00AB40C0 0x00004900 0x0135FA9C 0x0135FAA0) Gothic2.exe, zCParser::CallFunc()+719 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 15510023:006C92DE (0xFFFFFFFE 0x0135FB00 0x0089078B 0x0135FCA0) Gothic2.exe, oCGame::LoadWorld()+558 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGame.cpp, line 2902 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:00792504 (0x00012298 0x11508A48 0x00AB4118 0x00AB40C0) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792504 (0x000122CF 0x008B23B3 0x0082E6F0 0x0A5823D4) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792CBF (0x00AB40C0 0x00004900 0x0135FA9C 0x0135FAA0) Gothic2.exe, zCParser::CallFunc()+719 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 15510023:006C92DE (0xFFFFFFFE 0x0135FB00 0x0089078B 0x0135FCA0) Gothic2.exe, oCGame::LoadWorld()+558 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGame.cpp, line 2902 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:00792504 (0x000122CF 0x008B23B3 0x0082E6F0 0x0A5823D4) Gothic2.exe, zCParser::DoStack()+2980 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 14150023:00792CBF (0x00AB40C0 0x00004900 0x0135FA9C 0x0135FAA0) Gothic2.exe, zCParser::CallFunc()+719 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 15510023:006C92DE (0xFFFFFFFE 0x0135FB00 0x0089078B 0x0135FCA0) Gothic2.exe, oCGame::LoadWorld()+558 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGame.cpp, line 2902 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:00792CBF (0x00AB40C0 0x00004900 0x0135FA9C 0x0135FAA0) Gothic2.exe, zCParser::CallFunc()+719 byte(s), P:\dev\g2addon\release\ZenGin\_ulf\zParser.cpp, line 15510023:006C92DE (0xFFFFFFFE 0x0135FB00 0x0089078B 0x0135FCA0) Gothic2.exe, oCGame::LoadWorld()+558 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGame.cpp, line 2902 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:006C1E96 (0x0135FBD4 0x0A5823D4 0x0135FBD0 0x0135FB00) Gothic2.exe, oCGame::CallScriptStartup()+550 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGame.cpp, line 8660023:006C9FD9 (0x0135FA9C 0x0135FB6C 0xFFFFFFFE 0x0135FB6C) Gothic2.exe, oCGame::LoadWorldStartup()+969 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGame.cpp, line 31490023:00502DFD (0x0135FEC8 0x00000000 0x014E3862 0x00000001) Gothic2.exe, WinMain()+141 byte(s), P:\dev\g2addon\release\ZenGin\_carsten\zWin32.cpp, line 1054+17 byte(s) .... <zError.cpp,#474>
03:45 Warn: 0 00 23:006C9FD9 (0x0135FA9C 0x0135FB6C 0xFFFFFFFE 0x0135FB6C) Gothic2.exe, oCGame::LoadWorldStartup()+969 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGame.cpp, line 31490023:00502DFD (0x0135FEC8 0x00000000 0x014E3862 0x00000001) Gothic2.exe, WinMain()+141 byte(s), P:\dev\g2addon\release\ZenGin\_carsten\zWin32.cpp, line 1054+17 byte(s) .... <zError.cpp,#474>
03:45 Warn: 0 00 23:006C92DE (0xFFFFFFFE 0x0135FB00 0x0089078B 0x0135FCA0) Gothic2.exe, oCGame::LoadWorld()+558 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGame.cpp, line 2902 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:006C6696 (0xFFFFFFFE 0x0135FBD0 0x0082E6F0 0x00000000) Gothic2.exe, oCGame::LoadGame()+246 byte(s), P:\dev\g2addon\release\Gothic\_ulf\oGame.cpp, line 2147 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:00429BF9 (0x00000000 0x00400000 0x014E3862 0x0135FEC4) Gothic2.exe, CGameManager::Menu()+2345 byte(s), P:\dev\g2addon\release\Gothic\_bert\oGameManager.cpp, line 1474 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:00425C35 (0x0082F0EC 0x00000001 0x00170ABE 0x0A5823D4) Gothic2.exe, CGameManager::Run()+1029 byte(s), P:\dev\g2addon\release\Gothic\_bert\oGameManager.cpp, line 713 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:0078188B (0x0000002C 0x001A5A36 0x00000011 0x00000000) Gothic2.exe, MainProg()+75 byte(s), P:\dev\g2addon\release\Gothic\_ulf\Phoenix.cpp, line 111 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:00503270 (0x00400000 0x00000000 0x014E3862 0x00000001) Gothic2.exe, HandledWinMain()+928 byte(s), P:\dev\g2addon\release\ZenGin\_carsten\zWin32.cpp, line 1169 .... <zError.cpp,#474>
03:45 Warn: 0 00 23:00502DFD (0x0135FEC8 0x00000000 0x014E3862 0x00000001) Gothic2.exe, WinMain()+141 byte(s), P:\dev\g2addon\release\ZenGin\_carsten\zWin32.cpp, line 1054+17 byte(s) .... <zError.cpp,#474>
03:45 Warn: 0 00 23:007D43F8 (0x00000004 0x0000FFFF 0x000000B8 0x00000000) Gothic2.exe, WinMainCRTStartup()+224 byte(s) .... <zError.cpp,#474>
03:45 Warn: 0 == ===================================== UNHANDLED EXCEPTION OCCURED ====================================================== .... <zError.cpp,#474>
03:49 Warn: 0 B: GMAN: gameSession is existing. Call CGameManager::Done() before! .... <oGameManager.cpp,#375>
03:49 Info: 3 B: VP: zBinkPlayer deinitialized .... <zBinkPlayer.cpp,#62>
03:49 Info: 2 N: MSB: Loading Model-Script (binary) 'HUMANS.MDS' .... <zModelProto.cpp,#4849>
03:49 Info: 5 D: MDL: Loading Model-Hierarchy: HUMANS.MDH .... <zModelProto.cpp,#1665>
Warum genau es crasht kann ich dir jetzt nicht sagen, aber LeGo-Kram gehört in die INIT_Global() (oder später). Die Startup ist zu früh, da ist LeGo noch nicht initialisiert und wer weiß was dann passiert.
Mehr oder weniger aus genau diesem Grund gibt es den Suffix -Once für die ganzen FF-Funktionen, dann macht das keine Probleme.
Ach, richtig... Die Reihenfolge §enton
Ich glaube, ich sollte mir das mal aufschreiben §ugly
Gut, jetzt startet es zumindest schonmal, den Rest sollte ich hinbekommen, danke dir :)
Ich glaube, ich sollte mir das mal aufschreiben §ugly
Gut, jetzt startet es zumindest schonmal, den Rest sollte ich hinbekommen, danke dir :)
Ich denke eher, ich sollte das mal aufschreiben. Ich hab das immer für selbstverständlich gehalten (warum etwas initialisieren, wenn man es sowieso schon vorher benutzen kann?), aber das ist irgendwie eine unfaire Annahme. Ich schreib gleich mal 'n kurzen Satz in die ReadMe.
Sehr schön :)
Okay, doch nochmal ich :)
Funktioniert jetzt alles soweit, zumindest technisch, logisch ist das 'ne andere Sache, aber egal :D
Zwei oder drei Seiten weiter vorne habe ich mich ja ein wenig mit den Buffs beschäftigt und es ja auch (Danke nochmal dafür, Lehona!) geschafft, die zurecht zu schieben.
Nun habe ich mithilfe der Bars das Interface etwas überarbeitet. Natürlich passen die Buffs jetzt nicht mehr. "Gut, kein Problem", denkt sich der Dada und fängt an, alles in mühseliger Kleinarbeit an seinen Platz zu rücken. Denkt sich dann "Teste das doch mal in richtiger Auflösung" und: Zack! Sieht natürlich kacke aus, etwas verschoben §ugly
Die drei Bars, die ich eingerichtet habe, werden mit
y = Print_Screen[PS_Y] - 30;
positioniert. Dabei wird der Wert immer etwas angepasst, um genau zu sein -60 und -90.
Dabei handelt es sich ja, wenn ich das richtig verstanden habe um virtuelle Koordinaten, also maximale y-Koordinate - 30 virtuelle Pixel.
Die Buffs habe ich versucht entsprechend anzupassen, in etwa so:
v = View_Create((((100+xsize)*k)+100), Print_Screen[PS_Y]-150, ((100+xsize)*k+xsize+100), Print_Screen[PS_Y]-200);
Und ich befürchte, dort liegt mein Denkfehler.
Vermutlich hab ich den Unterschied zwischen virtuellen und pixelgenauen Koordinaten noch nicht ganz geblickt.
Hat vielleicht jemand eine kurze Erklärung über den Unterschied für mich? Und vielleicht einen kleinen Hinweis, welches Koordinatensystem bei welchem der obigen beiden Anwendungsfälle benutzt wird?
Liebe Grüße
Dada :)
Pixelgenaue Koordinaten sind genau das, was der Name verspricht :p Würdest du einen Punkt an die Koordinaten (512, 384) setzen, erscheint er für einen Spieler mit der Auflösung 1024x768 genau in der Mitte, für jemanden mit der Auflösung 800x600 allerdings irgendwo unten rechts (4. Quadrant). Die maximalen Koordinaten (exakt unten Rechts) ist also abhängig von der Auflösung, die der Spieler gewählt hat. Damit man nicht selber herausfinden muss, in welcher Auflösung der Spieler spielt, gibt es die Variablen Print_Screen[PS_Y] und Print_Screen[PS_X], die jeweils die Höhe und Breite enthalten.
Virtuelle Koordinaten versprechen quasi genau das Gegenteil. Sie sind immer zwischen 0 und 8191 (oder 8192?) inklusive, egal, auf welcher Auflösung der Spieler spielt.
Das heißt, dass ein Punkt an den Koordinaten (4096, 4096) immer genau in der Mitte angezeigt wird.
Um beliebig zwischen diesen beiden Formaten zu wechseln, gibt es Print_ToVirtual() (http://lego.worldofplayers.de/?Interface#Print_ToVirtual) und Print_ToPixel() (http://lego.worldofplayers.de/?Interface#Print_ToPixel).
Wenn du ein Interface designst wirst du üblicherweise nicht drumrum kommen, diese beiden Methoden zu mischen.
Edit: View-Funktionen, die pixelgenaue Koordinaten erwarten, tragen üblicherweise das Suffix Pxl, d.h. standardmäßig werden virtuelle Koordinaten benutzt. Das ist in den meisten Fällen nämlich genau das, was man von seinem Code erwartet :)
P.S.: Ich glaube, dass in manchen Randfällen die Auflösung nicht korrekt berechnet/ausgelesen wird. Dafür gibt es aber schon einen Fix, der irgendwann seinen Weg in ein Release finden wird :)
Wenn ich mir die Buff- und Bar-Pakete so ansehe, nutzen die Buffs also pixelgenaue Koordinaten (sonst käme man ja nicht auf Werte wie 7000 und 7500). Die Bars hingegen virtuelle, da sonst die Rechnung Print_Screen[PS_X]-x keinen Sinn ergeben würde :p
Wenn ich das richtig verstehe, müsste ich also etwa folgende Berechnung durchführen und nutzen:
var int maxy1; maxy1 = Print_ToPixel(Print_Screen[PS_Y]-150, PS_Y);
var int maxy2; maxy2 = Print_ToPixel(Print_Screen[PS_Y]-200, PS_Y);
v = View_Create((((100+xsize)*k)+100), maxy1, ((100+xsize)*k+xsize+100), maxy2);
Leider hilft das ganze überhaupt nicht.
Ich vermute einfach mal, ich habe einen ganz banalen Denkfehler darin. Jemand vielleicht einen Geistesblitz für mich?
Edit: Habe gerade deinen Edit gelesen, aber leider nicht ganz in meinen Schädel bekommen. Betrifft mich das überhaupt?
Und auf deinen Hinweis mit der Auflösung habe ich mir die Print_GetScreenSize einmal angesehen. Dort wird der Wert zVidResFullscreenX ausgelesen. Ich teste gerade im Fenstermodus, macht das einen Unterschied? Oder wird immer die tatsächliche Spielgröße ausgelesen und nicht die Bildschirmauflösung?
Da hat du jetzt irgendwas durcheinander geworfen, es ist genau andersrum. Pixelgenaue Koordinaten, die über 7000 gehen? Will ich sehen, wer so eine Auflösung hat :p
Print_Screen[PS_X] und Print_Screen[PS_Y] sind natürlich ebenfalls in pixelgenauen Koordinaten (denn sonst wären es einfach nur Konstanten), schließlich gehen virtuelle Koordinaten immer bis 8191.
Wenn du möchtest, dass die Buffs immer genau 50px hoch sind, musst du Print_ToVirtual() verwenden, nicht Print_ToPixel().
View_Create() hat offensichtlicherweise auch nicht das Suffix Pxl, es erwartet also virtuelle Koordinaten. Vergleiche dazu View_CreatePxl() (http://lego.worldofplayers.de/?View#View_CreatePxl).
Zum Fenstermodus kann ich ehrlich gesagt nichts sagen §kratz Der angesprochene Fix verlässt sich aber nicht mehr auf die .ini-Einträge, das dürfte damit also korrekt behandelt werden.
Probleme mit dem Auslesen der Auflösung gab/gibt es - meines Wissens - nur, wenn z.B. während des Spielens die Auflösung geändert wurde, d.h. in Randfällen.
Ich bin mir gerade nicht sicher, wie oft ich diesen Satz diese Woche schon von mir gegeben habe: "Ich hasse dumme Fehler, sie sind dumm und kosten Zeit!"
Dieser Ansatz ist fast richtig:
var int maxy1; maxy1 = Print_ToPixel(Print_Screen[PS_Y]-150, PS_Y);
var int maxy2; maxy2 = Print_ToPixel(Print_Screen[PS_Y]-200, PS_Y);
v = View_Create((((100+xsize)*k)+100), maxy1, ((100+xsize)*k+xsize+100), maxy2);
Wenn man nun bedenkt, dass PS_Y ganz unten liegt, kann es ja nicht funktionieren, wenn y2 oberhalb von y1 liegt. Zumindest wenn ich das Prinzip richtig verstanden habe §ugly
Korrigiert sieht das Ganze dann so aus:
var int maxy1; maxy1 = Print_ToPixel(Print_Screen[PS_Y]-200, PS_Y);
var int maxy2; maxy2 = Print_ToPixel(Print_Screen[PS_Y]-150, PS_Y);
v = View_Create((((100+xsize)*k)+100), maxy1, ((100+xsize)*k+xsize+100), maxy2);
Ich bin mir nicht sicher, ob der Code schön geschrieben ist, aber er funktioniert :D
Ich muss mal ganz frech fragen: hast du meinen Post nicht gelesen? §kratz
Nach deinem Code sollten die Buffs jetzt irgendwo im ersten Quadranten liegen, anstatt im dritten.
Du wendest Print_ToPixel() auf bereits pixelgenaue Koordinaten an! Das ist ungefähr das Äquivalent von FahrenheitToCelsius(25°C) oder DollarToEuro(100€). Da Daedalus nicht typsicher ist, gibt es keinen direkten Fehler, aber Murks kommt trotzdem raus.
Wenn ich deinen Code teste, sehe ich gar keine Icons; das liegt vermutlich daran, dass meine Auflösung zu klein ist (800x600 im Fenstermodus).
Na toll. *Hier Satz über dumme Fehler einfügen*
Der Code in meinen Dateien ist richtig. Für den Post habe ich dummerweise den Code aus dem alten Post verwendet.
var int maxy1; maxy1 = Print_Screen[PS_Y]-150;
var int maxy2; maxy2 = Print_Screen[PS_Y]-200;
v = View_Create((((100+xsize)*k)+100), maxy1, ((100+xsize)*k+xsize+100), maxy2);
So steht's jetzt in der Buffs.d drin und wird immer schön oberhalb der Bars angezeigt, egal welche Auflösung :D
Aber ja, deinen Post habe ich gelesen. Dadurch sind mir zwar im ersten Moment ein paar Synapsen durchgebrannt, aber das waren scheinbar genau die, die mich blockiert haben §ugly
Achso, getestet habe ich es mit derselben Auflösung ^2^
Hello §wink I have small problem with Render. I assign texture to render:
if(!Hlp_IsValidHandle (vptr))
{
vptr = View_Create(x,y,x+512,y+512);
View_SetTexture(vptr,aCQuickslot_Texture);
View_AddText(vptr,2048,512,IntToString(slot),PF_Font);
Render_AddView(vptr); //Thanks@Splash!
MEM_WriteStatArr(_QS_BG_View,slot,vptr);
};
But When i trying destroy this:
func void _QS_RemoveRender(var int slot)
{
var int rptr; rptr = MEM_ReadStatArr(_QS_Render,slot); //Render pointer
var int vptr; vptr = MEM_ReadStatArr(_QS_BG_View,slot); //View pointer
if(Hlp_IsValidHandle(rptr))
{
Render_CloseView(rptr);
Render_Remove(rptr);
if(Hlp_IsValidHandle(vptr))
{
var RenderItem r; r=get(rptr);//not work :<
View_Close(vptr);//View_Delete = Crash...
r.view = 0;//not work :<
r.view_open = 0; //not work :<
MEM_WriteStatArr(_QS_BG_View,slot,0);
};
MEM_WriteStatArr(_QS_Render,slot,0);
};
};
Texture is back (without number)... How i can destroy this? I'm trying all, but all isn't working.
Ok. This Is work :) I must save texture to varriable.
Okay, ich mal wieder...
Lehona, ich habe mal versucht dein Respawnsystem, basierend auf PermMem und ForEachHndl einzupflegen. Das tolle ist: Ich hab's sogar verstanden! §ugly
Nur meckert Gothic beim Parsen der Scripte. Ihm würde hier ein ";" fehlen:
class RespawnObject {
var int inst;
var string wp;
var int respawnDay;
};
instance RespawnObject@(RespawnObject);
func void AddToRespawnArray(var c_npc slf) {
var int hndl; hndl = new(RespawnObject@);
var RespawnObject myRespawnObject; myRespawnObject = get(hndl);
myRespawnObject.inst = Hlp_GetInstanceID(slf);
myRespawnObject.wp = slf.spawnPoint;
myRespawnObject.respawnDay = Wld_GetDay() + r_MinMax(2,8);
};
func void checkRespawns() {
ForEachHndl(RespawnObject@, _CheckRespawns);
};
func void _CheckRespawns(var int hndl) {
var RespawnObject myRespawnObject; myRespawnObject = get(hndl);
if (myRespawnObject.respawnDay <= Wld_GetDay()) {
Wld_InsertNpc(myRespawnObject.inst, myRespawnObject.wp);
delete(hndl);
};
return rContinue;
};
Wie man sehen kann, ist der Code (fast) vollständig kopiert, daher meine Verwirrung. Lasse ich da nur ein return; stehen, wird der Code ohne zu Murren akzeptiert. Das kann aber nicht der Sinn sein.
Daher meine übliche Frage: Bin ich zu blöd, oder läuft da tatsächlich etwas schief?
Nein, das muss einfach eine Funktion sein, die auch einen Integer zurückgibt, d.h. du musst das 'void' in der Signatur durch 'int' ersetzen.
Ich kann leider den ursprünglichen Beitrag nicht mehr editieren.
Milky-Way
24.01.2017, 16:06
Nein, das muss einfach eine Funktion sein, die auch einen Integer zurückgibt, d.h. du musst das 'void' in der Signatur durch 'int' ersetzen.
Ich kann leider den ursprünglichen Beitrag nicht mehr editieren.
um welchen Beitrag / welches Thema geht es? (Link bitte)
Ich kann das kurz wieder aufmachen.
um welchen Beitrag / welches Thema geht es? (Link bitte)
Ich kann das kurz wieder aufmachen.
http://forum.worldofplayers.de/forum/threads/1126551-Skriptpaket-LeGo-2/page4?p=19677272&viewfull=1#post19677272
Du kannst auch einfach die "void _CheckRespawn" durch "int _CheckRespawn" ersetzen.
Oh... Ja, das gibt Sinn.
Also doch nicht nur abschreiben und verstehen, sondern auch durchdenken §ugly
Milky-Way
24.01.2017, 22:02
http://forum.worldofplayers.de/forum/threads/1126551-Skriptpaket-LeGo-2/page4?p=19677272&viewfull=1#post19677272
Du kannst auch einfach die "void _CheckRespawn" durch "int _CheckRespawn" ersetzen.
erledigt :)
Ich habe gestern noch einen kleinen Schatz in den LeGo-Scripten entdeckt, den ich wohl vor vielen Monaten gescriptet, aber niemals dokumentiert hatte: final() (http://lego.worldofplayers.de/?Beispiele_Final).
Man kann damit die final-clause von Java emulieren, d.h. ein bestimmter Codeblock wird bei Verlassen der Funktion ausgeführt, unabhängig davon, wann oder wo die Funktion verlassen wird.
Da es keine Exceptions o.ä. gibt, ist das sicherlich weit weniger nützlich als in "richtigen" Sprachen, aber vielleicht ist es ja für irgendjemanden nützlich. Außerdem ist es ein weiteres (relativ einfaches) Beispiel für diese ganz besondere Art von Meta-Programming, die Daedalus hervorgebracht hat (wie z.B. while(), locals(), repeat()).
Ich habe gestern noch einen kleinen Schatz in den LeGo-Scripten entdeckt, den ich wohl vor vielen Monaten gescriptet, aber niemals dokumentiert hatte: final() (http://lego.worldofplayers.de/?Beispiele_Final).
Man kann damit die final-clause von Java emulieren, d.h. ein bestimmter Codeblock wird bei Verlassen der Funktion ausgeführt, unabhängig davon, wann oder wo die Funktion verlassen wird.
Ist definiert, was passiert, wenn der Block und/oder die Funktion mit einem Rückgabewert verlassen wird?
func int testFinalIntConst() {
if (final()) {
return 2;
};
return 1;
};
func int testFinalIntReturn() {
return 1;
if (final()) {
return 2;
};
return 3;
};
func int testFinalIntStatic() {
var int result; result = 1;
if (final()) {
result = 2;
// return result;
};
return result;
};
...Beispiel für local() fehlt hier :)
Ich habe gerade keinen Zugang zu meinem Rechner (um es zu testen), aber Daedalus ist ja zum Glück recht vorhersehbar:
Da return x; einfach nur x auf den Stack pusht und dann aus der Funktion springt, wird man damit den Stack korrumpieren. In der dritten Funktion wird der Return-Wert auf 2 aktualisiert.
Guter Input, diese Randfälle sind nicht unbedingt ersichtlich. Ich werde sie so oder so ähnlich in die Dokumentation aufnehmen.
Frank-95
08.02.2017, 13:29
Don't know if you know that, but in the latest release of lego visualfx.d is missing from resources folder. §wink
Kann es sein, dass die Buffs nicht speichersicher sind?
Wenn ich einen Spielstand lade und danach einen Buff anwende, stürzt das Spiel mit Access Violation ab.
Ikarus Stacktrace:
02:19 Info: 5 C: SND: Creating Sound Instance INV_CLOSE (alternative: 0) .... <zSndMss.cpp,#1297>
02:19 Fault: 0 Q: [start of stacktrace]
02:19 Fault: 0 Q: MEM_WRITEINT_() + 35 bytes
02:19 Fault: 0 Q: MEM_WRITEINTARRAY(0, 0, 192) + 45 bytes
02:19 Fault: 0 Q: MEM_ARRAYINSERT(381066432, 192) + 238 bytes
02:19 Fault: 0 Q: BUFFLIST_ADD(192) + 58 bytes
02:19 Fault: 0 Q: BUFF_APPLY((instance)317719312, 19197) + 215 bytes
02:19 Fault: 0 Q: USETESTBUFF() + 217 bytes
02:19 Fault: 0 Q: [end of stacktrace]
02:19 Fault: 0 Q: Exception handler was invoked. Ikarus tried to print a Daedalus-Stacktrace to zSpy. Gothic will now crash and probably give you a stacktrace of its own.
Der Vollständigkeit halber einmal die beiden Buffs, die mit UseTestbuff() applied werden:
instance maxManaBuff(lCBuff)
{
name = "Mana-Erhöhung";
bufftype = BUFF_GOOD;
durationMS = 10*1000;//300000;
onApply = SAVE_GetFuncID(maxManaBuff_Apply);
onRemoved = SAVE_GetFuncID(maxManaBuff_Remove);
buffTex = "manabuff.tga";
};
func void maxManaBuff_Apply(var int bh)
{
normalMana = hero.attribute[ATR_MANA_MAX];
var int ptr; ptr = Buff_GetNpc(bh);
if (!ptr) { return; };
var c_npc n; n = _^(ptr);
n.attribute[ATR_MANA_MAX] = 100;
n.attribute[ATR_MANA] = 100;
};
func void maxManaBuff_Remove (var int bh)
{
hero.attribute[ATR_MANA_MAX] = normalMana;
hero.attribute[ATR_MANA] = normalMana;
};
instance maxHealthBuff(lCBuff)
{
name = "Lebensbonus";
bufftype = BUFF_GOOD;
durationMS = 10*1000;//300000;
onApply = SAVE_GetFuncID(maxHealthBuff_Apply);
onRemoved = SAVE_GetFuncID(maxHealthBuff_Remove);
buffTex = "healthbuff.tga";
};
func void maxHealthBuff_Apply(var int bh)
{
normalHealth = hero.attribute[ATR_HITPOINTS_MAX];
var int ptr; ptr = Buff_GetNpc(bh);
if (!ptr) { return; };
var c_npc n; n = _^(ptr);
n.attribute[ATR_HITPOINTS_MAX] = 250;
n.attribute[ATR_HITPOINTS] = 250;
};
func void maxHealthBuff_Remove (var int bh)
{
hero.attribute[ATR_HITPOINTS_MAX] = normalHealth;
hero.attribute[ATR_HITPOINTS] = normalHealth;
};
Das sieht für mich nicht nach einem Buff-spezifischem Problem aus, eher als würden Arrays (zCArray) nicht richtig aus dem Savegame geladen. Ich schau mir das mal genauer an - falls ich es nicht reproduzieren kann, melde ich mich nochmal.
Gut, dachte halt spontan daran, weil es bisher nur auftritt, wenn ich nach dem Laden einen Buff anwenden möchte :)
Ich glaube ich habe den Fehler behoben (auch wenn die Symptome etwas merkwürdig waren). Wenn PM versucht hat, ein leeres Array aus dem Savegame zu laden, wurde MEM_Alloc(0) ausgeführt, was dann später wohl für Verwirrung gesorgt hat :p Der Fehler tritt nämlich nicht auf, wenn man speichert, während noch ein Buff aktiv ist. Der Fix ist in der nächsten LeGo-Version enthalten. Falls du ihn jetzt schon beheben möchtest:
In der PermMem.d Zeile 1620 durch folgendes ersetzen:
if (oa.elements > 0) {
ptr = MEM_Alloc(oa.elements * 4);
};
...
Und das, liebe Freunde, nenne ich vorbildlichen Support!
Super, läuft nun wie geschmiert ^2^
Btw, 1600+ Zeilen... "Ein etwas umfangreicheres Paket" ist sehr nett ausgedrückt... :D
Btw, 1600+ Zeilen... "Ein etwas umfangreicheres Paket" ist sehr nett ausgedrückt... :D
PermMem ist (und bleibt) vermutlich das tollste, was jemals in LeGo enthalten sein wird. Für viele Modentwickler mag FrameFunctions diesen Platz einnehmen, aber PM löst ein schwieriges Problem (persistenter Speicher) auf so natürliche und intuitive Weise, dass man manchmal fast vergisst, dass man mit Daedalus arbeitet und nicht mit z.B. C. Für einen "Library-Dev" wirklich ein reinster Segen (Außerdem macht mich das dann quasi zu einem Library-Developer-Developer, ich bin quasi noch eine Stufe darüber - das wollte ich schon immer mal sagen).
GiftGrün
13.02.2017, 22:37
Du entwickelst Bibliotheksentwickler? §ugly
Du entwickelst Bibliotheksentwickler? §ugly
Ich hab hier bestimmt schon den einen oder anderen Entwickler groß gezogen, im übertragenen Sinne. Naja, zumindest beigetragen habe ich bestimmt (genau wie der Rest des Editings). Aber Bibliotheksentwicklungsentwickler passt wohl besser. Ich hätte bei "Library-Dev-Dev" bleiben müssen, das ist mehrdeutig ;)
F a w k e s
14.02.2017, 17:24
Hello folks,
I have recently (2 weeks ago) discovered that there is LeGo (Lehona, Gottfried - thank you this package is really cool!) port for G1. Levitation was something I wanted to have in my small mod for quite a while, and today I was finally successful, so wanted to share this with you, maybe somebody else will find it useful as well. I think there might be a better way on how to do this, I am just a beginner in terms of Ikarus and LeGo stuff so please do not be too harsh on me ;)
//Requires LeGo G1
var int flg_Hero_Levitation;
//Old Instruction zCAIPlayer__CheckPhysics
var int OI_zCAIPlayer__CheckPhysics;
//[G1]
//004FE140 .text Debug data ?CheckPhysics@zCAIPlayer@@AAEXXZ
const int zCAIPlayer__CheckPhysics = 5235008;
FUNC VOID _HOOK_CHECK_PHYSICS ()
{
if (flg_Hero_Levitation == TRUE)
{
var int npcptr;
npcptr = MEM_ReadInt (ESP + 4);
var oCNPC npc;
npc = _^(npcptr);
//If this is player - jump out of the original function
if NPC_IsPlayer (npc)
{
//If I am following this properly >> HookEngine (zCAIPlayer__CheckPhysics, 6, "_HOOK_CHECK_PHYSICS");
//it means we have shifted original function by 6 bytes ?
//So store original instruction from address zCAIPlayer__CheckPhysics + 6 into int OI_zCAIPlayer__CheckPhysics
//and insert to this address ASMINT_OP_retn - this should exit the original function
if (OI_zCAIPlayer__CheckPhysics == 0)
{
OI_zCAIPlayer__CheckPhysics = MEM_ReadInt (zCAIPlayer__CheckPhysics + 6);
MemoryProtectionOverride (zCAIPlayer__CheckPhysics + 6, 4);
MEM_WriteByte (zCAIPlayer__CheckPhysics + 6, ASMINT_OP_retn);
};
} else
//If this is not player - continue with original function
{
//if we have replaced original instruction then put it back
if (OI_zCAIPlayer__CheckPhysics != 0)
{
//Insert into address zCAIPlayer__CheckPhysics + 6 original instruction OI_zCAIPlayer__CheckPhysics
MemoryProtectionOverride (zCAIPlayer__CheckPhysics + 6, 4);
MEM_WriteInt (zCAIPlayer__CheckPhysics + 6, OI_zCAIPlayer__CheckPhysics);
OI_zCAIPlayer__CheckPhysics = 0;
};
};
} else
{
//if levitation effect is off and original instruction was replaced - put it back
if (OI_zCAIPlayer__CheckPhysics != 0)
{
MemoryProtectionOverride (zCAIPlayer__CheckPhysics + 6, 4);
MEM_WriteInt (zCAIPlayer__CheckPhysics + 6, OI_zCAIPlayer__CheckPhysics);
OI_zCAIPlayer__CheckPhysics = 0;
};
};
};
const int hookOnce = 0;
func void Init_Global ()
{
...
if (!hookOnce)
{
OI_zCAIPlayer__CheckPhysics = 0;
HookEngine (zCAIPlayer__CheckPhysics, 6, "_HOOK_CHECK_PHYSICS");
hookOnce = 1;
};
};
INSTANCE Levitation_Potion (C_Item)
{
name = NAME_Trank;
mainflag = ITEM_KAT_POTIONS;
Flags = ITEM_MULTI;
value = 0;
visual = "ItFo_Potion_Mana_01.3ds";
material = MAT_GLAS;
on_state[0] = Use_Levitation_Potion;
scemeName = "POTIONFAST";
description = "Potion of levitation";
text[5] = NAME_Value; count[5] = Value;
};
FUNC VOID Use_Levitation_Potion ()
{
if (NPC_IsPlayer (self))
{
flg_Hero_Levitation = TRUE;
};
};
Ich schmeiß hier nochmal 'ne kurze Frage rein. Die HookEngine habe ich soweit (glaube ich :D) verstanden. Nur bereiten mir gerade die Offsets Kopfschmerzen.
Bspw. habe ich die Funktion an der Adresse 0x0075A660/7710304. In Zerxes Liste ist ein Offset von 22 angegeben. Berechnet sich dann die eigentliche Adresse auf 7710326? Oder habe ich da einen Denkfehler?
Ich habe dir die Funktion aus IDA einfach mal kopiert:
.text:0075A660 ; protected: int __thiscall oCNpc::EV_StopProcessInfos(class oCMsgConversation *)
.text:0075A660 ?EV_StopProcessInfos@oCNpc@@IAEHPAVoCMsgConversation@@@Z proc near
.text:0075A660 56 push esi
.text:0075A661 8B F1 mov esi, ecx
.text:0075A663 E8 28 51 F0 FF call ?GetInformationManager@oCInformationManager@@SAAAV1@XZ ; oCInformationManager::GetInformationManager(void)
.text:0075A668 8B C8 mov ecx, eax
.text:0075A66A E8 D1 6B F0 FF call ?Exit@oCInformationManager@@QAIXXZ ; oCInformationManager::Exit(void)
.text:0075A66F 8B CE mov ecx, esi
.text:0075A671 E8 1A 29 F6 FF call ?StopTalkingWith@oCNpc@@QAEXXZ ; oCNpc::StopTalkingWith(void)
.text:0075A676 B8 01 00 00 00 mov eax, 1
.text:0075A67B 5E pop esi
.text:0075A67C C2 04 00 retn 4
.text:0075A67C ?EV_StopProcessInfos@oCNpc@@IAEHPAVoCMsgConversation@@@Z endp
Ganz kurz was zur Darstellung: Ganz links sind die Adressen, ganz rechts sind die Instruktionen und "mittig" stehen die tatsächlichen Bytes, wie sie in der .exe vorhanden sind.
Um eine Funktion zu hooken, werden 5 Byte "Platz" benötigt. Diese 5 Byte haben aber zwei wichtige Einschränkungen:
Es dürfen keine Instruktionen "zerschnitten" werden. Würdest du also am Anfang der Funktion hooken, würden 8 Byte überschrieben werden müssen, ansonsten "zerschneidest" du die dritte Instruktion (call).
Diese 5+ Bytes dürfen keine JMPs oder CALLs enthalten (um genau zu sein: Keine Instruktionen, die relative Offsets enthalten). Da die Instruktionen innerhalb dieser 5+ Bytes an einen anderen Ort kopiert werden, würden die relativen Offsets dann an ganz andere Stellen zeigen.
Die erste Position, die diese Anforderungen erfüllt, ist 75A676h, was genau 22 Bytes nach dem Anfang ist. Als Länge (oldInstr) kannst du hier "5" angeben, damit wird einfach nur die mov-Instruktion überschrieben, die genau 5 Byte lang ist.
Okay, danke, dann habe ich tatsächlich richtig gedacht :D Nur die Funktion stimmt nicht. Da finde ich aber auch noch die richtige §ugly
Kurz zum Verständnis: Woran würde ich erkennen, wo beide Bedingungen zutreffen? Ich hätte mich spontan an den 00 Bytes orientiert. Und vor allem: Wie komme ich da auf das Offset? Ich habe für mich einfach mal die Bytes vor 0x0075A676 gezählt und bin dabei auf die besagten 22 gekommen. Denke ich richtig?
Okay, danke, dann habe ich tatsächlich richtig gedacht :D Nur die Funktion stimmt nicht. Da finde ich aber auch noch die richtige §ugly
Kurz zum Verständnis: Woran würde ich erkennen, wo beide Bedingungen zutreffen? Ich hätte mich spontan an den 00 Bytes orientiert. Und vor allem: Wie komme ich da auf das Offset? Ich habe für mich einfach mal die Bytes vor 0x0075A676 gezählt und bin dabei auf die besagten 22 gekommen. Denke ich richtig?
Wenn du mir sagst, was du suchst, kann ich dir bestimmt helfen :p Finde mich mittlerweile in der gothic2.exe ganz gut zurecht, denke ich.
Am einfachsten ist es, wenn du Zerxes' Liste verwendest: Das Script, mit dem er sie erstellt hat, beachtet diese Bedingungen und liefert nur gültige Offsets + Instruktionslängen.
Naja, da links die Adressen stehen, kannst du einfach die Differenz berechnen: Von der mov-Instruktion (75A676h) bis zur nächsten Instruktion, der pop-Instruktion (75A67Bh) sind es genau 5 Byte Unterschied. Da Kopfrechnen mit hexadezimalen Zahlen immer etwas ungewohnt und langsam ist, lasse ich mir in IDA die tatsächlichen Bytes direkt dazu anzeigen (bei der mov-Instruktion wäre das B8 01 00 00 00), die lassen sich nämlich ganz einfach zählen :p Und damit du keine JMP/CALL-Instruktionen überschreibst, schaust du eben rechts im Text, dass in den jeweiligen Zeilen kein JMP/CALL steht. Kleine Einschränkungen: Es gibt noch ein paar "verwandte" Instruktionen, die zwar auch relative jumps sind, aber nicht direkt "jmp" heißen. Aus dem Kopf fallen mir da vor allem die "conditional jumps" ein (manchmal auch als jcc bezeichnet, das steht für sowas wie "jump conditional code", z.B. "jnz" - "jump not zero" oder "jge" - "jump greater or equal").
Wenn du mir sagst, was du suchst, kann ich dir bestimmt helfen :p Finde mich mittlerweile in der gothic2.exe ganz gut zurecht, denke ich.
Etwas, das man auch ganz gut ohne Ikarus lösen kann §ugly Ich habe ja neue Lebens-/Mana-/EP-Balken erstellt, mithilfe von LeGo-Bars, jedoch werden die natürlich auch während der Dialoge angezeigt. Wäre ja nicht unbedingt schlimm, würden sie nicht komplett in die InfoBox reinragen. Wollte daher beim Starten des Dialogs die Balken löschen, beim Beende die Balken neu erstellen.
Dachte mir also: Hmm, einfach die Funktionen hooken...
Viel bessere Idee: Einfach über die ZS_Talk regeln :C:
Edit: Oh.Mein.Gott... Manchmal fühle ich mich echt dumm. Frag mich nicht, warum ich gestern Abend die Bedingungen und das verschieben nicht wirklich kapiert habe, jetzt hat es endlich Klick gemacht...
Gerade ist ein Problem mit den Views, bezogen auf View_Open aufgefallen:
Scheinbar möchte ein View nicht mehrmals geöffnet werden. Ich schmeiß' mal ein wenig Beispielcode rein, um das Ganze zu zeigen.
Folgendes funktioniert:
func void manaBarLoop() {
var int manaBar;
var int manaBarStatus;
if (!Hlp_IsValidHandle(manaBar)) {
manaBar = Bar_Create(mpBar);
};
Bar_SetMax(manaBar, hero.attribute[ATR_MANA_MAX]);
Bar_SetValue(manaBar, hero.attribute[ATR_MANA]);
if (MEM_Game.pause_screen || !InfoManager_HasFinished()) {
Bar_Hide(manaBar);
manaBarStatus = FALSE;
};
if (manaBarStatus == FALSE) {
Bar_Show(manaBar);
manaBarStatus = TRUE;
};
};
Dadurch wird der View nur einmal geöffnet. (Bar_Show ruft ja auch nur 2 View_Open auf). Das hier hingegen funktioniert nicht:
func void manaBarLoop() {
var int manaBar;
var int manaBarStatus;
if (!Hlp_IsValidHandle(manaBar)) {
manaBar = Bar_Create(mpBar);
};
Bar_SetMax(manaBar, hero.attribute[ATR_MANA_MAX]);
Bar_SetValue(manaBar, hero.attribute[ATR_MANA]);
if (MEM_Game.pause_screen || !InfoManager_HasFinished()) {
Bar_Hide(manaBar);
manaBarStatus = FALSE;
} else {
Bar_Show(manaBar);
};
};
Dadurch würde der View jeden Frame neu geöffnet. Dadurch wird der View (die Bar) gar nicht mehr angezeigt, bzw. in diesem Beispiel für den Bruchteil einer Sekunde, nachdem ein Menü geschlossen wurde.
mud-freak hatte eine Idee, ich habe sie in verschiedenen Ausführungen versucht.
Folgender Fix bringt Linderung, allerdings wird der View beim erstellen gar nicht angezeigt, erst beim zweiten Mal:
func void ViewPtr_Open(var int ptr) {
var zCView v; v = _^(ptr);
if (v.isOpen) { return; };
var int textlinesBak; textlinesBak = v.textLines_next;
v.textLines_next = 0;
// zCView::Open destroys all textlines (why??)
CALL__thiscall(ptr, zCView__Open);
v.textLines_next = textlinesBak;
};
func void View_Open(var int hndl) {
ViewPtr_Open(getPtr(hndl));
};
Auch invertiert oder mit v.isClosed wird es höchstens nur noch schlimmer.
Vielleicht hat ja jemand eine Idee, woran es liegt :)
Edit: mud-freak sagte noch folgendes:
Vielleicht reicht es auch schon zu sagen, die Bars lassen sich nicht mehr zeigen. Dass alle zCViews betroffen sind (und meine "Fixes") waren alles nur Spekulationen ohne Boden, vielleicht liegt das Problem ganz wo anders.
Ich hab schon öfter gehört, dass irgendwas mit den Bars nicht stimmt, aber habe es bisher nie reproduzieren können (zufälligerweise habe ich aber auch nie Code bekommen, der das Problem auslöst...).
Da ich jetzt etwas handfestes habe, schau ich mir das mal an. Bin die nächsten zwei Wochen aber noch in einem strammen Prüfungsregime und finde wohl erst danach Zeit dafür. Aber es wird nicht vergessen ;)
Dann wünsche ich viel Erfolg ^2^
Gibt's eigentlich schon News zur G1-Version von LeGo? §wink
mud-freak
03.04.2017, 20:08
Mark56 hat einiges zu Gothic 1 portiert (siehe dev branch im SVN (https://app.assembla.com/spaces/lego2/subversion/source/HEAD/dev)). Ich hatte ihn vor einigen Wochen mal zu der Stabilität gefragt (allerdings nur in Hinsicht auf FrameFunctions/HookEngine):
Lego on G1 works pretty fine. Only differences that I currently know of are trialogs.d, names.d nad some Lego init stuff. But generally, its pretty much all the same. Hovewer one can notice strange AI beahvior at moments. But it is hard to say if caused by frame functions. HookEngine works flawlesly I would say.
I use LeGo quite extensively...
Mark56 hat einiges zu Gothic 1 portiert (siehe dev branch im SVN (https://app.assembla.com/spaces/lego2/subversion/source/HEAD/dev)). Ich hatte ihn vor einigen Wochen mal zu der Stabilität gefragt (allerdings nur in Hinsicht auf FrameFunctions/HookEngine):
Hui, das klingt ja richtig gut :) Dann steht ja Spine-Features für G1 bald nichts mehr im Weg :)
Ich habe in letzter Zeit leider nicht sehr viel Aufmerksamkeit für LeGo (insbesondere den G1-branch) übrig gehabt.
Allerdings fehlen im Großen und Ganzen, wie Mark56 gesagt hat, nur noch Details. Vielleicht kann ich ja nachher mal alle Changes mergen und eine Version zusammenstellen.
mud-freak
04.04.2017, 08:21
Ich habe in letzter Zeit leider nicht sehr viel Aufmerksamkeit für LeGo (insbesondere den G1-branch) übrig gehabt.
Allerdings fehlen im Großen und Ganzen, wie Mark56 gesagt hat, nur noch Details. Vielleicht kann ich ja nachher mal alle Changes mergen und eine Version zusammenstellen.
Beim Mergen solltest du aufpassen, denn Mark hat AIV_TALENT von Userconst.d nach EngineAdr.d, bzw. EngineAdr_G1.d "ausgelagert", was den Sinn der Userkonstanten etwas nichtig macht (Revision 124 (https://app.assembla.com/spaces/lego2/subversion/commits/124)). Vielleicht wäre es angebracht stattdessen auch Userconst.d zwischen Gothic 1 und Gothic 2 zu splitten.
Geschmackssache wäre natürlich noch EngineAdr.d in EngineAdr_G2.d umzubenennen (Ikarus-Style).
Aber das sind Sachen, die dir beim Mergen selbst auffallen werden.
Beim Mergen solltest du aufpassen, denn Mark hat AIV_TALENT von Userconst.d nach EngineAdr.d, bzw. EngineAdr_G1.d "ausgelagert", was den Sinn der Userkonstanten etwas nichtig macht (Revision 124 (https://app.assembla.com/spaces/lego2/subversion/commits/124)). Vielleicht wäre es angebracht stattdessen auch Userconst.d zwischen Gothic 1 und Gothic 2 zu splitten.
Geschmackssache wäre natürlich noch EngineAdr.d in EngineAdr_G2.d umzubenennen (Ikarus-Style).
Aber das sind Sachen, die dir beim Mergen selbst auffallen werden.
Ja, bei der AIVar hatten wir uns irgendetwas kluges überlegt, ich weiß nur nicht mehr, was genau das war :p
Zum Glück gibt es ja Chatlogs...
GiftGrün
07.05.2017, 03:45
Nur interessehalber: Wie sicher ist eigentlich die Schleife, die man mit MEM_StackPos.position erhält?
Ein Stack in der Engine ist einfach eine Liste von Pointern, oder? Und loop = MEM_StackPos.position; [...] MEM_StackPos.position = loop; funktioniert, weil die Engine die abgearbeiteten Pointer nicht sofort aus dem Speicher löscht, daher kann der Stackpointer auf den gespeicherten Wert zurückgestellt werden und die Pointer dazwischen nochmal abarbeiten.
Im Falle, dass ich das richtig verstanden habe: Wie lange kann ein Loop sein, bevor der Speicher im Stack freigegeben/überschrieben wird? Würde ein Loop, der in einer Funktion startet und nicht in derselben Funktion aufhört, korrekt funktionieren? (Z.B. in loop = MEM_StackPos.position; return; }; MEM_StackPos.position = loop;)
Ich verstehe ehrlich gesagt nicht ganz, was du meinst, aber du liegst vermutlich falsch :p
Loops über eine Funktion hinweg können schief gehen, wenn man nicht genau weiß, was man macht. Das liegt daran, dass bei einem Call quasi ein neuer Kontext erstellt wird, der beim Return dann verworfen wird (und der vorherige wiederhergestellt).
Das ganze funktioniert, weil MEM_StackPos.position genau der Speicherbereich ist, der auf die nächste Instruktion (also den nächsten Befehl) zeigt. Das hat nichts mit irgendwelchen Pointern zu tun, die nicht oder zu spät gelöscht werden.
GiftGrün
07.05.2017, 17:06
Also Gothic hat ja seinen Anwendungs-Stack (so wie Windows seinen eigenen Stack hat), was im Prinzip eine Liste von Dingen ist, die die CPU in dem Moment noch zu machen hat, richtig? Und wenn man MEM_StackPos.position speichert, dann ist in der Variable Loop ein Pointer gespeichert, der auf das nächste Element im Stack zeigt, oder?
Kann man diesen Loop so lange machen, wie man will, oder wird die Instruktion an der Adresse Loop irgendwann ungültig?
EDIT: Wenn ich dich damit nerve, brauchst du natürlich nicht Lehrerin spielen.
Also Gothic hat ja seinen Anwendungs-Stack (so wie Windows seinen eigenen Stack hat), was im Prinzip eine Liste von Dingen ist, die die CPU in dem Moment noch zu machen hat, richtig? Und wenn man MEM_StackPos.position speichert, dann ist in der Variable Loop ein Pointer gespeichert, der auf das nächste Element im Stack zeigt, oder?
Kann man diesen Loop so lange machen, wie man will, oder wird die Instruktion an der Adresse Loop irgendwann ungültig?
EDIT: Wenn ich dich damit nerve, brauchst du natürlich nicht Lehrerin spielen.
Keine Angst, du nervst nicht, ich bin froh, wenn jemand mal was interessantes fragt :p
Der Codestack ist leider kein Stack, sondern eher ein Array, also alles steht hintereinander. Die einzige Art von Stack die es gibt ist implizit in der Vernestung von zCParser::DoStack() eingebaut. Das ist der Loop, der den Code ausführt. Bei Calls wird dann einfach DoStack(call-address); ausgeführt.
Die Funktion benutzt den Wert an der Stelle MEM_StackPos.position um sich jeweils die nächste Instruktion zu holen. Wenn man ihn ändert, ändert man die als nächste ausgeführte Instruktion in der DoStack().
GiftGrün
07.05.2017, 20:43
Als Nachhilfelehrer kenn ich das Gefühl... :D
Würde dann der Code:
MEM_StackPos.position += 4; // oder halt die Länge der Instruktion "foo1();"
foo1();
foo2();
nur foo2(); ausführen? So wie ich das verstehe, wären die letzten zwei Zeilen je ein goto-Befehl mit einem Pointer auf foo1 bzw. foo2 (wo jeweils die Instruktionen von foo1 und foo2 stehen) als Argument, und sobald in der Funktion foo1 "return;" ausgeführt wird, wird die Auswertung an der Stelle weitergeführt, wo sie vorher abgebrochen wurde (deshalb der Name return).
Mit dem passenden Offset würde das funktionieren, ja. CALL-Instruktionen sind 5 Byte (Call-Token + 4 Byte Zieladresse oder Symbolindex).
Mit 5 dürfte es dann schon funktionieren. Was aber zum Beispiel falsch wäre:
MEM_StackPos.position = MEM_StackPos.position + 5;
In diesem Fall würdest du nicht weit genug springen, weil (MEM_StackPos.position+5) ausgewertet wird, bevor das Assignment-Token kommt. Richtig wäre also (vermutlich) +6.
Weiter kommt man auch zu diesem, oberflächlich richtig aussehenden, aber eigentlich falschem Code:
var int loop; loop = MEM_StackPos.position + 0;
...
MEM_StackPos.position = loop;
Normalerweise muss man sowas aber nicht tun, also lässt man lieber die Finger davon :p
GiftGrün
08.05.2017, 02:09
Ich erinnere mich, in der Ikarus-Dokumentation gelesen zu haben, dass loop = MEM_StackPos.position + 0; anscheinend crasht; als mahnendes Beispiel dafür, dass man nicht mit der Loop-Konstruktion rumspielen soll, wenn man keine Ahnung hat. Hab mich schon gewundert, warum, weil loop und MEM_StackPos.position ja einfache Integer sind...
Also der Fehler bei loop = MEM_StackPos.position + 0; ist, dass es folgendes macht (in Pseudocode):
00000000 Add(MEM_StackPos.position,0); (wird irgendwo als 'tempvar' gespeichert)
00000009 Assign(loop,tempvar)
und zu dem Zeitpunkt, in dem die Addition ausgeführt wird, ist MEM_StackPos.position=00000009, weshalb
var int loop; loop = MEM_StackPos.position + 0;
...
MEM_StackPos.position = loop;
einfach unendlich oft Assign(loop,tempvar) ausführt und dann wieder in Zeile 00000009 springt, weil ja MEM_StackPos.position auf diese Zeile zeigt?
Daedalus benutzt eine stack-basierte VM, der Befehl Assign (zPAR_TOK_IS) holt sich also zwei Werte vom Stack, einmal eine Adresse und einmal einen Wert (der aber auch eine Adresse sein könnte). Das erste Assign geht noch gut, dann springt es (wie du richtig beobachtet hast) wieder zum Assign. Jetzt werden frühere Werte vom Stack gepoppt und entweder ist der erste Wert keine Adresse (-> Crash) oder die Ausführung geht irgendwo im nirgendwo weiter (höchst wahrscheinlich außerhalb des eigentlichen Codes), also ebenfalls Crash.
GiftGrün
08.05.2017, 18:04
Vielen Dank, Lehona, so langsam begreife ich, wie ein Programm "unter der Haube" aussieht.
Lass dich von Daedalus nicht ablenken, stackbasierte Architekturen sind heutzutage ziemlich ausgestorben (ausgenommen in VMs, vielleicht, aber auch da sind sie selten). Ein "richtiges" Programm, das direkt auf deiner CPU läuft, benutzt zwar auch einen Stack, aber die Instruktionen sind nicht darauf angewiesen (bzw. man nutzt den Stack nur als Datenspeicher, Instruktionen wie add greifen nicht darauf zu).
So etwas wie MEM_StackPos.position gibt es aber tatsächlich, dass nennt sich dann EIP (32bit) oder RIP (64bit).
GiftGrün
08.05.2017, 19:50
Dass sich die Technologie weiterentwickelt hat, ist mir schon bewusst. Genauso sieht man ja auch an einigen Stellen in den Skripten Programmier-Techniken, die heutzutage verpönt sind (z.B. die Lerndialoge, deren Effekte im Prinzip in der If-Abfrage stehen).
Ich war gerade auf YouTube.
https://upload.worldofplayers.de/files10/daedalus.png
Nun, diese Namen wurden ja auch nicht ganz zufällig ausgewählt :p
Bei Ikarus war mir das bewusst, aber dass auch noch Lego dabei ist. :D
GiftGrün
18.05.2017, 14:25
Ist es eigentlich möglich, eine neu erstellte Klasse/Instanz wieder aus einem Savegame rauszukriegen? Bei Funktionen und Variablen hat Gothic ja kein Problem, wenn sie z.B. in Modversion 1.0 vorkommen und im 1.0-Speicherstand gespeichert sind, aber nicht mehr in der 1.1-Mod vorhanden sind, mit der man den alten Speicherstand lädt. Wenn ich aber eine Klasse erstelle (die nur ints enthält) dann stürzt die Version 1.1 beim Laden des alten Speicherstandes ab.
Konkretes Beispiel:
CLASS AttackObj
{
var int att; // Pointer
var int vic; // ditto
var int mag; // Spell-ID
var int dmg; // Schaden
var int chargeup; // Aufladungs-Level
var int damtype; // Schadenstyp
var int time; // Zeitpunkt des Castens
};
INSTANCE AttackObj@ (AttackObj) {};
Um sicherzustellen, dass kein gespeichertes Objekt mehr diese Instanz nutzt, habe ich folgendes Skript einmal laufen lassen und dann abgespeichert:
func int _DelAttObj (var int hndl) {
var AttackObj myAttackObj; myAttackObj = get(hndl);
delete(myAttackObj);
return continue;
};
func void DelAttObjs () {
ForEachHndl(AttackObj@,_DelAttObj);
};
Ist es nicht möglich, das Savegame in einen Zustand zu bringen, sodass es ohne den ersten Codeblock in der Mod lädt? Oder hab ich das Löschen vergeigt?
Mit und ohne die zwei Codeblöcke funktioniert das Spiel ansonsten einwandfrei, es ist einfach nur Code-Ballast den ich in meinem in 1.0 gestarteten Spiel behalten muss und dann entfernen muss, bevor ich 1.1 release.
EDIT: Das Problem scheint die Instanz zu sein, nicht die Klasse. Wenn ich die unterste Zeile im ersten Codeblock auskommentiere, kommt es auch zum Crash zu Startup.
EDIT2: Mir ist gerade erst aufgefallen, dass das Grundproblem gar nichts mit LeGo zu tun hat. Soll ich einen anderen Thread aufmachen?
Wenn das Problem nicht an LeGo direkt liegt (du kannst in der SCRPTSAVE.SAV einfach alle Einträge der genannten Klasse/Instanz von Hand löschen zum Testen), kann ich dir wohl nicht weiterhelfen. Generell ist es problematisch, alte Spielstände weiterhin zu benutzen, mehr weiß ich leider auch nicht.
Mir ist nicht ganz klar, warum die Instanz eine Funktion zugewiesen hat und keine Instanzenreferenz ist (wie Self, Other, ... ohne geschweifte Klammern).
Mir ist nicht ganz klar, warum die Instanz eine Funktion zugewiesen hat und keine Instanzenreferenz ist (wie Self, Other, ... ohne geschweifte Klammern).
Weil entweder Gottfried oder ich mal geschrieben habe, dass beides okay ist.
Macht das denn einen Unterschied, außer dass der "Konstruktor" bei der einen Variante null ist und bei der anderen "nur" leer?
Macht nur intern einen kleinen Unterschied, der meist keine Rolle spielen dürfte. Ist mir nur aufgefallen...
Sollte man vielleicht überprüfen ob get etwas gültiges zurückgibt, bevor man delete aufruft?
Hab hier kein Gothic zur Hand, also nur ins Blaue geraten :-)
Sollte man vielleicht überprüfen ob get etwas gültiges zurückgibt, bevor man delete aufruft?
Sollte man, aber delete() kommt auch mit ungültigen Werten zurecht.
Du hast mich aber auf eine Idee gebracht: Man sollte definitv die Dokumentation (http://lego.worldofplayers.de/?PermMem#delete) lesen :p
Als Parameter wird natürlich ein Handle erwartet, kein Objekt. Daher lässt sich der oben gezeigte Code auch viel einfacher schreiben:
ForEachHndl(AttackObj@, delete);
GiftGrün
19.05.2017, 01:42
Danke, damit hat's geklappt!
Frank-95
01.06.2017, 10:18
I might have found a bug (not like the one in my previous thread :p)
Do you remember when I told you that FFs did not automatically run on loading? Well you fixed that with one of the previous realeses. Nevertheless it seems that the fix does not apply with FFs with data.
FF_ApplyOnceExt(PoisonDamage, 1000, -1); //automatically starts
FF_ApplyOnceExtData(DMG_HeavyInjury, 1000, -1, MEM_InstToPtr(hero)); //does not, have to call it explicitly in the init
Frank-95
03.06.2017, 21:42
Also, I found a problem with Wld_SetTime used with FFs. Trivial example:
instance TestPC3(C_Item)
{
name = "time";
mainflag = ITEM_KAT_DOCS;
flags = ITEM_MISSION;
value = 0;
visual = "ItWr_Scroll_01.3DS";
material = MAT_LEATHER;
on_state[0] = UseTestPC3;
scemeName = "MAP";
description = name;
};
var int a;
func void aaa()
{
Wld_SetTime(a+9,10+a);
a += 1;
};
func void UseTestPC3()
{
FF_ApplyOnceExt(aaa,3000,5);
};
It just crashes, zspy logs nothing, but I get this window:
https://thumb.ibb.co/k8k5yF/Immagine.png (https://ibb.co/k8k5yF)
The problem seems not to be in the time change itself, but rather in the routines changes. In fact if I did the same thing this way:
func void aaa()
{
MEM_WorldTimer.worldTime = addf(MEM_WorldTimer.worldTime,mkf(250000));
};
func void UseTestPC3()
{
FF_ApplyOnceExt(aaa,3000,5);
};
Hours increase by 1 each time, but I have no change in npcs routine.
Interesting find. I'll try to find out what' s causing the crash.
Frank-95
04.06.2017, 11:49
If you need more info, this morning I made some reverse engineering to check where the problem could be.
Daedalus Wld_SetTime changes npcs routines and, if needed, spawn npcs or monsters too. No functions of oCWorldTimer do this, so I supposed that the function that Wld_SeTime calls is:
.text:006C4DE0 ; void __thiscall oCGame::SetTime(oCGame *this, int, int, int)
This function, in addition to change the time, calls:
.text:007764D0 ; void __thiscall oCRtnManager::SetDailyRoutinePos(oCRtnManager *this, int)
.text:006CACB0 ; void __thiscall oCGame::SetObjectRoutineTimeChange(oCGame *this, int, int, int, int)
.text:00777A40 ; void __thiscall oCSpawnManager::SpawnImmediately(oCSpawnManager *this, int)
So I've reimplement that function manually:
func void RtnManager_SetDailyRoutinePos(var int state)
{
const int RtnManager_ptr = 11219400;
CALL_IntParam(state);
CALL__thiscall(RtnManager_ptr,7824592);
};
func void Game_SetObjectRoutineTimeChange(var int hour1, var int min1, var int hour2, var int min2)
{
CALL_IntParam(min2);
CALL_IntParam(hour2);
CALL_IntParam(min1);
CALL_IntParam(hour1);
CALL__thiscall(MEM_ReadInt(MEMINT_oGame_Pointer_Address),7122096);
};
func void SpawnManager_SpawnImmediately(var int state)
{
CALL_IntParam(state);
CALL__thiscall(MEM_Game.spawnman,7830080);
};
func void Game_ManualSetTime(var int day, var int hour, var int minute)
{
var int currentHour; currentHour = Wld_GetHour();
var int currentMinute; currentMinute = Wld_GetMinute();
MEM_WorldTimer.day = day;
MEM_WorldTimer.worldTime = addf(MEM_WorldTimer.worldTime,mulf(mkf(oCWorldTimer_TicksPerHour),mkf(hour)));
MEM_WorldTimer.worldTime = addf(MEM_WorldTimer.worldTime,mulf(mkf(oCWorldTimer_TicksPerMin_approx),mkf(minu te)));
RtnManager_SetDailyRoutinePos(1);
Game_SetObjectRoutineTimeChange(currentHour,currentMinute,Wld_GetHour(),Wld_GetM inute());
SpawnManager_SpawnImmediately(1);
};
(okay, I've mistakened since my function just adds time and not sets it, but the point is not in there, which is the easiest part)
My function works, except for what I've just said. Anyway it crashes the same way when I call it via frame function:
func void aaa()
{
Game_ManualSetTime(1,12,32);
};
func void UseTestPC3()
{
FF_ApplyOnceExt(aaa,3000,1);
};
Exactly like the original function
func void Game_SetTime(var int day, var int hour, var int minute)
{
CALL_IntParam(minute);
CALL_IntParam(hour);
CALL_IntParam(day);
CALL__thiscall(MEM_ReadInt(MEMINT_oGame_Pointer_Address),7097824);
};
Then I commented the last three function and checked which made the game crashes. It resulted to be RtnManager_SetDailyRoutinePos.
Frank-95
13.06.2017, 13:31
Okay I've found out that the problem is not just in that function. If I call SetDailyRoutinePos via FF, it works. If I call SetObjectRoutinePos and/or SpawnImmediately, it works. If I call the three together, it doesn't.
A hack to make oCGame_SetTime work via FF, is to do something like this:
var int currentHour; currentHour = Wld_GetHour();
var int currentMinute; currentMinute = Wld_GetMinute();
FF_ApplyOnceExt(func1, time, 1);
func void func()
{
MEM_WorldTimer.worldTime = ...; //change or add time as needed
RtnManager_SetDailyRoutinePos(1);
FF_ApplyOnceExt(func2,1000,1);
};
func void func2()
{
Game_SetObjectRoutineTimeChange(currentHour,currentMinute,Wld_GetHour(),Wld_GetM inute());
SpawnManager_SpawnImmediately(1);
};
It's not clean, but it's the best I've come up so far
mud-freak
14.06.2017, 10:58
Gibt es eine einfache Möglichkeit zu testen, ob eine Adresse gültig ist?
Der Hintergrund ist eine Data-FrameFunction, dessen Data ein Pointer ist, der nach Spielladen sicherlich ungültig ist.
func void exampleFF(var int ptr) {
if (valid(ptr)) {
MEM_ReadInt(ptr+someoffset); // This would crash after loading a saved game
};
};
Das von PermMem bereitgestellte Handle-System ist ein guter Ansatz. Allerdings zeigt der Pointer auf eine nicht-dokumentierte Klasse, daher komme ich mit wrap (http://lego.worldofplayers.de/?PermMem#wrap) so nicht weiter. Gibt es da eine einfache Alternative?
Der Pointer muss das Laden eines Spiels nicht überleben, ich will nur einen Crash abfangen.
EDIT: Die Problemstellung ist ein bisschen falsch hier (der Beispiel-Code führt nicht zwingend zum Crash), da die Adresse an sich immer gültig ist. Was ich einfach überprüft habe ist, ob der Wert an bestimmtem Offset vom Pointer den Erwartungen enspricht.
Dein Beispiel ist etwas sehr minimal, aber deine Frage geht auf jedenfall in die Richtung "geht nicht".
Ohne deinen Fall besser zu kennen würde ich es so machen:
var int currGen;
class PtrGeneration {
var int ptr;
var int generation;
};
instance PtrGeneration@(PtrGeneration);
func int createPtr(var int ptr) {
var int h; h = new(PtrGeneration@);
var PtrGeneration PtrG; PtrG = get(h);
PtrG.ptr = ptr;
PtrG.generation = currGen;
};
func int validPtr(var int h) {
var PtrGeneration PtrG; PtrG = get(h);
return (PtrG.generation == currGen);
};
Dann musst du currGen nur noch an geeigneter Stelle inkrementieren - beim Spielstand laden oder beim Neustart von Gothic.
mud-freak
09.08.2017, 12:48
Gibt es ein definiertes Verhalten, wenn man zwei verschiedene Deadalusfunktionen an die gleiche Adresse hooked?
Beispiel:
HookEngineF(addr1, 5, func1);
HookEngineF(addr1, 5, func2);
Ich hätte erwartet, dass beim erreichen der Adresse zuerst func1 und dann func2 aufgerufen wird, aber ich bin mir nicht so sicher. Kann man überhaupt für eine Reihenfolge garantieren, oder ist so etwas so wie etwas gefährlich?
EDIT: Hat sich erledigt. Hat eine Weile gedauert bis ich nachvollziehen konnte, dass der ursprüngliche Opcode an der Adresse ans Ende kopiert wird und dann durch den rekursiven Aufrufen (wenn man erneut an die Adresse hooken will) erneut überschrieben/verschoben wird. Somit werden also die Hooks natürlich der Reihenfolge entsprechend der Initialisierung nach aufgerufen.
Milky-Way
09.08.2017, 18:25
Gibt es ein definiertes Verhalten, wenn man zwei verschiedene Deadalusfunktionen an die gleiche Adresse hooked?
Beispiel:
HookEngineF(addr1, 5, func1);
HookEngineF(addr1, 5, func2);
Ich hätte erwartet, dass beim erreichen der Adresse zuerst func1 und dann func2 aufgerufen wird, aber ich bin mir nicht so sicher. Kann man überhaupt für eine Reihenfolge garantieren, oder ist so etwas so wie etwas gefährlich?
EDIT: Hat sich erledigt. Hat eine Weile gedauert bis ich nachvollziehen konnte, dass der ursprüngliche Opcode an der Adresse ans Ende kopiert wird und dann durch den rekursiven Aufrufen (wenn man erneut an die Adresse hooken will) erneut überschrieben/verschoben wird. Somit werden also die Hooks natürlich der Reihenfolge entsprechend der Initialisierung nach aufgerufen.
Ich hätte erwartet, dass zuerst func2, dann func1 und dann der ursprüngliche Code ausgeführt wird? (oder ist es das, was du meinst)
Es könnte aber vielleicht zu Problemen führen, wenn die 5 Bytes nicht passen für den ersten Befehl von func1?
mud-freak
09.08.2017, 18:41
So wie ich es aus dem Code entnehmen konnte, wird die ursprüngliche Instruktion durch einen Jump zu einer neuen Adresse ersetzt. An dieser neuen Adresse wird (u.a.) die Deadalusfunktion aufgerufen und anschliessen wird die ursprüngliche Instruktion drangehängt, bevor es zurück "nach" die usprüngliche Adresse geht.
Wenn man nun erneut an der selben Adresse hookt, wird dem Jump gefolgt und dann wieder die ursprüngliche Instruktion, die einst an der ursprünglichen Adresse war, durch einen Jump ersetzt und anschliessend wieder angehängt. So geht das immer weiter und die Reihenfolge der Hooks bleibt erhalten.
Ich bitte um Korrektur wenn ich das falsch verstanden habe.
GiftGrün
10.08.2017, 18:39
Ist es nicht so, dass der Jump ersetzt wird? Wie ich das verstehe:
Sagen wir, addr1 wäre 00F43960 und die Funktion an dieser Stelle foo (10 bytes). Dann sähe das nach dem ersten Hook doch so aus:
00F43960 - JUMP TO
00F43961 - 13
00F43962 - 5A
00F43963 - 92
00F43964 - 34
Und beginnend ab Adresse 135A9234 steht [was auch immer func1 macht] [erste fünf Bytes von foo] RETURN.
Der zweite Hook macht dann daraus:
00F43960 - JUMP TO
00F43961 - 24
00F43962 - AF
00F43963 - 40
00F43964 - 19
Und an Adresse 24AF4019 steht [was auch immer func2 macht] JUMP TO 135A9234 RETURN. Was dann dazu führt, dass func2 vor func1 aufgerufen wird.
mud-freak
10.08.2017, 18:50
Wenn ein Jump an addr1 schon existiert, wird die HookEngine-Funktion rekursiv aufgerufen mit der Adresse zu der der Jump zeigt, plus ein paar zerquetschte (https://app.assembla.com/spaces/lego2/subversion/source/HEAD/stable/HookEngine.d#ln54), so dass man wieder auf der Originalinstruktion landet. Das heisst, func2 wird genau nach dem Aufruf (und Aufräumen) von func1 gehookt.
Milky-Way
10.08.2017, 19:23
Wenn ein Jump an addr1 schon existiert, wird die HookEngine-Funktion rekursiv aufgerufen mit der Adresse zu der der Jump zeigt, plus ein paar zerquetschte (https://app.assembla.com/spaces/lego2/subversion/source/HEAD/stable/HookEngine.d#ln54), so dass man wieder auf der Originalinstruktion landet. Das heisst, func2 wird genau nach dem Aufruf (und Aufräumen) von func1 gehookt.
Ah gut, dann gibt es an dieser Stelle also tatsächlich (nützlicherweise) ein definiertes Verhalten.
Milky-Way
14.08.2017, 04:11
Bei den Trialogen scheint es mir so, als würden Helme (wear = WEAR_Head;) nicht richtig gehandhabt, im Sinne von, wenn einer der Npc einen Helm aufhat, dann springt der Helm immer hin und her, wenn der sprechende Npc wechselt. Kann das jemand bestätigen und beheben? :)
mud-freak
14.08.2017, 06:40
Bei den Trialogen scheint es mir so, als würden Helme (wear = WEAR_Head;) nicht richtig gehandhabt, im Sinne von, wenn einer der Npc einen Helm aufhat, dann springt der Helm immer hin und her, wenn der sprechende Npc wechselt. Kann das jemand bestätigen und beheben? :)
Auch ohne das zu testen, kann ich dir das bestätigen. Die Visuals (u.A.) der NPCs werden im Trialog fortlaufend ausgetauscht. In den Skripten werden dabei Rüstungen, Waffen usw. berücksichtigt, nur Helme bisher nicht. Kann man den derzeitig getragenen Helm eines NPCs abfragen (so etwas wie Npc_GetEquippedArmor nur für Helme)?
EDIT: Hier eine etwas sinnvollere Antwort: Du kannst diese Abfrage so ans Ende von _TRIA_Copy anfügen:
func void _TRIA_Copy(var int n0, var int n1) {
// ...
var int mw0; mw0 = Npc_GetMeleeWeapon(np0);
var int rw0; rw0 = Npc_GetRangedWeapon(np0);
var int mw1; mw1 = Npc_GetMeleeWeapon(np1);
var int rw1; rw1 = Npc_GetRangedWeapon(np1);
Npc_TradeItem(np0, mw0, mw1);
Npc_TradeItem(np0, rw0, rw1);
Npc_TradeItem(np1, mw1, mw0);
Npc_TradeItem(np1, rw1, rw0);
// Helm
var int h0; h0 = Funktion zum Helm holen(np0);
var int h1; h1 = Funktion zum Helm holen(np1);
Npc_TradeItem(np0, h0, h1);
Npc_TradeItem(np1, h1, h0);
};
Wenn du das findest, sollte es am besten ins nächste Release von LeGo aufgenommen werden.
mud-freak
23.08.2017, 17:22
I found a bug in LeGo, which I will try to fix. Before I do, I just want to raise awareness here (hence writing in English).
The bug only affects the LeGo package LeGo_Gamestate (in Gothic 2). The variable Gamestate as well as any Gamestate Listener does not reliably inform about level changes.
A level change is only recognized the first time for each level of a play-through.
Some internal info:
The INIT_Global is called every time a game is loaded, a new game is started or a level change is performed. For some reason it is called twice during a level change. LeGo compensates for that. However, it turns out, that the INIT_Global is called twice only the very first time of entering a new level. Afterwards, it will always be only called once.
Here is a table of how often the INIT_Global is called for different conditions. (Note: It is even more different for Gothic 1, actually causing crashes when starting a new game from a running session.)
Gothic 1
New session
Running session
Gothic 2
New session
Running session
New game
twice
twice
New game
once
once
Lvl change
-
once or twice
Lvl change
-
once or twice
Load game
once
once
Load game
once
once
mud-freak
23.08.2017, 19:34
I found a bug in LeGo, which I will try to fix.
Bug fix (Revision 132 (https://app.assembla.com/spaces/lego2/subversion/commits/132) on dev-Branch):
--- old/Content/LeGo/LeGo.d
+++ new/Content/LeGo/LeGo.d
@@ -92,12 +92,12 @@
if (f & LeGo_Saves) {
if(_LeGo_IsLevelChange()) {
- // During level change, LeGo_InitAlways is called twice!
+ // During level change, LeGo_InitAlways is called twice on very first transistion!
_LeGo_LevelChangeCounter += 1;
- // update gamestate status after the last call of _LeGo_IsLevelChange
- // for avoiding duplicate user function calls (e.g. in startup)
- if(_LeGo_Flags & LeGo_Gamestate && (_LeGo_LevelChangeCounter == 2)) {
+ // update gamestate status at the first call of _LeGo_IsLevelChange
+ // because it is only called once for consecutive level changes
+ if(_LeGo_Flags & LeGo_Gamestate && (_LeGo_LevelChangeCounter == 1)) {
_Gamestate_Init(Gamestate_WorldChange);
};
};
This only fixes the bug for Gothic 2!
EDIT: Der Fix für Gothic 1 ist nun auch im SVN.
Thanks for this. It was problem for quite time. :gratz
Also in Gothic 1. Please try to look at this bug.
1. start game
2. select "load game" (but do NOT load )
3. go back to main menu.
4. select and start new game.
In this case. Lego thinks I am loading "save 0" and crashes because it attempts to load wrong permmem or whatever §cry
It has to do with the current system of detecting what savegame is being loaded...
mud-freak
24.08.2017, 07:34
Also in Gothic 1. Please try to look at this bug.
1. start game
2. select "load game" (but do NOT load )
3. go back to main menu.
4. select and start new game.
In this case. Lego thinks I am loading "save 0" and crashes because it attempts to load wrong permmem or whatever §cry
It has to do with the current system of detecting what savegame is being loaded...
Thanks for reporting this. However, I could not reproduce this bug. It might already be fixed with Revision 134 (https://app.assembla.com/spaces/lego2/subversion/commits/134). Can you try if the bug still happens for you?
I want tried Gothic II QuickBar (https://forum.worldofplayers.de/forum/threads/1500134-Gothic-II-QuickBar), but if inicialize LeGo_Render in scripts, even without initializing QuickSlots, when I start a new game or load from old save in end of loading I always get a error message "Out of Memory" (my config: Win 10 LTSB 1607, Core i5 2500k, 8GB RAM DDR3, GeForce GTX 970 4Gb RAM) then next error windows that write LOG in Gothic2.rpt:
//======================UNHANDLED EXCEPTION======================
Gothic2.exe caused an EXCEPTION_BREAKPOINT in module SHW32.DLL at 0023:637E1A11, Ordinal99()+6673 byte(s)
EAX=00000001 EBX=FFFFA28C ECX=4A607EBB EDX=00000000 ESI=637E1990
EDI=00000001 EBP=0135F4AC ESP=0135F494 EIP=637E1A11 FLG=00000246
CS=0023 DS=002B SS=002B ES=002B FS=0053 GS=002B
//===================== INFOS =========================
Gothic II - 2.6 (fix), Parser Version: 50
User: Admin, CPUType: 586, Mem: 0 MB total, 0 MB free
//====================== CALLSTACK ========================
0023:637E1A11 (0xFFFFA28C 0x00000000 0x0135F4D8 0x637E1AC7) SHW32.DLL, Ordinal99()+6673 byte(s)
0023:637E7B33 (0xFFFFA28C 0x00000001 0x2E3AB898 0x00000000) SHW32.DLL, notImplemented_0000012B__shi_leaveCriticalSection()+23171 byte(s)
0023:637E1AC7 (0xFFFFA28C 0x00000001 0x00792568 0x00AB4108) SHW32.DLL, shi_calloc()+0023 byte(s)
I use last LeGo 2.40, tried also dev builds.
I tried to initialize with different options:
Lego_Init(LeGo_Render);
Lego_Init(LeGo_All | LeGo_Render)
Lego_Init(LeGo_All | LeGo_Render &~ LeGo_BloodSplats);
I tried to run mod with SystemPack 1.7 and without it, tried to run with D3D11-Renderer and without it, in all variants i get that error.
If I do not use LeGo_Render, for example with this set: Lego_Init(LeGo_Focusnames | LeGo_Cursor | Lego_Buttons); mod start normal.
What is it can be, what else to try?
How you parsing files? I parsing look like this, and work:
Game_InitGerman();
LeGo_Init(LeGo_All | LeGo_Render);
QS_Init();
How you parsing files? I parsing look like this, and work:
Game_InitGerman();
LeGo_Init(LeGo_All | LeGo_Render);
QS_Init();
I try it, but i get error with LeGo_Render also WITHOUT QS_Init();.
ADD:
Tried inicialize LeGo_Render on other clean Gothic2 scripts and get same eroor, perhaps LeGo_Render conflicts in my OS or installed soft, drivers...
Bezüglich https://forum.worldofplayers.de/forum/threads/1126551-Skriptpaket-LeGo-2/page12?p=21130811&viewfull=1#post21130811.
Ist es auch möglich das der Wert im Handelsfenster aktualisiert wird? Hier steht an dieser Stelle immer noch der "alte" Wert.
I have question about PermMem. How is called function:
FFItem_Archiver && FFItem_Unarchiver?
This function is called or no - never?
*_Unarchiver and *_Archiver are special suffixes and those functions are called when an object/handle of the respective class is (un-)archived. When no such function exists, PermMem interprets the values of the object as literal (i.e. ints as just numbers and strings as strings), so you don't need to write special (un-)archivers most of the time.
If i have class CBuff, when i made function CBuff_Archiver - i have archiver?
Yes. Look at the FrameFunction-archivers to see how they work. You usually don't need them, though.
mud-freak
08.09.2017, 13:38
Bezüglich https://forum.worldofplayers.de/forum/threads/1126551-Skriptpaket-LeGo-2/page12?p=21130811&viewfull=1#post21130811.
Ist es auch möglich das der Wert im Handelsfenster aktualisiert wird? Hier steht an dieser Stelle immer noch der "alte" Wert.
Etwas spät aber hier nun eine Lösung:
func void changeTradeValueDisplay() {
var oCItem itm; itm = _^(EBX); // Displayed item
var oCNpc npc; npc = _^(MEM_InformationMan.npc); // Trading NPC
// Overwrite the display of the item value
MEM_WriteInt(/*esp+144h-ach*/ ESP+152, 19); // E.g. new value: 19 gold
};
const int oCItemContainer__DrawItemInfo = 7369937; //0x7074D1
HookEngineF(oCItemContainer__DrawItemInfo, 7, changeTradeValueDisplay);
changeTradeValueDisplay wird nur aufgerufen, wenn man sich auf der rechten Seite (Spielerinventar) des Tradingmenus befindet und überschreibt nur den Anzeigewert. Den tatsächlichen Verkaufswert muss man nach wie vor überschreiben.
Seltsamerweise ist standardmässig die horizontale Position der Wert-Anzeige im Infofenster nicht immer an der selben Stelle (vgl. z.B. Bögen und Pfeile). Das ist schon immer so und hat glaube ich was mit der Itemkategorie zutun. Möglicherweise kann es daher zu Problemen kommen mit vielstelligen Werten (alles bis drei einschl. dreistellig passt aber immer).
Hallo zusammen, ist es möglich, das LeGo-Skriptpaket (Version 2.4.0) in einer Gothic 1 Mod zu verwenden? Als Abhängigkeit habe ich bereits Ikarus (Version 1.2) eingefügt, welches sauber geparst wird. Parse ich direkt nach den Ikarus-Dateien allerdings die Header.src von LeGo, bekomme ich folgenden Fehler in Lego/Names.d: "unknown identifier: ocnpc.name_1 (line 14)". Kann mir jemand weiterhelfen?
mud-freak
18.09.2017, 13:47
Hallo zusammen, ist es möglich, das LeGo-Skriptpaket (Version 2.4.0) in einer Gothic 1 Mod zu verwenden? Als Abhängigkeit habe ich bereits Ikarus (Version 1.2) eingefügt, welches sauber geparst wird. Parse ich direkt nach den Ikarus-Dateien allerdings die Header.src von LeGo, bekomme ich folgenden Fehler in Lego/Names.d: "unknown identifier: ocnpc.name_1 (line 14)". Kann mir jemand weiterhelfen?
Momentan gibt es noch keine offizielle Unterstützung für Gothic 1. Ich benutze aber problemlos die Dateien aus dem Development-Branch (https://app.assembla.com/spaces/lego2/subversion/source/HEAD/dev) vom LeGo SVN. Damit funktioniert bei mir soweit alles (was ich davon benutze) problemlos: Ich konnte, unter Benutzung von Hooks, FrameFunctions, ConsoleCommands und Views, Gothic 1 komplett durchspielen. "Nicht offiziell unterstützt" sage ich deshalb, weil noch nicht alle Pakete ausgiebig getestet wurden.
Wenn du die Dateien aus dem dev-Branch für Gothic 1 verwenden willst, musst du die Datei EngineAdr_G2.d aus deinem Verzeichnis löschen.
Momentan gibt es noch keine offizielle Unterstützung für Gothic 1. Ich benutze aber problemlos die Dateien aus dem Development-Branch (https://app.assembla.com/spaces/lego2/subversion/source/HEAD/dev) vom LeGo SVN. Damit funktioniert bei mir soweit alles (was ich davon benutze) problemlos: Ich konnte, unter Benutzung von Hooks, FrameFunctions, ConsoleCommands und Views, Gothic 1 komplett durchspielen. "Nicht offiziell unterstützt" sage ich deshalb, weil noch nicht alle Pakete ausgiebig getestet wurden.
Wenn du die Dateien aus dem dev-Branch für Gothic 1 verwenden willst, musst du die Datei EngineAdr_G2.d aus deinem Verzeichnis löschen.
Vielen Dank für den Tipp! Kompiliert erstmal fehlerfrei. :) Welche Funktion muss ich denn zur Initialisierung von LeGo aufrufen? Und wo sollte ich das im Optimalfall in G1 tun? Ein Init_Global() wie in der LeGo-Readme beschrieben kann ich in den G1-Skripten leider nicht finden. Ich benötige die Listen-Implementation von LeGo in G1.
Ich bin mir ziemlich sicher, dass die Listen nicht initialisiert werden müssen, d.h. das sollte sofort lauffähig sein.
Um LeGo zu initialisieren (falls du noch andere Sachen verwenden möchtest) gibt es in G1 leider keine so gut geeignete Stelle wie die INIT_Global(), man wird also wohl nicht drumherum kommen, den LeGo_Init-Eintrag in jede INIT_XXX()-Funktion zu schreiben (oder zumindest indirekt - man kann sich ja seine eigene INIT_Global() bauen, die dann aus jeder INIT_XXX() aufgerufen wird).
Edit: Ja, es gibt keine Initialisierung für die Listen.
Etwas spät aber hier nun eine Lösung:
func void changeTradeValueDisplay() {
var oCItem itm; itm = _^(EBX); // Displayed item
var oCNpc npc; npc = _^(MEM_InformationMan.npc); // Trading NPC
// Overwrite the display of the item value
MEM_WriteInt(/*esp+144h-ach*/ ESP+152, 19); // E.g. new value: 19 gold
};
const int oCItemContainer__DrawItemInfo = 7369937; //0x7074D1
HookEngineF(oCItemContainer__DrawItemInfo, 7, changeTradeValueDisplay);
changeTradeValueDisplay wird nur aufgerufen, wenn man sich auf der rechten Seite (Spielerinventar) des Tradingmenus befindet und überschreibt nur den Anzeigewert. Den tatsächlichen Verkaufswert muss man nach wie vor überschreiben.
Seltsamerweise ist standardmässig die horizontale Position der Wert-Anzeige im Infofenster nicht immer an der selben Stelle (vgl. z.B. Bögen und Pfeile). Das ist schon immer so und hat glaube ich was mit der Itemkategorie zutun. Möglicherweise kann es daher zu Problemen kommen mit vielstelligen Werten (alles bis drei einschl. dreistellig passt aber immer).
Vielen Dank, funktioniert super! :gratz
Fisk2033
17.10.2017, 08:58
Weiß nicht, ob ich es hier oder im ScriptBin fragen soll, aber irgendwie ists ja eine Funktion die komplett auf LeGo aufbaut. Ich hab gestern Screefade ausprobiert. Erstmal den Effekt finde ich super und er ist genau das, was ich in dieser Situation benötige. Problem ist nur der Ablauf. :D
Eigentlich sollte es so sein:
1. Held beginnt Dialog mit NPC
2. Gespräch zwischen NPC/Held ist erledigt
3. Screenfade beginnt und wirkt
4. Held und NPC werden weg teleportiert
5. Screenfade wird wieder ausgeblendet
Momentan wird der Screenfade, aber als aller erstes ausgeführt. Sprich der Dialog findet im Blackscreen statt. Wie krieg ich den wirklich erst ausgeführt, wenn die Dialoge alle durch sind? §wink
mud-freak
17.10.2017, 09:00
Momentan wird der Screenfade, aber als aller erstes ausgeführt. Sprich der Dialog findet im Blackscreen statt. Wie krieg ich den wirklich erst ausgeführt, wenn die Dialoge alle durch sind? §wink
Hast du es schon mit AI_Function (http://lego.worldofplayers.de/?AI_Function) probiert?
Fisk2033
17.10.2017, 10:22
Hast du es schon mit AI_Function (http://lego.worldofplayers.de/?AI_Function) probiert?
Ich Dummkopf. *Facepalm-Smiley*
Danke, wie konnte ich das nur vergessen >.<
Vielleicht könnten wir das in den LeGo-Thread verlegen; hier geht es ja um ein (womöglich) komplett unabhängiges Problem mit den Dialogen in Gothic 2.
In der Tat.
Ja, das ist mir auch in den Sinn gekommen, weil mein zusätzlicher Assembly-Code das ganze etwas lang und unübersichtlich gemacht hat. Dabei könnte man auch umbauen, wie mehrere Funktionen an der selben Adresse hooken. Bisher werden dabei ja die Register vor und nach jedem einzelnen Funktionsaufruf hin- und hergeschoben. Etwas ähnliches wie das Foreachhandle-Konstrukt wie in den Framefunctions wäre da vielleicht auch nicht schlecht.
LeGo bietet EventHandler, damit erreicht man alle Funktionen, die sich für eine bestimmte Adresse angemeldet haben :) Muss man nur eben Funktionen schreiben, die auch einen Ptr anstatt Handle akzeptieren.
mud-freak
18.10.2017, 11:34
Dem Gothic Datenstack kann man nicht trauen ;-) Nico hat ja schon erwähnt, wie schnell plötzlich zusätzliche Werte auf dem Stack landen können. Andersrum gibt es auch Funktionen, die sich darauf verlassen, dass der Stack leer ist (Von einem leeren Stack poppen -> 0).
Ich versuche gerade die Vorgehensweise zu verstehen. Wäre es dann sinnvoll,...
... die Register wie gehabt in Deaduls-Variablen zur Verfügung zu stellen
... globale Instanzen (yINSTANCE_HELP, self, other, ...?) und Register in Deadalus zu sichern
... den Stack in Deadalus zu sichern
... den Stack dann zu leeren (weil einige Externals einen leeren Stack erwarten)
... alle hookenden Deadalusfunktionen (mittels EventHandler) aufzurufen
... dann den Stack wieder zu leeren (weil einige Externals den Stack nicht ordentlich aufräumen)
... dann den Stack aus der Deadalus-Sicherung wiederherzustellen
... die Register und globale Instanzen wiederherzustellen
... die manipulierten Deadalus-Variablen wieder zurück in die Register schieben (CX, DI und AX)
Wenn man die ganzen Sichererungen in Deadalus dann noch in einem Stack-ähnlichem Datentyp anlegt, wäre sie auch vor rekursiven Hooks geschützt. Gibt es in Deadalus (bzw. Ikarus) schon Stack-ähnliche Datentypen?
Habe ich das soweit richtig zusammen gefasst?
EDIT: Vielleicht würden für die Sicherungen Arrays mit diesen bereits existierenden Funktionen genügen:
MEM_ArrayPush(array, value);
value = MEM_ArrayPop(array);
Teron Gorefiend
18.10.2017, 21:25
schön zu sehen dass es was neues für G1 gibt denn meine Pläne G1 als G2 Mod zu machen kann ich vergessen, die Weltenumwandlung und Spacer klappen einfach nicht, Was würde ich für eine G1 als G2 mod geben §cry
Habe zwar schonmal gefragt wegen Schadernsberechnung in G1 ändern (thread) (https://forum.worldofplayers.de/forum/threads/1149697-Script-Eigene-Schadensberechnung/page2), aber das war relativ lange her sodass ich nochmal nachfrage ob es inzwischen für G1 möglich ist.
mud-freak
23.10.2017, 13:25
Ich habe mich mal hingesetzt und die Hooks neu implementiert (https://app.assembla.com/spaces/lego2/subversion/commits/139). Hier eine kleine Übersicht wie sie nun funktionieren:
Nach wie vor wird aus der zu hookenden Engine-funktion herausgesprungen. An der neuen Adresse werden allerdings die Register nicht direkt in Deadalus-Variablen kopiert sondern einfach mit pushad der anschliessend aufgerufenen Verteilerfunktion als Funktionsparameter zur Verfügung gestellt.
Mit locals(); wird der Kontext in lokale Deadalus-Variablen gesichert: Die Registervariablen (für rekursive Hooks), die globalen Instanzen (falls diese überschrieben werden) - um rekursive Hooks zu ermöglichen.
Bevor die auf dem Stack liegenden Werte dann überschrieben und nach der Verteilerfunktion wieder in die Register geschoben werden (popad), werden die hookenden Deadalus-Funktionen von der Verteilerfunktion aufgerufen - nun aber mittels eines EventHandlers. Das hat vier Vorteile:
Nun wird zwischen jeder hookenden Deadalus-Funktion der Kontext wiederhergestellt, die Register werden aber nicht wie zuvor jedes mal hin und her kopiert, sondern erst nachdem alle hookenden Deadalus-Funktionen durchlaufen wurden.
Mit dem Pushen eines Puffers auf dem Datenstack (zehn mal Token_PushInt 0) vor jedem Funktionsaufruf soll verhindert werden dass unartige Funktionen etwas vom Stack poppen (ich hoffe das kommt nie mehr als zehn mal vor).
Nach jedem Funktionsaufruf wird der Datenstackpointer wiederhergestellt, um das was vom Puffer noch da ist verschwinden zu lassen und um auf dem Stack liegen gebliebene Daten zu verwerfen. Der Stackpointer zeigt also genau dahin wo er vorher drauf gezeigt hat.
Als netter Nebeneffekt ist es nun nicht mehr notwendig darauf zu achten, dass Hooks genau nur einmalig Initialisiert werden.
Folgendes ist nicht mehr nötig:
const int once = 0;
if (!once) {
HookEngineF(0x123456, 5, foo);
once = 1;
};
Im Folgenden hooken foo() und bar() jeweils nur einmal die Adresse 0x123456. Alle weiteren identischen Initialisierungen werden ignoriert. Die Reihenfolge bleibt aber bestehen: erst wird foo() dann wird bar() auf gerufen.
HookEngineF(0x123456, 5, foo); // Initialize hook and add foo() to EventHandler
HookEngineF(0x123456, 5, foo); // Ignore
HookEngineF(0x123456, 5, bar); // Add bar() to EventHandler
HookEngineF(0x123456, 5, foo); // Ignore
HookEngineF(0x123456, 5, bar); // Ignore
HookEngineF(0x123456, 5, foo); // Ignore
Wo ich noch unsicher bin, ist bei der Sicherung von ÿINSTANCE_HELP. Das Symbol selbst führte bei mir in Deadalus zu einem Syntaxfehler (instHlpBak = _@(ÿINSTANCE_HELP);). Ob folgender Ansatz richtig ist, weiss ich allerdings nicht, weil hier nur der Offset (Datenzeiger) des Instanzsymbols gesichert wird.
// Get address of yINSTANCE_HELP by symbol index 0
const int instHlpAddr = 0;
if (!instHlpAddr) {
instHlpAddr = MEM_GetSymbolByIndex(0)+zCParSymbol_offset_offset;
};
// Also secure global instances
// [...]
var int iHlpBak; iHlpBak = MEM_ReadInt(instHlpAddr); // Is the correct way to do this?
// [...]
// Restore global instances in between function calls
// [...]
MEM_WriteInt(instHlpAddr, iHlpBak);
Mich würde freuen, wenn ein Paar/paar prüfende Augen die Revision im dev-Branch im SVN (https://app.assembla.com/spaces/lego2/subversion/commits/139) anschauen könnten. Insgesamt sind es nicht viele Zeilen.
Moddern rate ich mit der neuen Implementierung vorsichtig zu sein, bis ein Zweiter meinen Code angeschaut hat, weil viele LeGo-Paket auf Hooks aufbauen.
EDIT: An der Benutzung von Hooks ändert sich übrigens nichts und die Implementierung ist Rückwärts-kompatibel.
EDIT2: Eine weitere Unsicherheit habe ich vergessen: In der alten Implementierung von Hooks lassen sich nur ECX, EAX und EDI duch die Deadalus-Funktion manipulieren. ESI, EBX und EDX aber nicht. Gibt es dafür einen Grund?
Sehr gute Arbeit! :A
Ich bin ein wenig überrascht, dass zCParser::CallFunc beliebige Parameterzahlen vom Stack poppt (natürlich anhand der Daedalus-Funktionssignatur), aber umso besser, wenn es so simpel ist :)
Deine Sicherung von ÿINSTANCE_HELP sieht korrekt aus - MEM_AssignInst() macht im Prinzip auch nur das.
Vermutlich sollte man noch die momentane Instanz sichern (siehe MEM_Get/SetUseInstance() in EngineAdr_G2.d), aber das muss am besten direkt am Anfang/Ende der Funktion passieren, weil man das Symbol sonst selber überschreibt bevor man es speichern kann. Eventuell funken einem hier die locals() dazwischen - das müsste man ausprobieren.
EDIT2: Eine weitere Unsicherheit habe ich vergessen: In der alten Implementierung von Hooks lassen sich nur ECX, EAX und EDI duch die Deadalus-Funktion manipulieren. ESI, EBX und EDX aber nicht. Gibt es dafür einen Grund?
Ja und zwar einen ganz einfachen: Wir waren nicht klug genug, um den Stack zu benutzen um die Register zu setzen/lesen, d.h. ich habe alle Opcodes dafür von Hand rausgesucht. Da das relativ lästig war, hab ich das eher nach Bedarf gemacht :p
mud-freak
24.10.2017, 11:03
Habe zwar schonmal gefragt wegen Schadernsberechnung in G1 ändern (thread) (https://forum.worldofplayers.de/forum/threads/1149697-Script-Eigene-Schadensberechnung/page2), aber das war relativ lange her sodass ich nochmal nachfrage ob es inzwischen für G1 möglich ist.
Ja, das sollte funktionieren. Im selben Forenthread, den du verlinkt hast gibt es später einen Post mit einer Portierung für Gothic 1.
Wie du LeGo in Gothic 1 zum Laufen bringst, findest du hier in diesem Thread ein paar Posts über deinem. Dazu sei nochmals gesagt, dass keine Stabilität für den Development-Branch von LeGo (https://app.assembla.com/spaces/lego2/subversion/source/HEAD/dev) gewährleistet ist.
Vermutlich sollte man noch die momentane Instanz sichern (siehe MEM_Get/SetUseInstance() in EngineAdr_G2.d), aber das muss am besten direkt am Anfang/Ende der Funktion passieren, weil man das Symbol sonst selber überschreibt bevor man es speichern kann. Eventuell funken einem hier die locals() dazwischen - das müsste man ausprobieren.
MEM_Get/SetUseInstance() war schon drin. Ich habe es aber jetzt mal wie du sagst zu Beginn bzw. am Ende der Funktion aufgerufen. Ich habe mich nicht komplett in locals() reingefuchst, aber weil MEM_GetUseInstance() nun vor locals() aufgerufen wird, habe ich es in einer temporären Variable zwischengespeichert, damit es nach einer Umspeicherung anschliessend von locals() vernünftig auf den Peudostack gelegt wird (ich hoffe das habe ich richtig verstanden), siehe HookEngine.d in Rev 140 Zeile 36 (https://app.assembla.com/spaces/lego2/subversion/source/140/dev/HookEngine.d#ln36) und Zeile 63 (https://app.assembla.com/spaces/lego2/subversion/source/140/dev/HookEngine.d#ln63).
Testen kann ich das ganze leider nicht, weil ich keine Adresse kenne an der das beim Hooken zu einem Problem werden könnte. Die Sicherung der anderen globalen Instanzen scheint aber zu funktionieren; Den Bug den Nico mit den Dialogen im alten Lager in GFA entdeckt hatte, bezieht sich nur auf yINSTANCE_HELP und ist mit der neuen Implementierung gefixt.
Fisk2033
24.10.2017, 15:11
Ich habe mich mal hingesetzt und die Hooks neu implementiert (https://app.assembla.com/spaces/lego2/subversion/commits/139). Hier eine kleine Übersicht wie sie nun funktionieren:
[...]
Ich weiß ja noch, wer mir Ende 2016 geschrieben hat, dass er ab dem 31.12 in Ruhestand gehen wird und hauptsächlich nur noch auf PN's antwortet. :p
Spaß bei Seite, danke das du weiterhin so tolle und vor allem ausführlich dokumentierte Arbeit. :gratz
MEM_Get/SetUseInstance() war schon drin. Ich habe es aber jetzt mal wie du sagst zu Beginn bzw. am Ende der Funktion aufgerufen. Ich habe mich nicht komplett in locals() reingefuchst, aber weil MEM_GetUseInstance() nun vor locals() aufgerufen wird, habe ich es in einer temporären Variable zwischengespeichert, damit es nach einer Umspeicherung anschliessend von locals() vernünftig auf den Peudostack gelegt wird (ich hoffe das habe ich richtig verstanden), siehe HookEngine.d in Rev 140 Zeile 36 (https://app.assembla.com/spaces/lego2/subversion/source/140/dev/HookEngine.d#ln36) und Zeile 63 (https://app.assembla.com/spaces/lego2/subversion/source/140/dev/HookEngine.d#ln63).
Testen kann ich das ganze leider nicht, weil ich keine Adresse kenne an der das beim Hooken zu einem Problem werden könnte. Die Sicherung der anderen globalen Instanzen scheint aber zu funktionieren; Den Bug den Nico mit den Dialogen im alten Lager in GFA entdeckt hatte, bezieht sich nur auf yINSTANCE_HELP und ist mit der neuen Implementierung gefixt.
Du könntest einfach in einer Instanz-Funktion eine External aufrufen, die eine Hook auslöst. In Instanz-Funktionen wird die UseInstance nur einmal ganz am Anfang gesetzt (deswegen gibt es in Buffs.d auch eine Funktion "SAVE_GetFuncID()"), da sollte das also relativ auszuprobieren sein (sonst müsste man sich schon irgendetwas relativ exotisches konstruieren). Wenn nach der Hook die Attribute noch richtig zugeteilt werden, stimmt alles.
Das der Bug im alten Lager gefixt ist, ist super :A
Spaß bei Seite, danke das du weiterhin so tolle und vor allem ausführlich dokumentierte Arbeit. :gratz
Dem schließe ich mich an. Und dass die Entwicklung von LeGo jetzt etwas öffentlicher statt findet ist auch super - ich bin mir sicher dass die Arbeit "im stillen Kämmerlein" einige Leute abgeschreckt hat. Dabei würde ich mich freuen, wenn hier noch mehr Menschen etwas (auch kleine Dinge!) beitragen oder überhaupt Probleme/Ideen diskutieren.
Das ist eine hervorragende Möglichkeit, um seine Kenntnisse im Scripten auf die Probe zu stellen ;)
mud-freak
24.10.2017, 21:24
Du könntest einfach in einer Instanz-Funktion eine External aufrufen, die eine Hook auslöst.
Guter Vorschlag. Die Erklärung hat mir auch nochmal das grundlegende Problem etwas verständlicher gemacht. Ich habe das mal getestet und die UseInstance bleibt erhalten. Die Änderung an den Hooks sollte also (die uns bekannten) Probleme alle beseitigt haben.
Ich habe mich heute mal dran gesetzt alle Pakete (ausser Dialoggestures, Sprite und Render) auf Gothic 1 Kompatibilität zu prüfen, zu testen und ein paar Fehler zu beheben. Meiner Meinung könnte es also dann bald das nächste LeGo-Release mit "offizieller" Gothic 1 Unterstützung geben.
(Ich selbst bin da natürlich etwas heiss drauf, denn die Veröffentlichung von GFA Version 1.0 setzt die Neuerungen in LeGo voraus.)
Im LeGo-Wiki gibt es nun ein Beispiel für die ConsoleCommands (http://lego.worldofplayers.de/?Beispiele_ConsoleCommands). Das ist nicht das sinnvollste Beispiel, aber es zeigt ganz gut wie einfach solche Befehle erstellt werden können.
Da die Hooks jetzt etwas anders funktionieren, kann man sie nun auch während des Spiels (komplett) entfernen. Das will ich aber gar nicht so sehr an die grosse Glocke hängen, weil es einfach nicht so viel Sinn macht.
Fisk2033
25.10.2017, 08:08
Im LeGo-Wiki gibt es nun ein Beispiel für die ConsoleCommands (http://lego.worldofplayers.de/?Beispiele_ConsoleCommands). Das ist nicht das sinnvollste Beispiel, aber es zeigt ganz gut wie einfach solche Befehle erstellt werden können.
@Lehona wäre es eigentlich möglich so einen "letzte Änderung" Button einzufügen, wie im Editing-Wiki: (https://wiki.worldofgothic.de/doku.php?id=start&do=recent) ich hab "neue" Artikel wie Buffs nur durch Zufall gefunden. Eventuell lässt sich sowas ja ganz einfach einfügen, aber wäre auch kein Weltuntergang wenn nicht.
Den Bug den Nico mit den Dialogen im alten Lager in GFA entdeckt hatte, bezieht sich nur auf yINSTANCE_HELP und ist mit der neuen Implementierung gefixt.
Vielen Dank für die ganze Arbeit (insbesondere die Portierung auf G1 und die Überarbeitung der Hooks)! Ich werde nächste Woche auch endlich wieder zum Testen kommen :)
Teron Gorefiend
25.10.2017, 16:24
Ja, das sollte funktionieren. Im selben Forenthread, den du verlinkt hast gibt es später einen Post mit einer Portierung für Gothic 1.
Wie du LeGo in Gothic 1 zum Laufen bringst, findest du hier in diesem Thread ein paar Posts über deinem. Dazu sei nochmals gesagt, dass keine Stabilität für den Development-Branch von LeGo (https://app.assembla.com/spaces/lego2/subversion/source/HEAD/dev) gewährleistet ist.
EDIT: ok weiß beim besten willen nicht, was da gestern schief ging aber momentan scheint wieder alles zu klappen.
@Lehona wäre es eigentlich möglich so einen "letzte Änderung" Button einzufügen, wie im Editing-Wiki: (https://wiki.worldofgothic.de/doku.php?id=start&do=recent) ich hab "neue" Artikel wie Buffs nur durch Zufall gefunden. Eventuell lässt sich sowas ja ganz einfach einfügen, aber wäre auch kein Weltuntergang wenn nicht.
Möglich wäre das sicherlich, aber so gar nicht mein Gebiet :p Am Wochenende hab ich vielleicht Zeit, mir das mal ganz kurz anzuschauen - aber tendenziell eher nicht. Neuerungen in LeGo werden aber ja sowieso immer hier im Thread gepostet (den du ja sicherlich verfolgst :p) - insofern sollte das ja eigentlich reichen.
mud-freak
26.10.2017, 18:17
Ich habe eine kleine Spielerei anzubieten (nicht lohnenswert für den ScriptBin). Eigentlich sollte das ein Beispiel für die ConsoleCommands im LeGo-Wiki werden, allerdings eignete sich das nicht so gut.
Mit Eingabe von "shutup" kann man einen laufenden Dialog komplett beendet (egal, ob Choices anstehen oder mehrere Dialogzeilen). Sicherlich nur für Testzwecke sinnvoll, z.B. wenn man ständig vergisst Xardas aus der Startup zu entfernen. Vielleicht kann es der ein oder andere gebrauchen. Der Code setzt LeGo_ConsoleCommands und LeGo_FrameFunctions voraus.
func void _shutUp() {
if (InfoManager_HasFinished()) {
FF_Remove(_shutUp);
return;
};
var C_NPC slf; slf = _^(MEM_InformationMan.npc);
Npc_ClearAIQueue(hero);
Npc_ClearAIQueue(slf);
AI_StopProcessInfos(slf);
};
func string shutUp(var string _) {
if (InfoManager_HasFinished()) {
return "No dialog running";
};
if (TRIA_Running) {
return "Trialogs not supported";
};
// Continuously cancel OUs until they are all gone. (FF instead of loop, because the next OU is started after this frame.)
FF_ApplyOnce(_shutUp);
return "Done";
};
Und in der Init_Global den Konsolenbefehl registrieren:
CC_Register(shutUp, "shutup", "Stop any dialog in progress");
GiftGrün
26.10.2017, 20:27
Da Lehona erst vor Kurzem das Diskutieren von Problemen angesprochen hat:
Bei mir gibt:
ConcatStrings(IntToString(1),IntToString(2));
den String 22 zurück, statt 12 wie erwartet. Hatte bisher nicht die Gelegenheit, das in einer Vanilla-Installation zu testen.
A = IntToString(1);
B = IntToString(2);
ConcatStrings(A,B);
hingegen funktioniert. Es kommt mir aber ziemlich unwahrscheinlich vor, dass das eine meiner Änderungen hervorgerufen haben könnte. Viel eher sieht es aus, als ob die Implementierung von PB mit zwei i2s-Befehlen in derselben Instruktion nicht zurechtkommt. Also drei Fragen:
1) Kann jemand anderes das Verhalten bestätigen?
2) Könnte man eine LeGo-Funktion schreiben, die dieses Problem der External fixt?
3) Ist dieses Thema hier passend?
Ja, dieses Verhalten ist bekannt und konsistent (wenn auch nervig). Externals geben Strings über eine Art globales String-Objekt zurück (ich bin mir nicht mehr ganz sicher ob das pro External gilt oder für alle Externals der selbe String verwendet wird).
Es wäre mindestens aufwändig bis schwierig, denke ich. Ist das denn wirklich nötig?
Passend genug :p
GiftGrün
26.10.2017, 21:13
Nein, wirklich nötig ist es nicht. Ich wollte nur anderen das kopflose Debugging ersparen, das mich heute einige Zeit gekostet hat.
mud-freak
28.10.2017, 22:16
In GFA gibt es einige Möglichkeiten primitive Formen im dreidimensionalen Raum zu visualisieren, z.B. die Flugbahn von Pfeilen mit einer Linie, oder die Boundingboxen von bestimmten Körperteilen von Gegnern als kritische Trefferzonen. Im Grunde war die Idee dahinter, Weltkoordinaten besser zu veranschaulichen, denn reine Zahlenwerte sind nicht besonders aussagekräftig.
Mark56 hatte vor einiger Zeit vorgeschlagen, diese Funktionen auszulagern, weil sie auch ausserhalb von GFA für Modder recht attraktiv sein könnten.
Daher habe ich sie nun als LeGo-Paket umgeschrieben und generell nutzbar gemacht.
Da diese Visualisierung nur während dem Modden, aber nicht in einer fertigen Mod Sinn machen, fällt das Paket nicht unter die Initialisierung von LeGo_All, sondern ist wie Buffs und Render einzeln zu initialisieren (z.B. LeGo_Init(LeGo_All | LeGo_Draw3D);).
Wer mehr über dieses Paket erfahren will, kann sich die Dokumentation (http://lego.worldofplayers.de/?Draw3D)und Beispiele (http://lego.worldofplayers.de/?Beispiele_Draw3D) dazu ansehen.
Das Paket ist bisher natürlich nur im Developmentbranch (https://app.assembla.com/spaces/lego2/subversion/source/HEAD/dev).
anyone knows how to add moving text like in g3?
LeGo has PrintS builtin, it moves the text upwards on the left side of the screen.
LeGo has PrintS builtin, it moves the text upwards on the left side of the screen.
I know , I have tried to modify for my own.
It moves text up while PrintS is called again - I wanna to exclude that so text will be automatically move text for demanded height. I could not find which part of the code is responsible for that.
Other things like to modify the text coordinates or anim8 times I change without a problem.
Milky-Way
21.11.2017, 15:48
Lehona hat zum Release der neuen Version (weil es hier schon recht lang geworden ist) ein neues Thema aufgemacht, in dem es nun weiter geht:
https://forum.worldofplayers.de/forum/threads/1505251-Skriptpaket-LeGo-4
Powered by vBulletin® Version 4.2.2 Copyright ©2025 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.