Skip to content

Commit

Permalink
Inventory Ctrl+Click instant move (#546)
Browse files Browse the repository at this point in the history
* Instantly move items without dragging (closes #526)
* In the player's inventory, items from the main inven list will go to appropriate slots.
  • Loading branch information
phobos2077 authored Jun 6, 2024
1 parent b3ec9f1 commit 0323e0c
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 2 deletions.
8 changes: 7 additions & 1 deletion artifacts/ddraw.ini
Original file line number Diff line number Diff line change
Expand Up @@ -237,12 +237,18 @@ ReloadWeaponKey=0

;A key to hold down to let you move/drop a whole stack of items at once without the 'Move Items' window
;Set to 0 if you don't want a fast move key, or a DX scancode otherwise
ItemFastMoveKey=29
ItemFastMoveKey=42

;Set to 1 to skip the 'Move Items' window when taking items from containers or corpses and not holding down ItemFastMoveKey
;Requires ItemFastMoveKey to be enabled
FastMoveFromContainer=0

;A key to hold down to let you move items between inventory lists by simply clicking on them
;In the inventory screen, items from the main inventory list will go to appropriate slots
;Set to 0 if you don't want a skip drag key, or a DX scancode otherwise
;Can be mapped to the same key as ItemFastMoveKey for a combined effect
ItemMoveSkipDragKey=29

;A key to press to open a debug game editor
;Set to 0 to disable, or a DX scancode otherwise
;Requires sfall debugging mode and FalloutDebug.exe from the modders pack
Expand Down
2 changes: 2 additions & 0 deletions sfall/FalloutEngine/VariableOffsets.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@
#define FO_VAR_i_worn 0x59E954
#define FO_VAR_idle_func 0x51E234
#define FO_VAR_In_WorldMap 0x672E1C
#define FO_VAR_im_value 0x59E93C
#define FO_VAR_info_line 0x5707D0
#define FO_VAR_interfaceBuffer 0x59D3F4
#define FO_VAR_interfaceWindow 0x519024
Expand All @@ -187,6 +188,7 @@
#define FO_VAR_last_buttons 0x51E2AC
#define FO_VAR_last_button_winID 0x51E404
#define FO_VAR_last_level 0x5707B4
#define FO_VAR_last_target 0x519110
#define FO_VAR_lastMovieH 0x638E64
#define FO_VAR_lastMovieW 0x638E68
#define FO_VAR_lastMovieX 0x638E6C
Expand Down
134 changes: 133 additions & 1 deletion sfall/Modules/Inventory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ static DWORD invSizeMaxLimit;
static DWORD reloadWeaponKey;
static DWORD itemFastMoveKey;
static DWORD skipFromContainer = 0;
static DWORD itemSkipDragKey;

void InventoryKeyPressedHook(DWORD dxKey, bool pressed) {
if (pressed && reloadWeaponKey && dxKey == reloadWeaponKey && IsGameLoaded() && (GetLoopFlags() & ~(COMBAT | PCOMBAT)) == 0) {
Expand Down Expand Up @@ -683,6 +684,129 @@ static __declspec(naked) void do_move_timer_hack() {
}
}

static void _fastcall DragSkipPrepare() {
fo::var::setInt(FO_VAR_im_value) = -1; // this prevents triggering "look at" at current item after skip
fo::func::gsound_play_sfx_file("iputdown");
}

static __declspec(naked) void move_inventory_hack() {
static const DWORD MoveInventory_BackNormal = 0x4747E2; // jz loc_47488B
static const DWORD MoveInventory_SkipPlanting = 0x474966; // cmp esi, 1
static const DWORD MoveInventory_SkipTaking = 0x474A30; // cmp esi, 1
__asm {
pushadc;
}
KeyDown(itemSkipDragKey); // check pressed
__asm {
test eax, eax;
popadc;
jnz skip_drag;
cmp dword ptr[esp + 64], 0; // restore stomped code
jmp MoveInventory_BackNormal;

skip_drag:
call DragSkipPrepare;
mov eax, dword ptr[esp + 60]; // isPlanting flag
test eax, eax;
jz jmp_taking;
jmp MoveInventory_SkipPlanting;
jmp_taking:
jmp MoveInventory_SkipTaking;
}
}

static DWORD BarterMoveInventory_BackNormal;
static DWORD BarterMoveInventory_SkipPlacing;
static DWORD BarterMoveInventory_SkipTaking;

static __declspec(naked) void barter_move_inventory_skip_drag_hack_common() {
__asm {
pushadc;
}
KeyDown(itemSkipDragKey); // check pressed
__asm {
test eax, eax;
popadc;
jnz skip_drag;
lea eax, ds:0[ebx * 4]; // restore stomped code
jmp BarterMoveInventory_BackNormal;

skip_drag:
call DragSkipPrepare;
mov eax, dword ptr[esp + 68]; // fromDude flag
test eax, eax;
jz jmp_taking;
jmp BarterMoveInventory_SkipPlacing;
jmp_taking:
jmp BarterMoveInventory_SkipTaking;
}
}

static __declspec(naked) void barter_move_inventory_skip_drag_hack() {
BarterMoveInventory_BackNormal = 0x474DC1; // sub eax, ebx
BarterMoveInventory_SkipPlacing = 0x474F83; // cmp esi, 1
BarterMoveInventory_SkipTaking = 0x475002; // cmp esi, 1
__asm {
jmp barter_move_inventory_skip_drag_hack_common;
}
}

static __declspec(naked) void barter_move_from_table_inventory_skip_drag_hack() {
BarterMoveInventory_BackNormal = 0x475085; // sub eax, ebx
BarterMoveInventory_SkipPlacing = 0x47524E; // cmp esi, 1
BarterMoveInventory_SkipTaking = 0x4752CB; // cmp esi, 1
__asm {
jmp barter_move_inventory_skip_drag_hack_common;
}
}

static DWORD _fastcall InvenPickupGetSkipAddr(fo::GameObject* item, long itemIndex) {
static const DWORD InvenPickup_SkipInven = 0x4711E8; // mov eax, [esp+20]
static const DWORD InvenPickup_SkipHandL = 0x47127D; // mov edx, ds:_i_lhand
static const DWORD InvenPickup_SkipHandR = 0x47130A; // mov ebx, ds:_i_rhand
static const DWORD InvenPickup_SkipArmor = 0x4713A9; // mov ecx, ds:_i_worn

if (!KeyDown(itemSkipDragKey)) return 0; // don't skip

DragSkipPrepare();
if (itemIndex < 0) {
// From slots to inventory.
return InvenPickup_SkipInven;
}
// From inventory to slots
if (fo::func::item_get_type(item) == fo::item_type_armor)
return InvenPickup_SkipArmor; // armor slot, potentially replacing
else if (fo::var::inven_dude == fo::var::obj_dude && fo::func::intface_is_item_right_hand())
return InvenPickup_SkipHandR; // right hand

return InvenPickup_SkipHandL; // left hand;
}

static __declspec(naked) void inven_pickup_skip_drag_hack() {
static const DWORD InvenPickup_Back1 = 0x470EBC; // mov eax, [esp+64]
static const DWORD InvenPickup_Back2 = 0x470EEA; // mov eax, ds:_i_wid
__asm {
pushadc;
mov ecx, [esp + 36]; // item
mov edx, esi; // item index
call InvenPickupGetSkipAddr;
test eax, eax;
jnz skip_drag;
popadc;
cmp esi, 0xFFFFFFFF; // restore stomped code
jz back_2;
jmp InvenPickup_Back1;
back_2:
jmp InvenPickup_Back2;

skip_drag:
pop ecx;
pop edx;
add esp, 4;
jmp eax;
}
}

static int invenApCost, invenApCostDef;
static char invenApQPReduction;

Expand Down Expand Up @@ -803,7 +927,7 @@ void Inventory::init() {
SafeWrite8(0x476569, 0x91); // xchg ecx, eax
};

itemFastMoveKey = IniReader::GetConfigInt("Input", "ItemFastMoveKey", DIK_LCONTROL);
itemFastMoveKey = IniReader::GetConfigInt("Input", "ItemFastMoveKey", 0);
if (itemFastMoveKey > 0) {
HookCall(0x476897, do_move_timer_hook);
// Do not call the 'Move Items' window when taking items from containers or corpses
Expand All @@ -816,6 +940,14 @@ void Inventory::init() {
MakeCall(0x4768A3, do_move_timer_hack);
}

itemSkipDragKey = IniReader::GetConfigInt("Input", "ItemMoveSkipDragKey", 0);
if (itemSkipDragKey > 0) {
MakeJump(0x4747DD, move_inventory_hack);
MakeJump(0x474DBA, barter_move_inventory_skip_drag_hack);
MakeJump(0x47507E, barter_move_from_table_inventory_skip_drag_hack);
MakeJump(0x470EB7, inven_pickup_skip_drag_hack);
}

// Move items from bag/backpack to the main inventory list by dragging them on the character portrait (similar to Fallout 1 behavior)
MakeCall(0x471452, inven_pickup_hack);

Expand Down

0 comments on commit 0323e0c

Please sign in to comment.