Ich habe mal eine Frage zu der Lösung mit dem Schwierigkeitsgrad. Wie genau ist MENU_DIFFLEVEL in deinem Beispiel im Menü definiert und wie wird das gesetzt? Ich habe im Moment das Problem, dass ich nicht an den vor dem Spielstart ausgewählten (über ChoiceBox) Schwierigkeitsgrad herankomme und hoffe es mit der GetAnyParserSymbol-Methode lösen zu können.
Ich habe mal eine Frage zu der Lösung mit dem Schwierigkeitsgrad. Wie genau ist MENU_DIFFLEVEL in deinem Beispiel im Menü definiert und wie wird das gesetzt? Ich habe im Moment das Problem, dass ich nicht an den vor dem Spielstart ausgewählten (über ChoiceBox) Schwierigkeitsgrad herankomme und hoffe es mit der GetAnyParserSymbol-Methode lösen zu können.
In dem Beispiel ist MENU_DIFFLEVEL einfach eine fest definierte Konstante zur simplen Demonstration. Wie GiftGrün seine Variable DifficultyLevel aus dem Menü heraus gesetzt hat, wäre tatsächlich interessant.
Der Code in den INSTANCEs wird dann ausgeführt, sobald diese Instanz erstellt wird (d.h., sobald der Menüeintrag, z.B. "leicht", ausgewählt wird).
“Da ist auch noch ein anderer Geruch in der Luft, der Geruch von Feuern, die in der Ferne brennen, mit einem Hauch Zimt darin - so riecht das Abenteuer!”
― aus Walter Moers' "Die 13 1/2 Leben des Käpt'n Blaubär"
“Da ist auch noch ein anderer Geruch in der Luft, der Geruch von Feuern, die in der Ferne brennen, mit einem Hauch Zimt darin - so riecht das Abenteuer!”
― aus Walter Moers' "Die 13 1/2 Leben des Käpt'n Blaubär"
Danke für das Video! Hat mir nochmal geholfen dein System zu verstehen. Ich hab's bei mir jetzt auch so einbauen können durch deine Beispiele und sogar mit meiner vorherigen Lösung das im Menü nachträglich noch umstellen zu können verbinden können. Falls der ungefähre Code jemanden interessiert:
Code:
// Zum einmaligen initialisieren des Schwierigkeitsgrades durch die Auswahl beim Spielstart
func void UpdateDifficultyAfterLoading() {
if (!modDifficultyInitialized) {
var int symPtr; symPtr = GetAnyParserSymbol(MEM_ReadInt(menuParserPointerAddress), "MENU_DIFFLEVEL");
if (symPtr) {
var zCPar_Symbol symb; symb = _^(symPtr);
setDifficulty(symb.content);
MEM_SetGothOpt("SPECIALOPTIONS", "difficulty", IntToString(symb.content));
} else {
// Sollte eigentlich nicht passieren. Aber nur für den Fall.
setDifficulty(DIFFICULTY_03);
MEM_SetGothOpt("SPECIALOPTIONS", "difficulty", "2"); // Normal
};
modDifficultyInitialized = true;
};
};
// Wird in Init_Global aufgerufen, um den Menüeintrag auf die Schwierigkeit des aktuellen Savegames zu stellen
func void UpdateDifficultyInMenuForCurrentGame() {
MEM_SetGothOpt("SPECIALOPTIONS", "difficulty", IntToString(modDifficulty - 1));
};
// Wird in einer Triggerschleife regelmäßig aufgerufen, um regelmäßig zu prüfen, ob der Spieler den Schwierigkeitsgrad im Menü geändert hat
func void UpdateDifficultyFromMenuOptions() {
if (modDifficulty) { // Weil wegen wird beim Spielstart gesetzt. Sollte unter keinen Umständen direkt am Anfang gefeuert werden.
var int difficulty;
difficulty = STR_ToInt(MEM_GetGothOpt("SPECIALOPTIONS", "difficulty")) + 1; // Da Einstellungen 0-basiert
setDifficulty(difficulty);
};
};
Danke für das Video! Hat mir nochmal geholfen dein System zu verstehen. Ich hab's bei mir jetzt auch so einbauen können durch deine Beispiele und sogar mit meiner vorherigen Lösung das im Menü nachträglich noch umstellen zu können verbinden können. Falls der ungefähre Code jemanden interessiert:
Code:
// Zum einmaligen initialisieren des Schwierigkeitsgrades durch die Auswahl beim Spielstart
func void UpdateDifficultyAfterLoading() {
if (!modDifficultyInitialized) {
var int symPtr; symPtr = GetAnyParserSymbol(MEM_ReadInt(menuParserPointerAddress), "MENU_DIFFLEVEL");
if (symPtr) {
var zCPar_Symbol symb; symb = _^(symPtr);
setDifficulty(symb.content);
MEM_SetGothOpt("SPECIALOPTIONS", "difficulty", IntToString(symb.content));
} else {
// Sollte eigentlich nicht passieren. Aber nur für den Fall.
setDifficulty(DIFFICULTY_03);
MEM_SetGothOpt("SPECIALOPTIONS", "difficulty", "2"); // Normal
};
modDifficultyInitialized = true;
};
};
// Wird in Init_Global aufgerufen, um den Menüeintrag auf die Schwierigkeit des aktuellen Savegames zu stellen
func void UpdateDifficultyInMenuForCurrentGame() {
MEM_SetGothOpt("SPECIALOPTIONS", "difficulty", IntToString(modDifficulty - 1));
};
// Wird in einer Triggerschleife regelmäßig aufgerufen, um regelmäßig zu prüfen, ob der Spieler den Schwierigkeitsgrad im Menü geändert hat
func void UpdateDifficultyFromMenuOptions() {
if (modDifficulty) { // Weil wegen wird beim Spielstart gesetzt. Sollte unter keinen Umständen direkt am Anfang gefeuert werden.
var int difficulty;
difficulty = STR_ToInt(MEM_GetGothOpt("SPECIALOPTIONS", "difficulty")) + 1; // Da Einstellungen 0-basiert
setDifficulty(difficulty);
};
};
Das verstehe ich nicht ganz. Den Ansatz, die Symbole aus den Menu-Skripten auslesen zu können, bedarf es nur, wenn man wie GiftGrün vermeiden möchte, Einträge in der Gothic.ini zu erstellen. Da du diese nun aber explizit erstellst und darauf zurückgreifst, brauchst du diesen ganzen Umweg gar nicht, sondern kannst es direkt so machen, wie Dada das hier noch einmal gezeigt hat:
Zitat von Dada
Ich habe das (vielleicht) etwas einfacher gelöst.
In den Menü-Skripten habe ich einen weiteren Einstellungspunkt angelegt, worüber man die Schwierigkeit einstellen kann.
In einer FrameFunction lasse ich nun jeden Frame die gesetzte Einstellung überprüfen. Der Schwierigkeitsgrad wird ja in die Ini-Datei geschrieben und kann daher fix ausgelesen werden:
Code:
func void setDifficulty()
{
var int difficulty;
difficulty = STR_ToInt(MEM_GetGothOpt("MAHLENDUR", "difficulty"));
// Do Stuff...
};
Durch den ersten Start mit den geänderten Menü-Skripten, werden die Grundwerte automatisch eingetragen und daher ist auch keine Fehlerbehandlung ob "0" nötig.
Ich hatte das Problem, dass diese Lösung beim Spielstart nicht ganz geklappt hat. Dazu wollte ich den Schwierigkeitsgrad "normal" vorselektiert haben. Das hat beim ersten Spielstart auch nicht ganz hingehauen. Von daher finde ich das mit den zu Anfang auswählbaren Menüpunkten ganz charamant, da man dort einstellen kann, dass ein Menüpunkt vorselektiert ist.
Kennt ihr eigentlich das Problem, dass die Maussteuerung in Gothic auf high-DPI-Monitoren sehr langsam ist? Oder auf high-res-Monitoren. Das Problem ist, bei einem 4k-Monitor ist die Verlangsamung so stark, dass sogar MouseSensitivity=1 das Problem nicht mehr beheben kann. Und höhere Werte werden anscheinend ignoriert. Könnte man da vielleicht irgendwie einen Korrekturfaktor einbauen, oder die Funktion, die auf MouseSensitivity zugreift, so abändern, dass sie beliebige (positive) Werte zulässt?
Falls das nicht möglich ist: Ist dieses Problem eigentlich verwandt damit, dass Gothic die Maus in der Mitte des Bildschirms "festklebt"? Das ist nämlich der Hauptgrund, wieso ich nicht einfach Windows die high-DPI-Skalierung übernehmen lasse (was dieses Problem möglicherweise lösen würde): Alles läuft prächtig, bis man ins Spiel kommt und der Held sich bei jeder noch so kleinen Mausbewegung schlagartig nach unten rechts dreht, weil die Maus in der Mitte des unskalierten Bildschirms sitzt, während für das kleinere Gothic-Fenster die Maus ganz am unteren rechten Rand zu liegen scheint. Das Resultat ist, dass Gothic jeden Maus-Input als "scharf nach unten rechts drehen" interpretiert.
EDIT: Der Fehler tritt auch mit dem Systempack auf, deshalb meine Frage.
“Da ist auch noch ein anderer Geruch in der Luft, der Geruch von Feuern, die in der Ferne brennen, mit einem Hauch Zimt darin - so riecht das Abenteuer!”
― aus Walter Moers' "Die 13 1/2 Leben des Käpt'n Blaubär"
While playing a game at least few minutes some dialogs of npc have been skipped so very often I see sentences mostly pc _hero. there is no rule of that. Mostly after save/load game problem is partly fixed and occurs again in further time of the game. There is no rule, sometimes is very often OK.
i wish to found a function which may be responsible for that, but I have no idea how to find it.
Kennt ihr eigentlich das Problem, dass die Maussteuerung in Gothic auf high-DPI-Monitoren sehr langsam ist? Oder auf high-res-Monitoren. Das Problem ist, bei einem 4k-Monitor ist die Verlangsamung so stark, dass sogar MouseSensitivity=1 das Problem nicht mehr beheben kann. Und höhere Werte werden anscheinend ignoriert. Könnte man da vielleicht irgendwie einen Korrekturfaktor einbauen, oder die Funktion, die auf MouseSensitivity zugreift, so abändern, dass sie beliebige (positive) Werte zulässt?
Falls das nicht möglich ist: Ist dieses Problem eigentlich verwandt damit, dass Gothic die Maus in der Mitte des Bildschirms "festklebt"? Das ist nämlich der Hauptgrund, wieso ich nicht einfach Windows die high-DPI-Skalierung übernehmen lasse (was dieses Problem möglicherweise lösen würde): Alles läuft prächtig, bis man ins Spiel kommt und der Held sich bei jeder noch so kleinen Mausbewegung schlagartig nach unten rechts dreht, weil die Maus in der Mitte des unskalierten Bildschirms sitzt, während für das kleinere Gothic-Fenster die Maus ganz am unteren rechten Rand zu liegen scheint. Das Resultat ist, dass Gothic jeden Maus-Input als "scharf nach unten rechts drehen" interpretiert.
EDIT: Der Fehler tritt auch mit dem Systempack auf, deshalb meine Frage.
Probier mal:
Zitat von Fisk2033
Ich weiß nicht, ob man es ohne das Systempack von MarGoth so nutzen kann, aber ich habe es hin bekommen!
In der ini hab ich den Eintrag:
zMouseRotationScale=4.0
gemacht und schon kann ich mich ohne Probleme mit der Maus bewegen! :P
Vielen Dank, es bessert das Problem etwas (es scheint aber auf horizontale Bewegung mehr zu wirken als auf vertikale ).
“Da ist auch noch ein anderer Geruch in der Luft, der Geruch von Feuern, die in der Ferne brennen, mit einem Hauch Zimt darin - so riecht das Abenteuer!”
― aus Walter Moers' "Die 13 1/2 Leben des Käpt'n Blaubär"
While playing a game at least few minutes some dialogs of npc have been skipped so very often I see sentences mostly pc _hero. there is no rule of that. Mostly after save/load game problem is partly fixed and occurs again in further time of the game. There is no rule, sometimes is very often OK.
i wish to found a function which may be responsible for that, but I have no idea how to find it.
I have that on all Lego versions.
Two simple reasons come to mind:
1) have you updated the output units?
2) dialogues that end with AI_StopProcessInfos(self); cause problems if self doesn't say anything beforehand (i.e. only other speaks).
I'm not putting the rest of it because I already know it works perfectly. The logic is that the debuff is applied before calculating the damage, then the damage is calculated (return dmg; ).
Without ZSpy the game crashes, with ZSpy it says this ^, the NPC doesn't lose hp and the hero somehow gets the XP. I'm clueless, is there a way to fix this or an alternative? Thank you!
Edit: Upon trying again, it gave me XP worth of a sheep, a wolf and a couple 200 XP. I don't know why hahaha, I was focusing the enemy the whole time (who didn't break a sweat, didn't lose any hp)
This is a weird error. MEM_PtrToInst is called with a seemingly legal pointer (457846256), yet the MEM_Error-Line shows a pointer of -1. Looking at the code of MEM_PtrToInst there is no reason to have different values on those two lines of the stacktrace, the parameter is taken directly and turned into a string.
Code:
func MEMINT_HelperClass MEM_PtrToInst (var int ptr) {
var MEMINT_HelperClass hlp;
const int hlpOffsetPtr = 0;
if (!hlpOffsetPtr) {
hlpOffsetPtr = MEM_ReadIntArray (currSymbolTableAddress, hlp) + zCParSymbol_offset_offset;
};
if (ptr <= 0) {
if (ptr < 0) {
MEM_Error (ConcatStrings ("MEM_PtrToInst: Invalid pointer: ", IntToString (ptr)));
return;
} else if (!MEM_AssignInstSuppressNullWarning) {
/* Instanzen die Null sind, will man eigentlich nicht, die machen nur Ärger. */
MEM_Warn ("MEM_PtrToInst: ptr is NULL. Use MEM_NullToInst if that's what you want.");
};
MEM_WriteInt(hlpOffsetPtr, 0);
} else {
MEM_WriteInt(hlpOffsetPtr, ptr);
};
MEMINT_StackPushInst (hlp);
};
Are you initializing the buffs using LeGo_Init(LeGo_All | LeGo_Buffs)? They have to be initialized explicitly because they have not been tested sufficiently. Unfortunately that should only impact the buff display of the hero, but maybe there's some other bug, so you should try that first.
Does that happen every time you try it? Are there any level changes involved? If I can reproduce the bug I can probably fix it.
Edit: Ikarus does some funky stuff to _^() to increase performance, that may be the reason why it displays 457846256 instead of -1 (probably the pointer to -1). That happens because Buff_GetNpc() returns -1 when the NPC was not found (e.g. level change). This should be considered a bug because missing pointers are generally signalled by a 0. For now you can change line 69 in Talents.d to a 0, this change will be included in later releases automatically.
This should properly guard your function so it will just exit instead of accessing empty instances/invalid pointers. So at least it won't crash anymore...
Edit2: Are you using the new version of the damage script that includes the damage descriptor? I can't see a reason for this behaviour, but it's new and might have bugs?
Edit: Ikarus does some funky stuff to _^() to increase performance, that may be the reason why it displays 457846256 instead of -1 (probably the pointer to -1). That happens because Buff_GetNpc() returns -1 when the NPC was not found (e.g. level change). This should be considered a bug because missing pointers are generally signalled by a 0. For now you can change line 69 in Talents.d to a 0, this change will be included in later releases automatically.
This should properly guard your function so it will just exit instead of accessing empty instances/invalid pointers. So at least it won't crash anymore...
Edit2: Are you using the new version of the damage script that includes the damage descriptor? I can't see a reason for this behaviour, but it's new and might have bugs?
Thank you for your answer, I'll edit the line right away. Yes, I've started LeGo using | LeGo_Buffs in Startup.d.
To be frank I thought there was only ONE version of the damage script, I'll find the new one and edit this message when I'll try it.
In the meantime, I've found a workaround to "force" the game to recognize the hero's focus as THE target (since it happens when swining the sword at someone, there's 99.9% chance of success) and it works now. Here's what I changed:
Spoiler:(zum lesen bitte Text markieren)
Code:
func void Bleed_damage(var int bh) {
var int ptr; ptr = Buff_GetNpc(bh);
if (!ptr) { return; };
var oCNpc _hero; _hero = Hlp_GetNpc(hero);
var c_npc pNpc; pNpc = MEM_PtrToInst(_hero.focus_vob);
var c_npc n; n = _^(ptr);
if(!Hlp_Is_oCNpc(_hero.focus_vob))
{
return;
}
else
{
Wld_PlayEffect("BleedEffect",pNpc,pNpc,0,0,0,FALSE);
B_MagicHurtNpc(hero,pNpc,hero.level);
};
};
I know, it's so spaghetti and wrong that it hurts, but it works
Edit #2: Without "workaround", doesn't work with the new dmg_ondmg function.
Levelchange, with the workaround, is NOT an issue. Script works fine after save/load and levelchange.
Geändert von Vic7im (19.03.2018 um 17:23 Uhr)
Grund: Pasted wrong workaround, fixed with highlighted changes.
While playing a game at least few minutes some dialogs of npc have been skipped so very often I see sentences mostly pc _hero. there is no rule of that. Mostly after save/load game problem is partly fixed and occurs again in further time of the game. There is no rule, sometimes is very often OK.
i wish to found a function which may be responsible for that, but I have no idea how to find it.
I have that on all Lego versions.
Hey pawbuj,
I had same problem! It took me a while till I fixed it - I have noticed in zSpy log following message:
NPC: Output-Unit: ... not started. Another NSC is having conversation with targetNPC: PC_HERO
From that I assumed that hero got somehow into another conversation with another NPC's. As soon as I have updated these 2 functions below - problem was fixed (red is old code, green adjusted):
Code:
FUNC VOID B_Say (var C_NPC slf, var C_NPC oth, var string text)
{
B_SmartTurnToNpc (slf, oth);
//AI_OutputSVM (slf, oth, text);
if (NPC_IsInState (slf, ZS_Talk))
|| (NPC_IsInState (oth, ZS_Talk))
{
AI_OutputSVM (slf, oth, text);
} else {
AI_OutputSVM (slf, NULL, text);
};
};
FUNC VOID B_SayOverlay (var C_NPC slf, var C_NPC oth, var string text)
{
B_SmartTurnToNpc (slf, oth);
//AI_OutputSVM_Overlay (slf, oth, text);
if (NPC_IsInState (slf, ZS_Talk))
|| (NPC_IsInState (oth, ZS_Talk))
{
AI_OutputSVM_Overlay (slf, oth, text);
} else {
AI_OutputSVM_Overlay (slf, NULL, text);
};
};
Geändert von F a w k e s (08.02.2019 um 19:27 Uhr)
have u also fix the bug with mobswitch state01 after save/load game? It causes the mover being stucked when mobswitch will be in position 1 (gate closed) while saving.
have u also fix the bug with mobswitch state01 after save/load game? It causes the mover being stucked when mobswitch will be in position 1 (gate closed) while saving.
Hi pawbuj,
uh oh, I personally did not encounter such an issue. Can you please tell us how to simply replicate this (ideally on G1 original version) in order to get an idea how to fix it?
Hello folks,
Finally finished one I believe nifty feature for my small G1 mod - so wanted to share it in case someone else would find it usefull!
Trading system in G1 is painfull. Below hook of oCViewDialogTrade__OnAccept will improve it a little-bit.
As soon as you select what items you want to sell & buy you can hit Enter to Accept trade - you wont need to make the $ amount on both Trader's item container and Buyer's item container equal. Just confirm Accept trade. [Bild: gm_trade_en_01.png]
Ore will be transferred automatically. (in case Trader / Buyer do have enough ore) [Bild: gm_trade_en_02.png]
//Requires LeGo G1 [tested with LeGo 2.5.0]
//[G1 addresses]
//0072A870 .text Debug data ?OnAccept@oCViewDialogTrade@@IAIXXZ
const int oCViewDialogTrade__OnAccept = 7514224;
//00729390 .text Debug data ?TransferAccept@oCViewDialogTrade@@IAIXXZ
const int oCViewDialogTrade__TransferAccept = 7508880;
//'Cannibalized' from Ikarus 1.2 oCNpc class definition
class oCItemContainer {
var int inventory2_vtbl; // 0x0550 1360
var int inventory2_oCItemContainer_contents; // 0x0554 zCListSort<oCItem>*
var int inventory2_oCItemContainer_npc; // 0x0558 oCNpc*
var int inventory2_oCItemContainer_selectedItem; // 0x055C int
var int inventory2_oCItemContainer_offset; // 0x0560 int
var int inventory2_oCItemContainer_drawItemMax; // 0x0564 int
var int inventory2_oCItemContainer_itemListMode; // 0x0568 oTItemListMode
var int inventory2_oCItemContainer_frame; // 0x056C zBOOL
var int inventory2_oCItemContainer_right; // 0x0570 zBOOL
var int inventory2_oCItemContainer_ownList; // 0x0574 zBOOL
var int inventory2_oCItemContainer_prepared; // 0x0578 zBOOL
var int inventory2_oCItemContainer_passive; // 0x057C zBOOL
var int inventory2_oCItemContainer_viewCat; // 0x0580 zCView*
var int inventory2_oCItemContainer_viewItem; // 0x0584 zCView*
var int inventory2_oCItemContainer_viewItemActive; // 0x0588 zCView*
var int inventory2_oCItemContainer_viewItemHightlighted; // 0x058C zCView*
var int inventory2_oCItemContainer_viewItemActiveHighlighted; // 0x0590 zCView*
var int inventory2_oCItemContainer_viewItemFocus; // 0x0594 zCView*
var int inventory2_oCItemContainer_viewItemActiveFocus; // 0x0598 zCView*
var int inventory2_oCItemContainer_viewItemHightlightedFocus; // 0x059C zCView*
var int inventory2_oCItemContainer_viewItemActiveHighlightedFocus; // 0x05A0 zCView*
var int inventory2_oCItemContainer_viewItemInfo; // 0x05A4 zCView*
var int inventory2_oCItemContainer_viewItemInfoItem; // 0x05A8 zCView*
var int inventory2_oCItemContainer_textView; // 0x05AC zCView*
var int inventory2_oCItemContainer_viewArrowAtTop; // 0x05B0 zCView*
var int inventory2_oCItemContainer_viewArrowAtBottom; // 0x05B4 zCView*
var int inventory2_oCItemContainer_rndWorld; // 0x05B8 zCWorld*
var int inventory2_oCItemContainer_posx; // 0x05BC int
var int inventory2_oCItemContainer_posy; // 0x05C0 int
var string inventory2_oCItemContainer_textCategoryStatic; // 0x05C4 zstring
var int inventory2_oCItemContainer_m_bManipulateItemsDisabled; // 0x05D8 zBOOL
var int inventory2_oCItemContainer_m_bCanTransferMoreThanOneItem; // 0x05DC zBOOL
var int inventory2_oCItemContainer_image_chroma; // 0x05E0 zCOLOR
var int inventory2_oCItemContainer_blit_chroma; // 0x05E4 zCOLOR
// }
var int inventory2_owner; // 0x05E8 oCNpc*
var int inventory2_packAbility; // 0x05EC zBOOL
// zCListSort<oCItem>[INV_MAX] inventory {
var int inventory2_inventory0_Compare; // 0x05F0 int(_cdecl*)(oCItem*,oCItem*)
var int inventory2_inventory0_data; // 0x05F4 oCItem*
var int inventory2_inventory0_next; // 0x05F8 zCListSort<oCItem>*
var int inventory2_inventory1_Compare; // 0x05FC int(_cdecl*)(oCItem*,oCItem*)
var int inventory2_inventory1_data; // 0x0600 oCItem*
var int inventory2_inventory1_next; // 0x0604 zCListSort<oCItem>*
var int inventory2_inventory2_Compare; // 0x0608 int(_cdecl*)(oCItem*,oCItem*)
var int inventory2_inventory2_data; // 0x060C oCItem*
var int inventory2_inventory2_next; // 0x0610 zCListSort<oCItem>*
var int inventory2_inventory3_Compare; // 0x0614 int(_cdecl*)(oCItem*,oCItem*)
var int inventory2_inventory3_data; // 0x0618 oCItem*
var int inventory2_inventory3_next; // 0x061C zCListSort<oCItem>*
var int inventory2_inventory4_Compare; // 0x0620 int(_cdecl*)(oCItem*,oCItem*)
var int inventory2_inventory4_data; // 0x0624 oCItem*
var int inventory2_inventory4_next; // 0x0628 zCListSort<oCItem>*
var int inventory2_inventory5_Compare; // 0x062C int(_cdecl*)(oCItem*,oCItem*)
var int inventory2_inventory5_data; // 0x0630 oCItem*
var int inventory2_inventory5_next; // 0x0634 zCListSort<oCItem>*
var int inventory2_inventory6_Compare; // 0x0638 int(_cdecl*)(oCItem*,oCItem*)
var int inventory2_inventory6_data; // 0x063C oCItem*
var int inventory2_inventory6_next; // 0x0640 zCListSort<oCItem>*
var int inventory2_inventory7_Compare; // 0x0644 int(_cdecl*)(oCItem*,oCItem*)
var int inventory2_inventory7_data; // 0x0648 oCItem*
var int inventory2_inventory7_next; // 0x064C zCListSort<oCItem>*
var int inventory2_inventory8_Compare; // 0x0650 int(_cdecl*)(oCItem*,oCItem*)
var int inventory2_inventory8_data; // 0x0654 oCItem*
var int inventory2_inventory8_next; // 0x0658 zCListSort<oCItem>*
// }
var string inventory2_packstring; // 0x065C zstring[INV_MAX]
var string inventory2_packstring1;
var string inventory2_packstring2;
var string inventory2_packstring3;
var string inventory2_packstring4;
var string inventory2_packstring5;
var string inventory2_packstring6;
var string inventory2_packstring7;
var string inventory2_packstring8;
var int inventory2__offset[9]; // 0x0710 int[INV_MAX]
var int inventory2__itemnr[9]; // 0x0734 int[INV_MAX]
var int inventory2_maxSlots[9]; // 0x0758 int[INV_MAX]
var int inventory2_invnr; // 0x077C int
};
FUNC VOID _HOOK_TRADE_ONACCEPT ()
{
var int ptr;
var oCItemContainer Item_Container_Trader;
var oCItemContainer Item_Container_Trader_Offer;
var oCItemContainer Item_Container_Buyer_Offer;
var oCItemContainer Item_Container_Buyer;
//--- Get All 4 item containers (oCItemContainer)
//Traders Inventory
ptr = MEMINT_oCInformationManager_Address;
ptr = MEM_ReadInt (ptr + 24); //oCInformationManager.ocViewDialogTrade
if (ptr) {
ptr = MEM_ReadInt (ptr + 248); //oCViewDialogItemInventory
if (ptr) {
ptr = MEM_ReadInt (ptr + 256); //oCItemContainer
Item_Container_Trader = _^ (ptr);
};
};
//Traders 'offer'
ptr = MEMINT_oCInformationManager_Address;
ptr = MEM_ReadInt (ptr + 24); //oCInformationManager.ocViewDialogTrade
if (ptr) {
ptr = MEM_ReadInt (ptr + 252); //oCViewDialogStealContainer
if (ptr) {
ptr = MEM_ReadInt (ptr + 256); //oCItemContainer
Item_Container_Trader_Offer = _^ (ptr);
};
};
//Buyers 'offer'
ptr = MEMINT_oCInformationManager_Address;
ptr = MEM_ReadInt (ptr + 24); //oCInformationManager.ocViewDialogTrade
if (ptr) {
ptr = MEM_ReadInt (ptr + 260); //oCViewDialogItemContainer
if (ptr) {
ptr = MEM_ReadInt (ptr + 256); //oCItemContainer
Item_Container_Buyer_Offer = _^ (ptr);
};
};
//Buyers inventory
ptr = MEMINT_oCInformationManager_Address;
ptr = MEM_ReadInt (ptr + 24); //oCInformationManager.ocViewDialogTrade
if (ptr) {
ptr = MEM_ReadInt (ptr + 264); //oCViewDialogItemInventory
if (ptr) {
ptr = MEM_ReadInt (ptr + 256); //oCItemContainer
Item_Container_Buyer = _^ (ptr);
};
};
//--- Get Offer Value + Ore amount for both Trader and Buyer
var int Value_Traders_Offer;
var int Value_Buyers_Offer;
var int Ore_Trader;
var int Ore_Buyer;
var int Delta;
//inventory2_oCItemContainer_textCategoryStatic contains Total Value of offers - we can exploit that
Value_Traders_Offer = STR_ToInt (Item_Container_Trader_Offer.inventory2_oCItemContainer_textCategoryStatic);
Value_Buyers_Offer = STR_ToInt (Item_Container_Buyer_Offer.inventory2_oCItemContainer_textCategoryStatic);
var C_NPC Trader;
Trader = _^ (Item_Container_Trader.inventory2_owner);
Ore_Trader = NPC_HasItems (Trader, ItMiNugget);
var C_NPC Buyer;
Buyer = _^ (Item_Container_Buyer.inventory2_owner);
Ore_Buyer = NPC_HasItems (Buyer, ItMiNugget);
Delta = Value_Traders_Offer - Value_Buyers_Offer;
var string msg;
if (Delta == 0) {
//Trade went smoothly :)
} else
//Buyer has to supply ore !
if (Delta > 0) {
if (Delta > Ore_Buyer) {
PrintScreen_Ext ("Buyer does not have enough ore.", -1, _YPOS_MESSAGE_LOGENTRY, "font_old_20_white.tga", _TIME_MESSAGE_LOGENTRY);
} else
{
//Accept Transfer anyway!
ptr = MEMINT_oCInformationManager_Address;
ptr = MEM_ReadInt (ptr + 24); //oCInformationManager.ocViewDialogTrade
CALL__thiscall (ptr, oCViewDialogTrade__TransferAccept);
//'Move' ore from Buyer to Trader
NPC_RemoveInvItems (Buyer, ItMiNugget, Delta);
CreateInvItems (Trader, ItMiNugget, Delta);
msg = ConcatStrings ("Ore transferred from Buyer: ", IntToString (Delta));
PrintScreen_Ext (msg, -1, _YPOS_MESSAGE_LOGENTRY, "font_old_20_white.tga", _TIME_MESSAGE_LOGENTRY);
};
} else
//Trader has to supply ore !
{
//Abs (Delta)
Delta = 0 - Delta;
if (Delta > Ore_Trader) {
PrintScreen_Ext ("Trader does not have enough ore.", -1, _YPOS_MESSAGE_LOGENTRY, "font_old_20_white.tga", _TIME_MESSAGE_LOGENTRY);
} else
{
//Accept Transfer anyway!
ptr = MEMINT_oCInformationManager_Address;
ptr = MEM_ReadInt (ptr + 24); //oCInformationManager.ocViewDialogTrade
CALL__thiscall (ptr, oCViewDialogTrade__TransferAccept);
//'Move' ore from Trader to Buyer
NPC_RemoveInvItems (Trader, ItMiNugget, Delta);
CreateInvItems (Buyer, ItMiNugget, Delta);
msg = ConcatStrings ("Ore transferred from Trader: ", IntToString (Delta));
PrintScreen_Ext (msg, -1, _YPOS_MESSAGE_LOGENTRY, "font_old_20_white.tga", _TIME_MESSAGE_LOGENTRY);
};
};
};
Hook it with:
HookEngine (oCViewDialogTrade__OnAccept, 6, "_HOOK_TRADE_ONACCEPT");
I actually hope someone [I don't want to point my finger to anyone specific Lehona, Gottfried, Mud-Freak!] can improve this even further.
Originally I didn't want to force trade by calling oCViewDialogTrade__TransferAccept and to move Ore from Buyer <- -> Trader directly.
Idea was to move Ore from Item_Container_Buyer to Item_Container_Buyer_Offer in case Buyer would have to supply ore,
or from Item_Container_Trader to Item_Container_Trader_Offer in case Trader would have to supply some ore.
However I was not able to make this work flawlessly - I struggled with item transfers from inventory2_oCItemContainer_contents
LeGo code is still out of my league!