Kann man irgendwie verhindern, dass der Spieler auch alle Items, die das Flag 'ITEM_MISSION' haben, droppen kann? Ich habe hier im Forum zwar etwas gefunden, wie man bestimmte Items wieder aus der Welt entfernen und dem Spieler wieder geben kann, aber das ist eigentlich nicht das, was ich möchte. Ich habe mir das eher so vorgestellt, dass einfach gar nichts passiert, wenn der Spieler versucht ein Item mit diesem Flag zu droppen. Also auch keine Animation etc.
Probier es mal damit. Die Funktion CanDropItem kannst du deinen Belieben anpassen. Hier beispielhaft für alle Missionsitems. Das SVM und den Print kannst du auch herausnehmen.
Spoiler:(zum lesen bitte Text markieren)
Code:
/*
* CanDropItem
* Source: https://forum.worldofplayers.de/forum/threads/?p=25716520
*
* Dictate which items may be dropped
*
* - Requires Ikarus, LeGo (HookEngine)
* - Compatible with Gothic 1 and Gothic 2
*
* Instructions
* - Initialize from Init_Global with
* CanDropItem_Init()
*/
func int CanDropItem(var C_Item itm){// Do not drop mission items
if (itm.flags & ITEM_MISSION){Print("I should probably keep this");
B_Say(hero, NULL, "$DONTKNOW"); //Hmm ... No ...
return FALSE;
};
// ... more ...
return TRUE;
};
func void CanDropItem_Init(){
const int oCNpcInventory__HandleEvent_drop_G1 = 6743982; //0x66E7AE
const int oCNpcInventory__HandleEvent_drop_G2 = 7398710; //0x70E536
HookEngineF(+MEMINT_SwitchG1G2(oCNpcInventory__HandleEvent_drop_G1,
oCNpcInventory__HandleEvent_drop_G2), 5, _CanDropItem);
};
func void _CanDropItem(){
const int oCItemContainer__GetSelectedItem_G1 = 6721968; //0x6691B0
const int oCItemContainer__GetSelectedItem_G2 = 7377600; //0x7092C0
var int selectedItemPtr;
var int container; container = ECX; // oCNpcInventory*
// Get selected item from inventory
const int call = 0;
if (CALL_Begin(call)){
CALL_PutRetValTo(_@(selectedItemPtr));
CALL__thiscall(_@(container), MEMINT_SwitchG1G2(oCItemContainer__GetSelectedItem_G1,
oCItemContainer__GetSelectedItem_G2));
call = CALL_End();
};
// Check selection
if (selectedItemPtr){
var C_Item itm; itm = _^(selectedItemPtr);
if (!CanDropItem(itm)){// The call to oCItemContainer::GetSelectedItem immediately returns zero, if oCItemContainer.contents == 0
const int contents = 0;
ECX = _@(contents)-4; // Offset of oCNpcInventory.contents is 0x04
};
};
};
Geändert von mud-freak (08.11.2020 um 10:17 Uhr)
Grund: Link im Code hinzugefügt
Probier es mal damit. Die Funktion CanDropItem kannst du deinen Belieben anpassen. Hier beispielhaft für alle Missionsitems. Das SVM und den Print kannst du auch herausnehmen.
Spoiler:(zum lesen bitte Text markieren)
Code:
/*
* Dictate which items may be dropped
*
* Initialize with CanDropItem_Init() in Init_Global
* Compatible with Gothic 1 and Gothic 2
*/
func int CanDropItem(var C_Item itm){// Do not drop mission items
if (itm.flags & ITEM_MISSION){Print("I should probably keep this");
B_Say(hero, NULL, "$DONTKNOW"); //Hmm ... No ...
return FALSE;
};
// ... more ...
return TRUE;
};
func void CanDropItem_Init(){
const int oCNpcInventory__HandleEvent_drop_G1 = 6743982; //0x66E7AE
const int oCNpcInventory__HandleEvent_drop_G2 = 7398710; //0x70E536
HookEngineF(+MEMINT_SwitchG1G2(oCNpcInventory__HandleEvent_drop_G1,
oCNpcInventory__HandleEvent_drop_G2), 5, _CanDropItem);
};
func void _CanDropItem(){
const int oCItemContainer__GetSelectedItem_G1 = 6721968; //0x6691B0
const int oCItemContainer__GetSelectedItem_G2 = 7377600; //0x7092C0
var int selectedItemPtr;
var int container; container = ECX; // oCNpcInventory*
// Get selected item from inventory
const int call = 0;
if (CALL_Begin(call)){
CALL_PutRetValTo(_@(selectedItemPtr));
CALL__thiscall(_@(container), MEMINT_SwitchG1G2(oCItemContainer__GetSelectedItem_G1,
oCItemContainer__GetSelectedItem_G2));
call = CALL_End();
};
// Check selection
if (selectedItemPtr){
var C_Item itm; itm = _^(selectedItemPtr);
if (!CanDropItem(itm)){// The call to oCItemContainer::GetSelectedItem immediately returns zero, if oCItemContainer.contents == 0
const int contents = 0;
ECX = _@(contents)-4; // Offset of oCNpcInventory.contents is 0x04
};
};
};
sehr cooles script. Könnte man das auch noch in soweit erweitern, dass man diese besagten items mit dem flag Mission nicht beim händler verkaufen kann?
das übersteigt mein können :-D
da müsste mir schon jemand die lösung genau aufschreiben
Das ist schon genau aufgeschrieben. Anstatt auf ItAr_Pal_H müsstest du nur auf das ITEM_MISSION-Flag überprüfen. Ich habe das Skript mal ein bisschen umformuliert (inhaltlich unverändert) im Stil von dem Skript oben, vielleicht ist es dann etwas offensichtlicher:
Spoiler:(zum lesen bitte Text markieren)
Code:
/*
* CanSellItem
*
* Source: https://forum.worldofplayers.de/forum/threads/?p=25716763
* Base on work by Frank-95: https://forum.worldofplayers.de/forum/threads/?p=24695536
*
* Dictate which items may be sold
*
* - Requires Ikarus, LeGo (HookEngine)
* - Compatible with Gothic 2 only
*
* Instructions
* - Initialize from Init_Global with
* CheckSellingItems_Init()
*/
func int CanSellItem(var C_Item itm){// Mission items cannot be sold
if (itm.flags & ITEM_MISSION){Print("I should probably keep this");
B_Say(hero, NULL, "$DONTKNOW"); //Hmm ... No ...
return FALSE;
};
// .. more ..
return TRUE;
};
func void CheckSellingItems_Init(){
const int oCViewDialogTrade__OnTransferLeft = 6862912; //0x68B840
HookEngineF(oCViewDialogTrade__OnTransferLeft, 6, CheckSellingItems);
MemoryProtectionOverride(oCViewDialogTrade__OnTransferLeft + 6, 4);
};
func void CheckSellingItems(){
const int oCViewDialogTrade__OnTransferLeft = 6862912; //0x68B840
var oCNpc her; her = Hlp_GetNpc(hero);
var C_Item itm;
itm = _^(List_GetS(her.inventory2_oCItemContainer_contents, her.inventory2_oCItemContainer_selectedItem + 2));
if (CanSellItem(itm)){
MEM_WriteByte(oCViewDialogTrade__OnTransferLeft + 6, 106); // push
} else {
MEM_WriteByte(oCViewDialogTrade__OnTransferLeft + 6, 195); // retn
};
};
Geändert von mud-freak (08.11.2020 um 10:23 Uhr)
Grund: Link im Code hinzugefügt
Probier es mal damit. Die Funktion CanDropItem kannst du deinen Belieben anpassen. Hier beispielhaft für alle Missionsitems. Das SVM und den Print kannst du auch herausnehmen.
Spoiler:(zum lesen bitte Text markieren)
Code:
/*
* Dictate which items may be dropped
*
* Initialize with CanDropItem_Init() in Init_Global
* Compatible with Gothic 1 and Gothic 2
*/
func int CanDropItem(var C_Item itm){// Do not drop mission items
if (itm.flags & ITEM_MISSION){Print("I should probably keep this");
B_Say(hero, NULL, "$DONTKNOW"); //Hmm ... No ...
return FALSE;
};
// ... more ...
return TRUE;
};
func void CanDropItem_Init(){
const int oCNpcInventory__HandleEvent_drop_G1 = 6743982; //0x66E7AE
const int oCNpcInventory__HandleEvent_drop_G2 = 7398710; //0x70E536
HookEngineF(+MEMINT_SwitchG1G2(oCNpcInventory__HandleEvent_drop_G1,
oCNpcInventory__HandleEvent_drop_G2), 5, _CanDropItem);
};
func void _CanDropItem(){
const int oCItemContainer__GetSelectedItem_G1 = 6721968; //0x6691B0
const int oCItemContainer__GetSelectedItem_G2 = 7377600; //0x7092C0
var int selectedItemPtr;
var int container; container = ECX; // oCNpcInventory*
// Get selected item from inventory
const int call = 0;
if (CALL_Begin(call)){
CALL_PutRetValTo(_@(selectedItemPtr));
CALL__thiscall(_@(container), MEMINT_SwitchG1G2(oCItemContainer__GetSelectedItem_G1,
oCItemContainer__GetSelectedItem_G2));
call = CALL_End();
};
// Check selection
if (selectedItemPtr){
var C_Item itm; itm = _^(selectedItemPtr);
if (!CanDropItem(itm)){// The call to oCItemContainer::GetSelectedItem immediately returns zero, if oCItemContainer.contents == 0
const int contents = 0;
ECX = _@(contents)-4; // Offset of oCNpcInventory.contents is 0x04
};
};
};
Zitat von mud-freak
Das ist schon genau aufgeschrieben. Anstatt auf ItAr_Pal_H müsstest du nur auf das ITEM_MISSION-Flag überprüfen. Ich habe das Skript mal ein bisschen umformuliert (inhaltlich unverändert) im Stil von dem Skript oben, vielleicht ist es dann etwas offensichtlicher:
Spoiler:(zum lesen bitte Text markieren)
Code:
/*
* Dictate which items may be sold
* Written by Frank-95 https://forum.worldofplayers.de/forum/threads/?p=24695536
*
* Initialize with CheckSellingItems_Init() in Init_Global
* Compatible with Gothic 2 only
*/
func int CanSellItem(var C_Item itm){// Mission items cannot be sold
if (itm.flags & ITEM_MISSION){Print("I should probably keep this");
B_Say(hero, NULL, "$DONTKNOW"); //Hmm ... No ...
return FALSE;
};
// .. more ..
return TRUE;
};
func void CheckSellingItems_Init(){
const int oCViewDialogTrade__OnTransferLeft = 6862912; //0x68B840
HookEngineF(oCViewDialogTrade__OnTransferLeft, 6, CheckSellingItems);
MemoryProtectionOverride(oCViewDialogTrade__OnTransferLeft + 6, 4);
};
func void CheckSellingItems(){
const int oCViewDialogTrade__OnTransferLeft = 6862912; //0x68B840
var oCNpc her; her = Hlp_GetNpc(hero);
var C_Item itm;
itm = _^(List_GetS(her.inventory2_oCItemContainer_contents, her.inventory2_oCItemContainer_selectedItem + 2));
if (CanSellItem(itm)){
MEM_WriteByte(oCViewDialogTrade__OnTransferLeft + 6, 106); // push
} else {
MEM_WriteByte(oCViewDialogTrade__OnTransferLeft + 6, 195); // retn
};
};
Sehr schön, so eine Funktion, die das verkaufen von Quest Items bei Händlern verhindert, wollte ich auch noch irgendwann mal mit einbauen. So kann ich jetzt aber gleich beides machen. Vielen Dank dafür!
Mal 'ne Frage hierzu: Könnte man das Skript eigentlich auch so erweitern, dass der Spieler Questitems - also Items mit dem ITEM_MISSION Flag - auch gar nicht mehr in oCMobContainer (also in Truhen zum Beispiel) legen kann? Ich bin mir zwar relativ sicher, dass das möglich ist - nur wie? Wie müsste das Skript dann aussehen?
Hier ist auch ein Skript dafür. Wieder gibt es eine Funktion (CanTransferItemToContainer), die nach belieben für Items angepasst werden kann.
Ich habe es kaum getestet (unter Gothic 1 gar nicht). Es sollte also noch ausgiebig getestet werden. Ich kann nicht garantieren, dass ich keine Sonderfälle übersehen habe. Die Prints "Should never be reached" sollten nie ausgegeben werden. Wenn das doch der Fall ist, habe ich einen Fehler gemacht.
Spoiler:(zum lesen bitte Text markieren)
Code:
/*
* CanTransferItemToContainer
* Source: https://forum.worldofplayers.de/forum/threads/?p=25961919
*
* Dictate which items may be transfered to containers
*
* - Requires Ikarus, LeGo (HookEngine)
* - Compatible with Gothic 1 and Gothic 2
*
* Instructions
* - Initialize from Init_Global with
* CanTransferItemToContainer_Init()
*/
func int CanTransferItemToContainer(var C_Item itm){// Mission items cannot be transfered to containers
if (itm.flags & ITEM_MISSION){Print("I should probably keep this");
B_Say(hero, NULL, "$DONTKNOW"); //Hmm ... No ...
return FALSE;
};
// .. more ..
return TRUE;
};
func void CanTransferItemToContainer_Init(){
const int oCItemContainer__TransferItem_G1 = 6723456; //0x669780
const int oCItemContainer__TransferItem_G2 = 7380800; //0x709F40
HookEngineF(+MEMINT_SwitchG1G2(oCItemContainer__TransferItem_G1,
oCItemContainer__TransferItem_G2), 5, _CanTransferItemToContainer);
};
func void _CanTransferItemToContainer(){// Get transfer direction (there should only be one)
var int direction; direction = MEM_ReadInt(ESP+4);
if (direction >= 0){// Remove this if I'm wrong
MEM_Info("Should never be reached");
MEM_Warn("Transfer FROM container to inventory should never be handled this way");
return;
};
// Get selected item from inventory
var int container; container = ECX;
const int oCItemContainer__GetSelectedItem_G1 = 6721968; //0x6691B0
const int oCItemContainer__GetSelectedItem_G2 = 7377600; //0x7092C0
const int call = 0;
if (CALL_Begin(call)){
CALL_PutRetValTo(_@(selectedItemPtr));
CALL__thiscall(_@(container), MEMINT_SwitchG1G2(oCItemContainer__GetSelectedItem_G1,
oCItemContainer__GetSelectedItem_G2));
call = CALL_End();
};
// Prepare overwriting opcode
const int oCItemContainer__TransferItem_jz_G1 = 6723473; //0x669791
const int oCItemContainer__TransferItem_jz_G2 = 7380817; //0x709F51
var int modAddr; modAddr = MEMINT_SwitchG1G2(oCItemContainer__TransferItem_jz_G1,
oCItemContainer__TransferItem_jz_G2);
const int once = 0;
if (!once){
MemoryProtectionOverride(modAddr, 4);
once = 1;
};
// Check selection
var int selectedItemPtr;
if (selectedItemPtr){
var C_Item itm; itm = _^(selectedItemPtr);
if (!CanTransferItemToContainer(itm)){// Set EAX to non-zero
MEM_WriteByte(modAddr, /*B0*/ 176); // mov eax, BYTE 1
MEM_WriteByte(modAddr+1, 1);
// Force jump
MEM_WriteByte(modAddr+2, /*EB*/ 235); // short jmp +0x11
MEM_WriteByte(modAddr+3, /*11*/ 17);
return;
};
};
// Restore default
MEM_WriteByte(modAddr, /*3B*/ 59); // cmp eax, edi
MEM_WriteByte(modAddr+1, /*C7*/ 199);
MEM_WriteByte(modAddr+2, /*74*/ 116); // short jmp +0x19
MEM_WriteByte(modAddr+3, /*19*/ 25);
};
Geändert von mud-freak (08.11.2020 um 10:17 Uhr)
Grund: Link im Code hinzugefügt
Hier ist auch ein Skript dafür. Wieder gibt es eine Funktion (CanTransferItemToContainer), die nach belieben für Items angepasst werden kann.
Ich habe es kaum getestet (unter Gothic 1 gar nicht). Es sollte also noch ausgiebig getestet werden. Ich kann nicht garantieren, dass ich keine Sonderfälle übersehen habe. Die Prints "Should never be reached" sollten nie ausgegeben werden. Wenn das doch der Fall ist, habe ich einen Fehler gemacht.
Spoiler:(zum lesen bitte Text markieren)
Code:
/*
* Dictate which items may be transfered to containers
*
* Initialize with CanTransferItemToContainer_Init() in Init_Global
* Compatible with Gothic 1 and Gothic 2
*/
func int CanTransferItemToContainer(var C_Item itm){// Mission items cannot be transfered to containers
if (itm.flags & ITEM_MISSION){Print("I should probably keep this");
B_Say(hero, NULL, "$DONTKNOW"); //Hmm ... No ...
return FALSE;
};
// .. more ..
return TRUE;
};
func void CanTransferItemToContainer_Init(){
const int oCItemContainer__TransferItem_G1 = 6723456; //0x669780
const int oCItemContainer__TransferItem_G2 = 7380800; //0x709F40
HookEngineF(+MEMINT_SwitchG1G2(oCItemContainer__TransferItem_G1,
oCItemContainer__TransferItem_G2), 5, _CanTransferItemToContainer);
};
func void _CanTransferItemToContainer(){// Get transfer direction (there should only be one)
var int direction; direction = MEM_ReadInt(ESP+4);
if (direction >= 0){// Remove this if I'm wrong
MEM_Info("Should never be reached");
MEM_Warn("Transfer FROM container to inventory should never be handled this way");
return;
};
// Get selected item from inventory
var int container; container = ECX;
const int oCItemContainer__GetSelectedItem_G1 = 6721968; //0x6691B0
const int oCItemContainer__GetSelectedItem_G2 = 7377600; //0x7092C0
const int call = 0;
if (CALL_Begin(call)){
CALL_PutRetValTo(_at(selectedItemPtr));
CALL__thiscall(_at(container), MEMINT_SwitchG1G2(oCItemContainer__GetSelectedItem_G1,
oCItemContainer__GetSelectedItem_G2));
call = CALL_End();
};
// Prepare overwriting opcode
const int oCItemContainer__TransferItem_jz_G1 = 6723473; //0x669791
const int oCItemContainer__TransferItem_jz_G2 = 7380817; //0x709F51
var int modAddr; modAddr = MEMINT_SwitchG1G2(oCItemContainer__TransferItem_jz_G1,
oCItemContainer__TransferItem_jz_G2);
const int once = 0;
if (!once){
MemoryProtectionOverride(modAddr, 4);
once = 1;
};
// Check selection
var int selectedItemPtr;
if (selectedItemPtr){
var C_Item itm; itm = _hat(selectedItemPtr);
if (!CanTransferItemToContainer(itm)){// Set EAX to non-zero
MEM_WriteByte(modAddr, /*B0*/ 176); // mov eax, BYTE 1
MEM_WriteByte(modAddr+1, 1);
// Force jump
MEM_WriteByte(modAddr+2, /*EB*/ 235); // short jmp +0x11
MEM_WriteByte(modAddr+3, /*11*/ 17);
return;
};
};
// Restore default
MEM_WriteByte(modAddr, /*3B*/ 59); // cmp eax, edi
MEM_WriteByte(modAddr+1, /*C7*/ 199);
MEM_WriteByte(modAddr+2, /*74*/ 116); // short jmp +0x19
MEM_WriteByte(modAddr+3, /*19*/ 25);
};
Vielen Dank.
Ich musste es allerdings noch etwas anpassen, damit es funktioniert hat:
Code:
/*
* Dictate which items may be transfered to containers
*
* Initialize with CanTransferItemToContainer_Init() in Init_Global
* Compatible with Gothic 1 and Gothic 2
*/
func int CanTransferItemToContainer(var C_Item itm) {
// Mission items cannot be transfered to containers
if (itm.flags & ITEM_MISSION)
&& (!Hlp_IsItem (itm, ITMI_OLDCOIN)) {
Print("Das brauche ich noch!");
B_Say_Overlay(hero, self, "$DONTKNOW"); //Hmm ... No ...
return FALSE;
};
// .. more ..
return TRUE;
};
func void CanTransferItemToContainer_Init() {
const int oCItemContainer__TransferItem_G1 = 6723456; //0x669780
const int oCItemContainer__TransferItem_G2 = 7380800; //0x709F40
HookEngineF(+MEMINT_SwitchG1G2(oCItemContainer__TransferItem_G1,
oCItemContainer__TransferItem_G2), 5, _CanTransferItemToContainer);
};
func void _CanTransferItemToContainer() {
// Get transfer direction (there should only be one)
var int direction; direction = MEM_ReadInt(ESP+4);
if (direction >= 0) {
// Remove this if I'm wrong
MEM_Info("Should never be reached");
MEM_Warn("Transfer FROM container to inventory should never be handled this way");
return;
};
// Get selected item from inventory
var int container; container = ECX;
const int oCItemContainer__GetSelectedItem_G1 = 6721968; //0x6691B0
const int oCItemContainer__GetSelectedItem_G2 = 7377600; //0x7092C0
const int call = 0;
if (CALL_Begin(call)) {
CALL_PutRetValTo(_@(selectedItemPtr));
CALL__thiscall(_@(container), MEMINT_SwitchG1G2(oCItemContainer__GetSelectedItem_G1,
oCItemContainer__GetSelectedItem_G2));
call = CALL_End();
};
// Prepare overwriting opcode
const int oCItemContainer__TransferItem_jz_G1 = 6723473; //0x669791
const int oCItemContainer__TransferItem_jz_G2 = 7380817; //0x709F51
var int modAddr; modAddr = MEMINT_SwitchG1G2(oCItemContainer__TransferItem_jz_G1,
oCItemContainer__TransferItem_jz_G2);
const int once = 0;
if (!once) {
MemoryProtectionOverride(modAddr, 4);
once = 1;
};
// Check selection
var int selectedItemPtr;
if (selectedItemPtr) {
var C_Item itm; itm = _^(selectedItemPtr);
if (!CanTransferItemToContainer(itm)) {
// Set EAX to non-zero
MEM_WriteByte(modAddr, /*B0*/ 176); // mov eax, BYTE 1
MEM_WriteByte(modAddr+1, 1);
// Force jump
MEM_WriteByte(modAddr+2, /*EB*/ 235); // short jmp +0x11
MEM_WriteByte(modAddr+3, /*11*/ 17);
return;
};
};
// Restore default
MEM_WriteByte(modAddr, /*3B*/ 59); // cmp eax, edi
MEM_WriteByte(modAddr+1, /*C7*/ 199);
MEM_WriteByte(modAddr+2, /*74*/ 116); // short jmp +0x19
MEM_WriteByte(modAddr+3, /*19*/ 25);
};
_at musste ich durch _@ ersetzen und _hat durch _^.
_at musste ich durch _@ ersetzen und _hat durch _^.
Ah richtig, das habe vergessen zurück zu ersetzen. Farbeimer (Sytanxhighlighter) mag die Zeichen nicht und da ändere ich sie immer vor und nach dem Anwenden.
Ich habe das Skript oben im Post dahingehend korrigiert.