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.
This commit is contained in:
NovaRain
2019-10-04 22:04:05 +08:00
parent 27ec2d24fe
commit 4cf0c6688f
6 changed files with 193 additions and 32 deletions
+2 -1
View File
@@ -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
+34 -4
View File
@@ -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);
//}
+2 -1
View File
@@ -99,7 +99,7 @@ DWORD* ptr_inven_scroll_dn_bid = reinterpret_cast<DWORD*>(_inven_scroll_d
DWORD* ptr_inven_scroll_up_bid = reinterpret_cast<DWORD*>(_inven_scroll_up_bid);
DWORD* ptr_inventry_message_file = reinterpret_cast<DWORD*>(_inventry_message_file);
DWORD* ptr_itemButtonItems = reinterpret_cast<DWORD*>(_itemButtonItems);
DWORD* ptr_itemCurrentItem = reinterpret_cast<DWORD*>(_itemCurrentItem); // 0 - left, 1 - right
long* ptr_itemCurrentItem = reinterpret_cast<long*>(_itemCurrentItem); // 0 - left, 1 - right
DWORD* ptr_kb_lock_flags = reinterpret_cast<DWORD*>(_kb_lock_flags);
DWORD* ptr_last_buttons = reinterpret_cast<DWORD*>(_last_buttons);
DWORD* ptr_last_button_winID = reinterpret_cast<DWORD*>(_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;
+2 -1
View File
@@ -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*<eax>, int aPid<ebx>
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_;
+144 -20
View File
@@ -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;
@@ -1302,14 +1328,14 @@ static void __declspec(naked) InvenUnwieldFuncHook() {
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;
}
@@ -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
+6 -2
View File
@@ -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();