From 4cf0c6688f018001e8723467b86463e8b6df2098 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Fri, 4 Oct 2019 20:22:48 +0800 Subject: [PATCH] Improved the implementation of HOOK_INVENWIELD * it now runs for the player and NPCs when removing equipped items. * added an additional argument. Fixed drop_obj script function not removing the equipped armor properly for the player and party members. --- artifacts/scripting/hookscripts.txt | 3 +- sfall/BugFixes.cpp | 38 ++++++- sfall/FalloutEngine.cpp | 3 +- sfall/FalloutEngine.h | 3 +- sfall/HookScripts.cpp | 170 ++++++++++++++++++++++++---- sfall/HookScripts.h | 8 +- 6 files changed, 193 insertions(+), 32 deletions(-) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 43ca985a..28f4aa34 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,5 +440,6 @@ 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/BugFixes.cpp b/sfall/BugFixes.cpp index 2337ee05..b12b41c5 100644 --- a/sfall/BugFixes.cpp +++ b/sfall/BugFixes.cpp @@ -3,6 +3,7 @@ #include "BugFixes.h" #include "Define.h" #include "FalloutEngine.h" +#include "HookScripts.h" #include "LoadGameHook.h" #include "ScriptExtender.h" @@ -382,7 +383,7 @@ nextArmor: call inven_worn_; test eax, eax; jz noArmor; - and byte ptr [eax + 0x27], 0xFB; // Unset the flag of equipped armor + and byte ptr [eax + 0x27], 0xFB; // Unset flag of equipped armor (~Worn >> 24) jmp nextArmor; noArmor: mov eax, esi; @@ -398,7 +399,7 @@ nextArmor: call inven_worn_; test eax, eax; jz end; - and byte ptr [eax + 0x27], 0xFB; // Unset flag of equipped armor + and byte ptr [eax + 0x27], 0xFB; // Unset flag of equipped armor (~Worn >> 24) jmp nextArmor; end: retn; @@ -420,11 +421,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 adjust_ac_; // fix for party member + call adjust_ac_; mov edx, ecx; + pop ebx; xor eax, eax; end: retn; // must be eax = 0 @@ -432,7 +437,7 @@ dudeFix: test eax, eax; jz equipped; // no armor // additionally check flag of equipped armor for dude - test byte ptr [eax + 0x27], 0x4; + test byte ptr [eax + 0x27], 0x4; // Worn >> 24 jnz equipped; xor eax, eax; equipped: @@ -441,6 +446,29 @@ equipped: } } +static void __declspec(naked) obj_drop_hook() { + __asm { + test byte ptr [edx + 0x27], 0x7; // (Worn | Right_Hand | Left_Hand) >> 24 + jz skipHook; + call InvenUnwield_HookDrop; // run HOOK_INVENWIELD before dropping item +skipHook: + test byte ptr [edx + 0x27], 0x4; // Worn >> 24 + jnz fixArmorStat; + jmp obj_remove_from_inven_; +fixArmorStat: + call isPartyMember_; // and dude + test eax, eax; + jz skip; + mov eax, ecx; + xor ebx, ebx; // new armor + call adjust_ac_; // eax - source, edx - removed armor + mov edx, esi; +skip: + mov eax, ecx; + jmp obj_remove_from_inven_; + } +} + static void __declspec(naked) partyMemberIncLevels_hook() { __asm { mov ebx, eax; // party member pointer @@ -2422,6 +2450,8 @@ void BugFixesInit() 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/FalloutEngine.cpp b/sfall/FalloutEngine.cpp index ca68c3af..cba8f4a0 100644 --- a/sfall/FalloutEngine.cpp +++ b/sfall/FalloutEngine.cpp @@ -99,7 +99,7 @@ DWORD* ptr_inven_scroll_dn_bid = reinterpret_cast(_inven_scroll_d DWORD* ptr_inven_scroll_up_bid = reinterpret_cast(_inven_scroll_up_bid); DWORD* ptr_inventry_message_file = reinterpret_cast(_inventry_message_file); DWORD* ptr_itemButtonItems = reinterpret_cast(_itemButtonItems); -DWORD* ptr_itemCurrentItem = reinterpret_cast(_itemCurrentItem); // 0 - left, 1 - right +long* ptr_itemCurrentItem = reinterpret_cast(_itemCurrentItem); // 0 - left, 1 - right DWORD* ptr_kb_lock_flags = reinterpret_cast(_kb_lock_flags); DWORD* ptr_last_buttons = reinterpret_cast(_last_buttons); DWORD* ptr_last_button_winID = reinterpret_cast(_last_button_winID); @@ -524,6 +524,7 @@ const DWORD obj_lock_is_jammed_ = 0x49D410; const DWORD obj_new_sid_inst_ = 0x49AAC0; const DWORD obj_outline_object_ = 0x48C2B4; const DWORD obj_pid_new_ = 0x489C9C; +const DWORD obj_remove_from_inven_ = 0x49B73C; const DWORD obj_remove_outline_ = 0x48C2F0; const DWORD obj_save_dude_ = 0x48D59C; const DWORD obj_scroll_blocking_at_ = 0x48BB44; diff --git a/sfall/FalloutEngine.h b/sfall/FalloutEngine.h index 1c8561fd..6fd3f809 100644 --- a/sfall/FalloutEngine.h +++ b/sfall/FalloutEngine.h @@ -341,7 +341,7 @@ extern DWORD* ptr_inven_scroll_dn_bid; extern DWORD* ptr_inven_scroll_up_bid; extern DWORD* ptr_inventry_message_file; extern DWORD* ptr_itemButtonItems; -extern DWORD* ptr_itemCurrentItem; // 0 - left, 1 - right +extern long* ptr_itemCurrentItem; // 0 - left, 1 - right extern DWORD* ptr_kb_lock_flags; extern DWORD* ptr_last_buttons; extern DWORD* ptr_last_button_winID; @@ -756,6 +756,7 @@ extern const DWORD obj_new_; // int aObj*, int aPid extern const DWORD obj_new_sid_inst_; extern const DWORD obj_outline_object_; extern const DWORD obj_pid_new_; +extern const DWORD obj_remove_from_inven_; extern const DWORD obj_remove_outline_; extern const DWORD obj_save_dude_; extern const DWORD obj_scroll_blocking_at_; diff --git a/sfall/HookScripts.cpp b/sfall/HookScripts.cpp index bcbbcf30..62e76267 100644 --- a/sfall/HookScripts.cpp +++ b/sfall/HookScripts.cpp @@ -1254,17 +1254,45 @@ 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(TGameObj* critter, TGameObj* item, long slot, long isWield, long isRemove) { + if (!isWield) { + // for the critter, the right slot is always the active slot + if (slot == INVEN_TYPE_LEFT_HAND && critter != *ptr_obj_dude) return 1; + // check the current active slot for the player + if (slot != INVEN_TYPE_WORN && critter == *ptr_obj_dude) { + long _slot = (slot != INVEN_TYPE_LEFT_HAND); + if (_slot != *ptr_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() { @@ -1275,13 +1303,11 @@ static void __declspec(naked) InvenWieldFuncHook() { mov args[8], ebx; // slot pushad; } - // right hand slot? if (args[2] != INVEN_TYPE_RIGHT_HAND && ItemGetType((TGameObj*)args[1]) != item_type_armor) { args[2] = INVEN_TYPE_LEFT_HAND; } - - InvenWieldHook_Script(1); // wield flag + InvenWieldHook_ScriptPart(1); // wield event __asm { test al, al; @@ -1298,18 +1324,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] = INVEN_TYPE_LEFT_HAND; } - args[1] = (DWORD)GetItemPtrSlot((TGameObj*)args[0], args[2]); // get item + // get item + args[1] = (DWORD)GetItemPtrSlot((TGameObj*)args[0], args[2]); - InvenWieldHook_Script(0); // unwield flag + InvenWieldHook_ScriptPart(0); // unwield event __asm { test al, al; @@ -1328,9 +1354,8 @@ static void __declspec(naked) CorrectFidForRemovedItemHook() { mov args[0], eax; // critter mov args[4], edx; // item mov args[8], ebx; // item flag - pushad; + pushadc; } - // set slot if (args[2] & 0x2000000) { // right hand slot args[2] = INVEN_TYPE_RIGHT_HAND; @@ -1339,20 +1364,117 @@ static void __declspec(naked) CorrectFidForRemovedItemHook() { } else { args[2] = 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 correctFidForRemovedItem_; + } +} + +static void __declspec(naked) item_drop_all_hack() { + __asm { + push eax; + push 1; // remove event + push 0; // unwield event + mov ecx, INVEN_TYPE_LEFT_HAND; + test ah, 0x1; // Left_Hand >> 24 + jnz skip; + test ah, 0x4; // 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; } } +// called from bugfixes for obj_drop_ +void __declspec(naked) InvenUnwield_HookDrop() { // ecx - critter, edx - item + __asm { + pushadc; + mov eax, INVEN_TYPE_LEFT_HAND; + test byte ptr [edx + 0x27], 0x1; // Left_Hand >> 24 + jnz isLeft; + test byte ptr [edx + 0x27], 0x4; // 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 { + 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() { + __asm { + cmp eax, ds:[_obj_dude]; + je runHook; + jmp item_move_all_; +runHook: + push eax; + push edx; + mov ecx, eax; // keep source + mov edx, ds:[_itemCurrentItem]; // get player's active slot + test edx, edx; + jz left; + call inven_right_hand_; + jmp skip; +left: + call 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 item_move_all_; + } +} + DWORD _stdcall GetHSArgCount() { return argCount; } @@ -1594,7 +1716,7 @@ static void HookScriptInit2() { 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); LoadHookScript("hs_invenwield", HOOK_INVENWIELD); @@ -1605,6 +1727,8 @@ static void HookScriptInit2() { HookCall(0x495F0B, InvenUnwieldFuncHook); // partyMemberCopyLevelInfo_ HookCall(0x45680C, CorrectFidForRemovedItemHook); // op_rm_obj_from_inven_ HookCall(0x45C4EA, CorrectFidForRemovedItemHook); // op_move_obj_inven_to_obj_ + HookCall(0x45C4F6, op_move_obj_inven_to_obj_hook); + MakeCall(0x4778AF, item_drop_all_hack, 3); __asm { xor edx, edx diff --git a/sfall/HookScripts.h b/sfall/HookScripts.h index d60831c2..6cf00316 100644 --- a/sfall/HookScripts.h +++ b/sfall/HookScripts.h @@ -63,6 +63,10 @@ void HookScriptClear(); extern DWORD InitingHookScripts; extern int __fastcall AmmoCostHook_Script(DWORD hookType, TGameObj* weapon, DWORD &rounds); -void _stdcall MouseClickHook(DWORD button, bool pressed); -DWORD _stdcall KeyPressHook(DWORD dxKey, bool pressed, DWORD vKey); void _stdcall RunHookScriptsAtProc(DWORD procId); + +DWORD _stdcall KeyPressHook(DWORD dxKey, bool pressed, DWORD vKey); +void _stdcall MouseClickHook(DWORD button, bool pressed); + +void InvenUnwield_HookDrop(); +void InvenUnwield_HookMove();