Dabei ist die Sicherheitsabfrage glaub ich nicht einmal nötig. Jedenfalls ob mit oder ohne, sobald meine Init-Funktion aufgerufen wurde, crasht Gothic beim Laden eines Spielstandes. Die Funktion sieht so aus:
Im Prinzip werden dort also nur die (globalen) Handles befüllt, die dann später mit View_Open bzw. Button_Show beim Schlafen angezeigt werden und danach wieder ausgeblendet (nicht gelöscht) werden.
Scheint mir auch erstmal recht harmlos, ich sehe da nicht, warum Gothic beim Laden dadurch crashen sollte. Oder werden die Handles irgendwann ungültig? Was seltsam wäre, da ja selbst wenn die Init-Funktion jedes mal (also in der Init_Global) beim Laden aufgerufen wird, die Handles wieder befüllt und somit gültig gemacht werden.
Kannst du rausfinden, welche Zeile davon crasht?
Außerdem: Ein paar mehr Infos zum Fehler bitte! Gibt es einen Stacktrace oder eine AV? Steht das in der Init_Global() nach LeGo_Init()? Initialisierst du die nötigen Pakete?
Wie es aussieht crasht es, sobald der erste Button angelegt wird. Die Funktion steht in der Init_Global nach LeGo_Init(alles außer bloodsplats).
Hier mal der Auszug aus dem Stacktrace:
Wie es aussieht crasht es, sobald der erste Button angelegt wird.
Nein, das stürzt in Lego_Init schon ab beim resetten von PermMem.
Erinnert mich ein Stück weit an ein Problem in DirtySwamp was durch Inkompatibilität mit dem Systempack verursacht wurde. Falls du Systempack hast: probier es mal ohne.
Nein, das stürzt in Lego_Init schon ab beim resetten von PermMem.
Erinnert mich ein Stück weit an ein Problem in DirtySwamp was durch Inkompatibilität mit dem Systempack verursacht wurde. Falls du Systempack hast: probier es mal ohne.
Ne, das ist in diesem Fall ein Bug in LeGo und hat nichts mit dem Systempack zu tun. Die Buttons versuchen beim Laden ihren View zu löschen, der aber bereits gelöscht wurde. Das sollte eigentlich kein Problem sein, aber in den Buttons wurde nicht delete() sondern View_Delete() verwendet und letzteres hatte quasi einen Bug in der Implementierung (da es nicht direkt delete() verwendet, sondern es quasi nachimplementiert). In der Funktion _Button_Delete() einfach delete() zu verwenden, behebt den Bug bereits.
Hello folks,
With GenerationLost we were trying to figure out which functions can be hooked in order to find out, what item player took from NPC, put to chest or took from chest.
Might be there are other functions, but we found these 2:
oCItemContainer__Remove - is called on transfer from chest
oCItemContainer__TransferItem - is called on transfer from NPC
In case that someone would find this useful
Code:
//Gothic 1
//Tested with [Ikarus 1.2], [Lego 2.5.0]
//Class cannibalized from Ikarus 1.2 oCNPC class
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
};
//00669320 .text Debug data ?Remove@oCItemContainer@@UAEXPAVoCItem@@@Z
const int oCItemContainer__Remove = 6722336;
/***
This function is called on transfer
From chest to NPC
***/
FUNC VOID _HOOK_ITEMCONTAINER_REMOVE ()
{
//Do we need Item_Container for anything here ?
// var oCItemContainer Item_Container;
var oCItem itm;
var int amount;
var int itmInstance;
// Item_Container = _^(ECX);
itm = _^(MEM_ReadInt (ESP + 4));
//Get amount
amount = itm.amount;
//Get item instance
itmInstance = Hlp_GetInstanceID (itm);
};
/***
This function is called on transfer
From NPC to hero
From hero to chest
***/
//00669780 .text Debug data ?TransferItem@oCItemContainer@@MAEHHH\@Z
const int oCItemContainer__TransferItem = 6723456;
FUNC VOID _HOOK_ITEMCONTAINER_TRANSFERITEM ()
{
var oCItemContainer Item_Container;
var C_NPC npc;
var oCItem itm;
var int amount;
var int itmInstance;
var oCNPC her;
//ECX 8245076 -> oCNpcContainer
//007DCF54 .rdata Debug data ??_7oCNpcContainer@@6B@
//oCNpcContainer = 8245076 !!
//ESP + 8 1 -> quantity
Item_Container = _^(ECX);
//Get inventory owner
npc = _^(Item_Container.inventory2_owner);
//Get Selected item
itm = _^(List_GetS(Item_Container.inventory2_oCItemContainer_contents, Item_Container.inventory2_oCItemContainer_selectedItem + 2));
//Get amount
amount = itm.amount;
//Get item instance
itmInstance = Hlp_GetInstanceID (itm);
};
HookEngine (oCItemContainer__TransferItem, 5, "_HOOK_STEAL_CONTAINER");
HookEngine (oCItemContainer__Remove, 6, "_HOOK_ITEMCONTAINER_REMOVE");
That is already solved here, unless you need it for G1 specifically.
Edit: Somehow I missed the line about G1, I'm an idiot. A solution for G1 will probably be quite different (in terms of where to hook) due to the different trading system. My bad.
oCItemContainer__Remove - is called on transfer from chest
Note though, that it's only called when the last item of a stack is taken and therefore "removed" from the container completely. Every transaction in between (eg. single items of a stack) is not covered by that function, which doesn't make it suitable for my purpose, unfortunately.
Note though, that it's only called when the last item of a stack is taken and therefore "removed" from the container completely. Every transaction in between (eg. single items of a stack) is not covered by that function, which doesn't make it suitable for my purpose, unfortunately.
LOL I have never realized this is the case - I was always testing it with 1 itmiNugget
However I double checked all available addresses now - and found another one - if you will hook this one instead - function will be called with each item transfer from chest to NPC (if you will transfer 3 items at once - function will be called 3 times):
Code:
//006693D0 .text Debug data ?Remove@oCItemContainer@@UAEPAVoCItem@@PAV2@H@Z
const int oCItemContainer__Remove_Item = 6722512;
FUNC VOID _HOOK_ITEMCONTAINER__REMOVE_ITEM ()
{
//Do we need Item_Container for anything here ?
// var oCItemContainer Item_Container;
var oCItem itm;
var int amount;
var int itmInstance;
// Item_Container = _^(ECX);
itm = _^(MEM_ReadInt (ESP + 4));
//Get amount
amount = itm.amount;
//Get item instance
itmInstance = Hlp_GetInstanceID (itm);
};
HookEngine (oCItemContainer__Remove_Item, 6, "_HOOK_ITEMCONTAINER__REMOVE_ITEM");
@Fawkes,
u are doing great job!
do u how any idea how to change the value of selected item (of item category) while trading?
f.e. after finish some quest Fisk will pay you plus 25 % better for ur melee weapon .
Gothic 1 of course
Hi pawbuj,
Below code did the trick for me - when I tested it now.
I don't remember where I have taken first 2 functions from !! (Trade_ChangeSellMultiplier, Trade_ChangeBuyMultiplier) seems like I forgot to copy link to original post in this case Most likely somewhere from this forum - hope author don't mind
Code:
FUNC VOID Trade_ChangeSellMultiplier (var int mul)
{
var int ptr;
ptr = MEMINT_oCInformationManager_Address;
ptr = MEM_ReadInt (ptr + 24); //oCInformationManager.dlgTrade
ptr = MEM_ReadInt (ptr + 260); //dlgTrade.oCViewDialogItemContainer
MEM_WriteInt (ptr + 268, mul); //oCViewDialogItemContainer.Multiplier = mul
};
FUNC VOID Trade_ChangeBuyMultiplier (var int mul)
{
var int ptr;
ptr = MEMINT_oCInformationManager_Address;
ptr = MEM_ReadInt (ptr + 24); //oCInformationManager.dlgTrade
ptr = MEM_ReadInt (ptr + 252); //dlgTrade.oCViewDialogStealContainer
MEM_WriteInt (ptr + 268, mul); //oCViewDialogStealContainer.Multiplier = mul
};
//---
//0072A2B0 .text Debug data ?OnTransferLeft@oCViewDialogTrade@@IAIHH@Z
const int oCViewDialogTrade__OnTransferLeft = 7512752;
//0072A530 .text Debug data ?OnTransferRight@oCViewDialogTrade@@IAIHH@Z
const int oCViewDialogTrade__OnTransferRight = 7513392;
FUNC VOID _HOOK_VIEWDIALOGTRADE_ONTRANSFERLEFT ()
{
var int mul;
//Set by default to 0.3
mul = (divf (mkf (3), mkf (10)));
var int ptr;
var oCItemContainer Item_Container_Buyer;
var oCItem itm;
//Buyer's Item_Container - will give us Item and Inventory category
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 item
itm = _^(List_GetS(Item_Container_Buyer.inventory2_oCItemContainer_contents, Item_Container_Buyer.inventory2_oCItemContainer_selectedItem + 2));
};
};
var C_NPC npc;
npc = _^(MEM_InformationMan.npc);
//Fisk ?
if (npc.ID == 311)
{
//Is player selling Weapons?
if (Item_Container_Buyer.inventory2_invnr == INV_WEAPON)
{
//Set Sell multiplier to 0.5
mul = divf (mkf (5), mkf (10));
};
};
//if item.value == 1 - set multiplier to 100 % (otherwise value of the item would be 0!)
if (itm.Value == 1)
{
mul = mkf (1);
};
Trade_ChangeSellMultiplier (mul);
};
FUNC VOID _HOOK_VIEWDIALOGTRADE_ONTRANSFERRIGHT ()
{
//...
//Trade_ChangeBuyMultiplier (...);
};
HookEngine (oCViewDialogTrade__OnTransferLeft, 10, "_HOOK_VIEWDIALOGTRADE_ONTRANSFERLEFT");
HookEngine (oCViewDialogTrade__OnTransferRight, 10, "_HOOK_VIEWDIALOGTRADE_ONTRANSFERRIGHT");
if you will hook this one instead - function will be called with each item transfer from chest to NPC (if you will transfer 3 items at once - function will be called 3 times)...
You, sir, are awesome! I tested a bit and there's actually more to it. Now we have the following cases:
1. "oCItemContainer__Remove_Item" covers BOTH transferring items from a dead NPC as well as from containers!
2. "oCItemContainer__TransferItem" covers item transfer from dead NPCs and from hero to containers.
Because I used the latter to apply weight to the hero in the first place I suddenly had double the weight for items I've taken from dead NPCs, that's how I found out. Now "oCItemContainer__TransferItem" I only use to cover moving items from hero to containers. Both together works like a charm.
Too bad though it doesn't seem to work for trading transactions as well, would've been a convenient coincidence.
Too bad though it doesn't seem to work for trading transactions as well, would've been a convenient coincidence.
edit: just realized weight should be multiplied by amount edit2: GenerationLost fixed amount calculation, editing in case anyone would not read whole discussion
Maybe a stupid idea - but you could potentially just recalculate total weight of all items when you finish trading - so you can call it from ZS_Talk_End or something:
Code:
FUNC INT NPC_Get_Inv_Category_Weight (var C_NPC slf, var int inv_category)
{
var int p;
var int itm_slot; itm_slot = 0;
var int total_weight; total_weight = 0;
var int amount;
//Loop
p = MEM_StackPos.position;
//Is there any item in itm_slot ?
amount = NPC_GetInvItemBySlot (slf, inv_category, itm_slot)
if (amount > 0)
{
//item is filled by above NPC_GetInvItemBySlot
total_weight += item.weight * amount;
itm_slot = itm_slot + 1;
MEM_StackPos.position = p;
};
return total_weight;
};
FUNC INT NPC_Get_Inv_Weight (var C_NPC slf)
{
var int total_weight; total_weight = 0;
total_weight += NPC_Get_Inv_Category_Weight (slf, INV_WEAPON);
total_weight += NPC_Get_Inv_Category_Weight (slf, INV_ARMOR);
total_weight += NPC_Get_Inv_Category_Weight (slf, INV_RUNE);
total_weight += NPC_Get_Inv_Category_Weight (slf, INV_MAGIC);
total_weight += NPC_Get_Inv_Category_Weight (slf, INV_FOOD);
total_weight += NPC_Get_Inv_Category_Weight (slf, INV_POTION);
total_weight += NPC_Get_Inv_Category_Weight (slf, INV_DOC);
total_weight += NPC_Get_Inv_Category_Weight (slf, INV_MISC);
return total_weight;
};
Geändert von F a w k e s (25.10.2018 um 21:45 Uhr)
Funny, I thought of the same But I figured it would be pretty expensive...?
I am not sure how it would affect performance only testing can tell
Or maybe you can call it only when you really buy/sell something. You could enter this weight re-calculation into oCViewDialogTrade__OnAccept hook, at the end of the code: https://forum.worldofplayers.de/foru...1#post25782129
I am not sure how it would affect performance only testing can tell
Or maybe you can call it only when you really buy/sell something. You could enter this weight re-calculation into oCViewDialogTrade__OnAccept hook, at the end of the code: https://forum.worldofplayers.de/foru...1#post25782129
I tried it out and it doesn't seem to affect peformance at all. I didn't test thoroughly but since there's the weight restriction, the inventory shouldn't be too full to affect performance anyway.
For that I hooked oCViewDialogTrade::OnExit already, that works just as well. Although I needed to adjust your code a little, concerning the item amount:
Code:
FUNC INT NPC_Get_Inv_Category_Weight (var C_NPC slf, var int inv_category)
{
var int p;
var int itm_slot; itm_slot = 0;
var int total_weight; total_weight = 0;
var int amount;
//Loop
p = MEM_StackPos.position;
//Is there any item in itm_slot ?
amount = NPC_GetInvItemBySlot (slf, inv_category, itm_slot); //this function properly returns item amount
if (amount > 0)
{
//item is filled by above NPC_GetInvItemBySlot
total_weight += item.weight * amount;
itm_slot = itm_slot + 1;
MEM_StackPos.position = p;
};
return total_weight;
};
//Gothic 1
//Tested with [Ikarus 1.2], [Lego 2.5.0]
//Class cannibalized from Ikarus 1.2 oCNPC class
class oCItemContainer {
[...]
Your class definition is a bit off. oCItemContainer is 160 Bytes:
Spoiler:(zum lesen bitte Text markieren)
Code:
class oCItemContainer {
var int vtbl; // 0x00
var int contents; // 0x04 zCListSort<oCItem>*
var int npc; // 0x08 oCNpc*
var string titleText; // 0x0C zSTRING
var int invMode; // 0x20 int
var int selectedItem; // 0x24 int
var int offset; // 0x28 int
var int maxSlotsCol; // 0x2C int
var int maxSlotsColScr; // 0x30 int
var int maxSlotsRow; // 0x34 int
var int maxSlotsRowScr; // 0x38 int
var int maxSlots; // 0x3C int
var int marginTop; // 0x40 int
var int marginLeft; // 0x44 int
var int frame; // 0x48 zBOOL
var int right; // 0x4C zBOOL
var int ownList; // 0x50 zBOOL
var int prepared; // 0x54 zBOOL
var int passive; // 0x58 zBOOL
var int TransferCount; // 0x5C zINT
var int viewTitle; // 0x60 zCView*
var int viewBack; // 0x64 zCView*
var int viewItem; // 0x68 zCView*
var int viewItemActive; // 0x6C zCView*
var int viewItemHightlighted; // 0x70 zCView*
var int viewItemActiveHighlighted; // 0x74 zCView*
var int viewItemInfo; // 0x78 zCView*
var int viewItemInfoItem; // 0x7C zCView*
var int textView; // 0x80 zCView*
var int viewArrowAtTop; // 0x84 zCView*
var int viewArrowAtBottom; // 0x88 zCView*
var int rndWorld; // 0x8C zCWorld*
var int posx; // 0x90 int
var int posy; // 0x94 int
var int m_bManipulateItemsDisabled; // 0x98 zBOOL
var int m_bCanTransferMoreThanOneItem; // 0x9C zBOOL
}; // 0xA0 sizeof_oCItemContainer
oCNpcInventory inherits from oCItemContainer and is 204 Bytes:
Spoiler:(zum lesen bitte Text markieren)
Code:
class oCNpcInventory {//oCItemContainer {
var int vtbl; // 0x00
var int _oCItemContainer_contents; // 0x04 zCListSort<oCItem>*
var int _oCItemContainer_npc; // 0x08 oCNpc*
var string _oCItemContainer_titleText; // 0x0C zSTRING
var int _oCItemContainer_invMode; // 0x20 int
var int _oCItemContainer_selectedItem; // 0x24 int
var int _oCItemContainer_offset; // 0x28 int
var int _oCItemContainer_maxSlotsCol; // 0x2C int
var int _oCItemContainer_maxSlotsColScr; // 0x30 int
var int _oCItemContainer_maxSlotsRow; // 0x34 int
var int _oCItemContainer_maxSlotsRowScr; // 0x38 int
var int _oCItemContainer_maxSlots; // 0x3C int
var int _oCItemContainer_marginTop; // 0x40 int
var int _oCItemContainer_marginLeft; // 0x44 int
var int _oCItemContainer_frame; // 0x48 zBOOL
var int _oCItemContainer_right; // 0x4C zBOOL
var int _oCItemContainer_ownList; // 0x50 zBOOL
var int _oCItemContainer_prepared; // 0x54 zBOOL
var int _oCItemContainer_passive; // 0x58 zBOOL
var int _oCItemContainer_TransferCount; // 0x5C zINT
var int _oCItemContainer_viewTitle; // 0x60 zCView*
var int _oCItemContainer_viewBack; // 0x64 zCView*
var int _oCItemContainer_viewItem; // 0x68 zCView*
var int _oCItemContainer_viewItemActive; // 0x6C zCView*
var int _oCItemContainer_viewItemHightlighted; // 0x70 zCView*
var int _oCItemContainer_viewItemActiveHighlighted; // 0x74 zCView*
var int _oCItemContainer_viewItemInfo; // 0x78 zCView*
var int _oCItemContainer_viewItemInfoItem; // 0x7C zCView*
var int _oCItemContainer_textView; // 0x80 zCView*
var int _oCItemContainer_viewArrowAtTop; // 0x84 zCView*
var int _oCItemContainer_viewArrowAtBottom; // 0x88 zCView*
var int _oCItemContainer_rndWorld; // 0x8C zCWorld*
var int _oCItemContainer_posx; // 0x90 int
var int _oCItemContainer_posy; // 0x94 int
var int _oCItemContainer_m_bManipulateItemsDisabled; // 0x98 zBOOL
var int _oCItemContainer_m_bCanTransferMoreThanOneItem; // 0x9C zBOOL
//}
var int owner; // 0xA0 oCNpc*
var int packAbility; // 0xA4 zBOOL
//zCListSort<oCItem> {
var int inventory_Compare; // 0xA8 int(_cdecl*)(oCItem*,oCItem*)
var int inventory_data; // 0xAC oCItem*
var int inventory_next; // 0xB0 zCListSort<oCItem>*
//}
var string packString; // 0xB4 zSTRING
var int maxSlots; // 0xC8 int
}; // 0xCC sizeof_oCNpcInventory
oCStealContainer (used for trading in Gothic 2!) is 164 Bytes and a correspondingly shorter "version" of oCNpcInventory and contains a (reduced) copy of the actual NPC's inventory. oCNpcContainer (164 Bytes) is yet another class that is used for looting dead NPCs (also a reduced copy of their inventory).
You already have some approaches for retrieving the inventory size.
Here is a list of actions to intercept, with engine addresses to hook/overwrite:
Player buys item from trader (oCViewDialogTrade::OnTransferRight)
Player transfers item from container (oCItemContainer::HandleEvent+249h)
Player transfers item from dead NPC (oCNpcContainer::HandleEvent+113h)
Player picks up item from world (oCNpc:: DoTakeVob)
Player gets item through script (handle in Daedalus)
/*
* En-/Disable key stroke registration of oCItemContainer and oCNpcContainer to prevent transferring items to the player
* inventory.
* Downside: As this is not a hook, there is no possibility to notify the player of the unsuccessful transfer.
*/
func void setInventoryToFull(var int yes){
const int SET = 0;
if (SET == yes){
return;
};
const int oCItemContainer_HandleEvent_actionKey = 7383177; //0x70A889
const int oCNpcContainer_HandleEvent_actionKey = 7387139; //0x70B803
if (yes){// Disable key registration
// Forbid transfer from oCItemContainer
MemoryProtectionOverride(oCItemContainer_HandleEvent_actionKey, 2);
MEM_WriteByte(oCItemContainer_HandleEvent_actionKey, /*EB*/ 235); // short jz beyond
MEM_WriteByte(oCItemContainer_HandleEvent_actionKey+1, /*36*/ 54);
// Forbid transfer from oCNpcContainer
MemoryProtectionOverride(oCNpcContainer_HandleEvent_actionKey, 2);
MEM_WriteByte(oCNpcContainer_HandleEvent_actionKey, /*EB*/ 235); // short jz beyond
MEM_WriteByte(oCNpcContainer_HandleEvent_actionKey+1, /*36*/ 54);
} else {// Restore default
MEM_WriteByte(oCItemContainer_HandleEvent_actionKey, /*8B*/ 139);
MEM_WriteByte(oCItemContainer_HandleEvent_actionKey+1, /*0D*/ 13);
MEM_WriteByte(oCNpcContainer_HandleEvent_actionKey, /*8B*/ 139);
MEM_WriteByte(oCNpcContainer_HandleEvent_actionKey+1, /*0D*/ 13);
};
SET = yes;
};
EDIT: I didn't see that you are talking about G1. So the addresses are off and my supposed "correction" of the oCItemContainer class might be as well. The script to block the key strokes might be useful to you, nevertheless.