diff --git a/artifacts/config_files/npcarmor.ini b/artifacts/config_files/npcarmor.ini index 90b20b7c..085658ba 100644 --- a/artifacts/config_files/npcarmor.ini +++ b/artifacts/config_files/npcarmor.ini @@ -72,7 +72,7 @@ Combat=16777350 ; Cat Jules [5] -PID=16777734 +PID=16777720 WeaponAnims=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 Default=16777353 Leather=16777347 @@ -82,7 +82,7 @@ Combat=16777226 ; Kitsune [6] -PID=16777724 +PID=16777718 WeaponAnims=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 Default=16777222 Leather=16777221 diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index c932355b..9e601a0d 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -431,7 +431,7 @@ int ret1 - Override setting (-1 - use engine handler, any other value - prev HOOK_INVENWIELD (hs_invenwield.int) -Runs before wielding or unwielding an armor or a weapon by a critter (except when using inventory by PC). +Runs before causing a critter or the player to wield/unwield an armor or a weapon (except when using the inventory by PC). An example usage would be to change critter art depending on armor being used or to dynamically customize weapon animations. NOTE: when replacing a previously wielded armor or weapon, the unwielding hook will not be executed. If you need to rely on this, try checking if armor/weapon is already equipped when wielding hook is executed. @@ -440,6 +440,7 @@ Critter arg1 - critter Obj arg2 - item being wielded or unwielded (weapon/armor) int arg3 - slot (INVEN_TYPE_*) int arg4 - 1 when wielding, 0 when unwielding +int arg5 - 1 when removing an equipped item from inventory, 0 otherwise int ret1 - overrides hard-coded handler (-1 - use engine handler, any other value - override) - NOT RECOMMENDED diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 8d7992b9..7766d97d 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -1,5 +1,6 @@ #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" +#include "HookScripts\InventoryHs.h" #include "Drugs.h" #include "LoadGameHook.h" #include "ScriptExtender.h" @@ -393,7 +394,7 @@ nextArmor: call fo::funcoffs::inven_worn_; test eax, eax; jz noArmor; - and byte ptr [eax + flags+3], ~Worn >> 24; // Unset the flag of equipped armor + and byte ptr [eax + flags+3], ~Worn >> 24; // Unset flag of equipped armor jmp nextArmor; noArmor: mov eax, esi; @@ -431,11 +432,15 @@ skip: jz dudeFix; test eax, eax; jz end; + // fix for party member + call InvenUnwield_HookMove; // run HOOK_INVENWIELD before moving item + push ebx; mov ecx, edx; xor ebx, ebx; // new armor xchg eax, edx; // set: eax - source, edx - removed armor - call fo::funcoffs::adjust_ac_; // fix for party member + call fo::funcoffs::adjust_ac_; mov edx, ecx; + pop ebx; xor eax, eax; end: retn; // must be eax = 0 @@ -452,6 +457,29 @@ equipped: } } +static void __declspec(naked) obj_drop_hook() { + __asm { + test byte ptr [edx + flags+3], (Worn | Right_Hand | Left_Hand) >> 24; + jz skipHook; + call InvenUnwield_HookDrop; // run HOOK_INVENWIELD before dropping item +skipHook: + test byte ptr [edx + flags+3], Worn >> 24; + jnz fixArmorStat; + jmp fo::funcoffs::obj_remove_from_inven_; +fixArmorStat: + call fo::funcoffs::isPartyMember_; // and dude + test eax, eax; + jz skip; + mov eax, ecx; + xor ebx, ebx; // new armor + call fo::funcoffs::adjust_ac_; // eax - source, edx - removed armor + mov edx, esi; +skip: + mov eax, ecx; + jmp fo::funcoffs::obj_remove_from_inven_; + } +} + static void __declspec(naked) partyMemberIncLevels_hook() { __asm { mov ebx, eax; // party member pointer @@ -2438,6 +2466,8 @@ void BugFixes::init() HookCall(0x45C49A, op_move_obj_inven_to_obj_hook); SafeWrite16(0x45C496, 0x9090); SafeWrite8(0x45C4A3, 0x75); // jmp > jnz + // Fix for drop_obj function + HookCall(0x49B965, obj_drop_hook); dlogr(" Done", DL_INIT); //} diff --git a/sfall/Modules/HookScripts/InventoryHs.cpp b/sfall/Modules/HookScripts/InventoryHs.cpp index 6a54abe9..ef709cfb 100644 --- a/sfall/Modules/HookScripts/InventoryHs.cpp +++ b/sfall/Modules/HookScripts/InventoryHs.cpp @@ -328,21 +328,48 @@ skip: } } -/* Common InvenWield hook */ -static bool InvenWieldHook_Script(int flag) { - argCount = 4; - args[3] = flag; // invenwield flag +/* Common InvenWield script hooks */ +static long __fastcall InvenWieldHook_Script(fo::GameObject* critter, fo::GameObject* item, long slot, long isWield, long isRemove) { + if (!isWield) { + // for the critter, the right slot is always the active slot + if (slot == fo::INVEN_TYPE_LEFT_HAND && critter != fo::var::obj_dude) return 1; + // check the current active slot for the player + if (slot != fo::INVEN_TYPE_WORN && critter == fo::var::obj_dude) { + long _slot = (slot != fo::INVEN_TYPE_LEFT_HAND); + if (_slot != fo::var::itemCurrentItem) return 1; // item in non-active slot + } + } + BeginHook(); + args[0] = (DWORD)critter; + args[1] = (DWORD)item; + args[2] = slot; + args[3] = isWield; // unwield/wield event + args[4] = isRemove; + + argCount = 5; + RunHookScript(HOOK_INVENWIELD); + + long result = (cRet == 0 || rets[0] == -1); + EndHook(); + + return result; // 1 - use engine handler +} + +static __declspec(noinline) bool InvenWieldHook_ScriptPart(long isWield, long isRemove = 0) { + args[3] = isWield; // unwield/wield event + args[4] = isRemove; + + argCount = 5; RunHookScript(HOOK_INVENWIELD); bool result = (cRet == 0 || rets[0] == -1); EndHook(); - return result; // True - use engine handler + return result; // true - use engine handler } static void __declspec(naked) InvenWieldFuncHook() { - using namespace fo; __asm { HookBegin; mov args[0], eax; // critter @@ -350,19 +377,17 @@ static void __declspec(naked) InvenWieldFuncHook() { mov args[8], ebx; // slot pushad; } - // right hand slot? - if (args[2] != INVEN_TYPE_RIGHT_HAND && GetItemType((GameObject*)args[1]) != item_type_armor) { - args[2] = INVEN_TYPE_LEFT_HAND; + if (args[2] != fo::INVEN_TYPE_RIGHT_HAND && fo::GetItemType((fo::GameObject*)args[1]) != fo::item_type_armor) { + args[2] = fo::INVEN_TYPE_LEFT_HAND; } - - InvenWieldHook_Script(1); // wield flag + InvenWieldHook_ScriptPart(1); // wield event __asm { test al, al; popad; jz skip; - jmp funcoffs::invenWieldFunc_; + jmp fo::funcoffs::invenWieldFunc_; skip: mov eax, -1; retn; @@ -373,18 +398,18 @@ skip: static void __declspec(naked) InvenUnwieldFuncHook() { __asm { HookBegin; - mov args[0], eax; // critter - mov args[8], edx; // slot + mov args[0], eax; // critter + mov args[8], edx; // slot pushad; } - // set slot if (args[2] == 0) { // left hand slot? args[2] = fo::INVEN_TYPE_LEFT_HAND; } - args[1] = (DWORD)fo::GetItemPtrSlot((fo::GameObject*)args[0], (fo::InvenType)args[2]); // get item + // get item + args[1] = (DWORD)fo::GetItemPtrSlot((fo::GameObject*)args[0], (fo::InvenType)args[2]); - InvenWieldHook_Script(0); // unwield flag + InvenWieldHook_ScriptPart(0); // unwield event __asm { test al, al; @@ -398,14 +423,14 @@ skip: } static void __declspec(naked) CorrectFidForRemovedItemHook() { + using namespace fo::ObjectFlag; __asm { HookBegin; mov args[0], eax; // critter mov args[4], edx; // item mov args[8], ebx; // item flag - pushad; + pushadc; } - // set slot if (args[2] & fo::ObjectFlag::Right_Hand) { // right hand slot args[2] = fo::INVEN_TYPE_RIGHT_HAND; @@ -414,21 +439,135 @@ static void __declspec(naked) CorrectFidForRemovedItemHook() { } else { args[2] = fo::INVEN_TYPE_WORN; // armor slot } - - InvenWieldHook_Script(0); // unwield flag (armor by default) - + InvenWieldHook_ScriptPart(0, 1); // unwield event (armor by default) + // engine handler is not overridden __asm { - test al, al; - popad; - jz skip; + popadc; jmp fo::funcoffs::correctFidForRemovedItem_; + } +} + +static void __declspec(naked) item_drop_all_hack() { + using namespace fo; + using namespace ObjectFlag; + __asm { + push eax; + push 1; // remove event + push 0; // unwield event + mov ecx, INVEN_TYPE_LEFT_HAND; + test ah, Left_Hand >> 24; + jnz skip; + test ah, Worn >> 24; + setz cl; // set INVEN_TYPE_WORN or INVEN_TYPE_RIGHT_HAND skip: - mov eax, -1; + push ecx; // slot + mov edx, esi; // item + mov ecx, edi; // critter + call InvenWieldHook_Script; + mov [esp + 0x40 - 0x2C + 8], eax; // itemIsEquipped (hook return result) + pop eax; retn; } } -void AdjustFidHook(DWORD vanillaFid) { +static bool hookInvenWieldIsInject = false; + +// called from bugfixes for obj_drop_ +void __declspec(naked) InvenUnwield_HookDrop() { // ecx - critter, edx - item + using namespace fo; + using namespace Fields; + using namespace ObjectFlag; + __asm { + cmp hookInvenWieldIsInject, 1; + je runHook; + retn; +runHook: + pushadc; + mov eax, INVEN_TYPE_LEFT_HAND; + test byte ptr [edx + flags + 3], Left_Hand >> 24; + jnz isLeft; + test byte ptr [edx + flags + 3], Worn >> 24; + setz al; // set INVEN_TYPE_WORN or INVEN_TYPE_RIGHT_HAND +isLeft: + push 1; // remove event + push 0; // unwield event + push eax; // slot + call InvenWieldHook_Script; // ecx - critter, edx - item + // engine handler is not overridden + popadc; + retn; + } +} + +// called from bugfixes for op_move_obj_inven_to_obj_ +void __declspec(naked) InvenUnwield_HookMove() { // eax - item, edx - critter + __asm { + cmp hookInvenWieldIsInject, 1; + je runHook; + retn; +runHook: + pushadc; + mov ecx, edx; + mov edx, eax; + push 1; // remove event + xor eax, eax; + push eax; // unwield event + push eax; // slot + call InvenWieldHook_Script; // ecx - critter, edx - item + // engine handler is not overridden + popadc; + retn; + } +} + +// called when unwelding dude weapon and armor +static void __declspec(naked) op_move_obj_inven_to_obj_hook() { + using namespace fo; + using namespace ObjectFlag; + __asm { + cmp eax, ds:[FO_VAR_obj_dude]; + je runHook; + jmp fo::funcoffs::item_move_all_; +runHook: + push eax; + push edx; + mov ecx, eax; // keep source + mov edx, ds:[FO_VAR_itemCurrentItem]; // get player's active slot + test edx, edx; + jz left; + call fo::funcoffs::inven_right_hand_; + jmp skip; +left: + call fo::funcoffs::inven_left_hand_; +skip: + test eax, eax; + jz noWeapon; + push 1; // remove event + push 0; // unwield event + mov ebx, INVEN_TYPE_LEFT_HAND; + sub ebx, edx; + push ebx; // slot: INVEN_TYPE_LEFT_HAND or INVEN_TYPE_RIGHT_HAND + mov edx, eax; // weapon + call InvenWieldHook_Script; // ecx - source + // engine handler is not overridden +noweapon: + mov edx, [esp + 0x30 - 0x20 + 12]; // armor + test edx, edx; + jz noArmor; + xor eax, eax; + push 1; // remove event + push eax; // unwield event + push eax; // slot: INVEN_TYPE_WORN + call InvenWieldHook_Script; // ecx - source + // engine handler is not overridden +noArmor: + pop edx; + pop eax; + jmp fo::funcoffs::item_move_all_; + } +} + +static void AdjustFidHook(DWORD vanillaFid) { if (!HookScripts::HookHasScript(HOOK_ADJUSTFID)) return; BeginHook(); @@ -474,7 +613,7 @@ void Inject_InventoryMoveHook() { MakeCall(0x49B660, PickupObjectHack); SafeWrite32(0x49B665, 0x850FD285); // test edx, edx SafeWrite32(0x49B669, 0xC2); // jnz 0x49B72F - SafeWrite8(0x49B66E, 0xFE); // cmp edi > cmp esi + SafeWrite8(0x49B66E, 0xFE); // cmp edi > cmp esi HookCall(0x471457, InvenPickupHook); } @@ -493,6 +632,10 @@ void Inject_InvenWieldHook() { 0x45680C, // op_rm_obj_from_inven_ 0x45C4EA // op_move_obj_inven_to_obj_ }); + HookCall(0x45C4F6, op_move_obj_inven_to_obj_hook); + MakeCall(0x4778AF, item_drop_all_hack, 3); + + hookInvenWieldIsInject = true; } void InitInventoryHookScripts() { diff --git a/sfall/Modules/HookScripts/InventoryHs.h b/sfall/Modules/HookScripts/InventoryHs.h index 832f5a78..6216851c 100644 --- a/sfall/Modules/HookScripts/InventoryHs.h +++ b/sfall/Modules/HookScripts/InventoryHs.h @@ -13,4 +13,7 @@ void Inject_SwitchHandHook(); void Inject_InventoryMoveHook(); void Inject_InvenWieldHook(); +void InvenUnwield_HookDrop(); +void InvenUnwield_HookMove(); + }