Backported ItemCounterAutoCaps from 4.x

This commit is contained in:
NovaRain
2024-05-23 20:17:49 +08:00
parent 0746d6b483
commit c74ddc1cbc
5 changed files with 91 additions and 15 deletions
+4
View File
@@ -670,6 +670,10 @@ ReloadReserve=-1
;Set to 1 to change the counter in the 'Move Items' window to start with maximum number, except in the barter screen ;Set to 1 to change the counter in the 'Move Items' window to start with maximum number, except in the barter screen
ItemCounterDefaultMax=0 ItemCounterDefaultMax=0
;Set to 1 to enable money/caps auto-balancing in the barter screen
;When moving money/caps to or from the table, the 'Move Items' window will be pre-filled with the correct balancing amount
ItemCounterAutoCaps=0
;Set to 1 to leave the music playing in dialogue with talking heads ;Set to 1 to leave the music playing in dialogue with talking heads
EnableMusicInDialogue=0 EnableMusicInDialogue=0
+4 -4
View File
@@ -12,7 +12,7 @@ PTR_(bckgnd, BYTE*)
PTR_(black_palette, DWORD) PTR_(black_palette, DWORD)
PTR_(BlueColor, BYTE) PTR_(BlueColor, BYTE)
PTR_(bottom_line, DWORD) PTR_(bottom_line, DWORD)
PTR_(btable, DWORD) PTR_(btable, fo::GameObject*)
PTR_(btncnt, DWORD) PTR_(btncnt, DWORD)
PTR_(cap, fo::AIcap*) // dynamic array PTR_(cap, fo::AIcap*) // dynamic array
PTR_(carCurrentArea, DWORD) PTR_(carCurrentArea, DWORD)
@@ -181,7 +181,7 @@ PTR_(procTableStrs, const char*) // table of procId (from define.h
PTR_(proto_main_msg_file, fo::MessageList) PTR_(proto_main_msg_file, fo::MessageList)
PTR_(proto_msg_files, fo::MessageList) // array of 6 elements PTR_(proto_msg_files, fo::MessageList) // array of 6 elements
PTR_(protoLists, fo::ProtoList) // array of 11 elements PTR_(protoLists, fo::ProtoList) // array of 11 elements
PTR_(ptable, DWORD) PTR_(ptable, fo::GameObject*)
PTR_(pud, DWORD) PTR_(pud, DWORD)
PTR_(queue, fo::Queue*) PTR_(queue, fo::Queue*)
PTR_(quick_done, DWORD) PTR_(quick_done, DWORD)
@@ -203,7 +203,7 @@ PTR_(sound_music_path2, char*)
PTR_(speech_volume, DWORD) PTR_(speech_volume, DWORD)
PTR_(square, DWORD*) // array of 3 pointers, use (square && 0xFFF) to get ground fid, and ((square >> 16) && 0xFFF) to get roof PTR_(square, DWORD*) // array of 3 pointers, use (square && 0xFFF) to get ground fid, and ((square >> 16) && 0xFFF) to get roof
PTR_(squares, DWORD*) PTR_(squares, DWORD*)
PTR_(stack, DWORD) // array of 10 DWORD PTR_(stack, fo::GameObject*) // array of 10 GameObject*
PTR_(stack_offset, DWORD) // array of 10 DWORD PTR_(stack_offset, DWORD) // array of 10 DWORD
PTR_(stat_data, fo::StatInfo) // array of size == STAT_real_max_stat PTR_(stat_data, fo::StatInfo) // array of size == STAT_real_max_stat
PTR_(stat_flag, DWORD) PTR_(stat_flag, DWORD)
@@ -213,7 +213,7 @@ PTR_(Tag_, DWORD)
PTR_(tag_skill, DWORD) PTR_(tag_skill, DWORD)
PTR_(target_curr_stack, DWORD) PTR_(target_curr_stack, DWORD)
PTR_(target_pud, DWORD*) PTR_(target_pud, DWORD*)
PTR_(target_stack, DWORD) // array of 10 DWORD PTR_(target_stack, fo::GameObject*) // array of 10 GameObject*
PTR_(target_stack_offset, DWORD) // array of 10 DWORD PTR_(target_stack_offset, DWORD) // array of 10 DWORD
PTR_(target_str, DWORD) PTR_(target_str, DWORD)
PTR_(target_xpos, DWORD) PTR_(target_xpos, DWORD)
+19 -6
View File
@@ -10,6 +10,9 @@
namespace sfall namespace sfall
{ {
static DWORD lastTableCostPC; // keep last cost for pc
static DWORD lastTableCostNPC;
// The hook is executed twice when entering the barter screen and after transaction: the first time is for the player; the second time is for NPC // The hook is executed twice when entering the barter screen and after transaction: the first time is for the player; the second time is for NPC
static DWORD __fastcall BarterPriceHook_Script(fo::GameObject* source, fo::GameObject* target, DWORD callAddr) { static DWORD __fastcall BarterPriceHook_Script(fo::GameObject* source, fo::GameObject* target, DWORD callAddr) {
bool barterIsParty = (*fo::ptr::dialog_target_is_party != 0); bool barterIsParty = (*fo::ptr::dialog_target_is_party != 0);
@@ -22,12 +25,12 @@ static DWORD __fastcall BarterPriceHook_Script(fo::GameObject* source, fo::GameO
args[1] = (DWORD)target; args[1] = (DWORD)target;
args[2] = !barterIsParty ? computeCost : 0; args[2] = !barterIsParty ? computeCost : 0;
fo::GameObject* bTable = (fo::GameObject*)*fo::ptr::btable; fo::GameObject* bTable = *fo::ptr::btable;
args[3] = (DWORD)bTable; args[3] = (DWORD)bTable;
args[4] = fo::func::item_caps_total(bTable); args[4] = fo::func::item_caps_total(bTable);
args[5] = fo::func::item_total_cost(bTable); args[5] = fo::func::item_total_cost(bTable);
fo::GameObject* pTable = (fo::GameObject*)*fo::ptr::ptable; fo::GameObject* pTable = *fo::ptr::ptable;
args[6] = (DWORD)pTable; args[6] = (DWORD)pTable;
long pcCost = 0; long pcCost = 0;
@@ -66,11 +69,11 @@ static void __declspec(naked) BarterPriceHook() {
call BarterPriceHook_Script; // edx - target call BarterPriceHook_Script; // edx - target
pop ecx; pop ecx;
pop edx; pop edx;
mov lastTableCostNPC, eax;
retn; retn;
} }
} }
static DWORD offersGoodsCost; // keep last cost for pc
static void __declspec(naked) PC_BarterPriceHook() { static void __declspec(naked) PC_BarterPriceHook() {
__asm { __asm {
push edx; push edx;
@@ -82,18 +85,28 @@ static void __declspec(naked) PC_BarterPriceHook() {
call BarterPriceHook_Script; call BarterPriceHook_Script;
pop ecx; pop ecx;
pop edx; pop edx;
mov offersGoodsCost, eax; mov lastTableCostPC, eax;
retn; retn;
} }
} }
static void __declspec(naked) OverrideCost_BarterPriceHook() { static void __declspec(naked) OverrideCost_BarterPriceHook() {
__asm { __asm {
mov eax, offersGoodsCost; mov eax, lastTableCostPC;
retn; retn;
} }
} }
void BarterPriceHook_GetLastCosts(long& outPcTableCost, long& outNpcTableCost) {
if (!HookScripts::HookHasScript(HOOK_BARTERPRICE)) {
outPcTableCost = fo::func::item_total_cost(*fo::ptr::ptable);
outNpcTableCost = fo::func::barter_compute_value(*fo::ptr::obj_dude, fo::ptr::target_stack[0]);
return;
}
outPcTableCost = lastTableCostPC;
outNpcTableCost = lastTableCostNPC;
}
static fo::GameObject* sourceSkillOn = nullptr; static fo::GameObject* sourceSkillOn = nullptr;
void SourceUseSkillOnInit() { sourceSkillOn = *fo::ptr::obj_dude; } void SourceUseSkillOnInit() { sourceSkillOn = *fo::ptr::obj_dude; }
@@ -735,7 +748,7 @@ void Inject_BarterPriceHook() {
0x475762 // display_table_inventories_ 0x475762 // display_table_inventories_
}; };
HookCalls(BarterPriceHook, barterPriceHkAddr); HookCalls(BarterPriceHook, barterPriceHkAddr);
const DWORD pcBarterPriceHkAddr[] = {0x4754F4, 0X47551A}; // display_table_inventories_ const DWORD pcBarterPriceHkAddr[] = {0x4754F4, 0x47551A}; // display_table_inventories_
HookCalls(PC_BarterPriceHook, pcBarterPriceHkAddr); HookCalls(PC_BarterPriceHook, pcBarterPriceHkAddr);
HookCall(0x474D3F, OverrideCost_BarterPriceHook); // barter_attempt_transaction_ (just overrides cost of offered goods) HookCall(0x474D3F, OverrideCost_BarterPriceHook); // barter_attempt_transaction_ (just overrides cost of offered goods)
} }
+2
View File
@@ -21,4 +21,6 @@ void Inject_RollCheckHook();
long PerceptionRangeHook_Invoke(fo::GameObject* watcher, fo::GameObject* target, long type, long result); long PerceptionRangeHook_Invoke(fo::GameObject* watcher, fo::GameObject* target, long type, long result);
void BarterPriceHook_GetLastCosts(long& outPcTableCost, long& outNpcTableCost);
} }
+62 -5
View File
@@ -24,6 +24,7 @@
#include "HeroAppearance.h" #include "HeroAppearance.h"
#include "HookScripts.h" #include "HookScripts.h"
#include "LoadGameHook.h" #include "LoadGameHook.h"
#include "HookScripts\MiscHs.h"
#include "..\Game\inventory.h" #include "..\Game\inventory.h"
#include "..\Game\items.h" #include "..\Game\items.h"
@@ -618,12 +619,66 @@ end:
} }
} }
static long CalculateSuggestedMoveCount(fo::GameObject* item, long maxQuantity, bool fromPlayer, bool fromInventory) {
// This is an exact copy of logic from https://github.com/alexbatalov/fallout2-ce/pull/311
if (item->protoId == fo::PID_BOTTLE_CAPS && !*fo::ptr::dialog_target_is_party) {
// Calculate change money automatically
long totalCostPlayer, totalCostNpc;
BarterPriceHook_GetLastCosts(totalCostPlayer, totalCostNpc);
// Actor's balance: negative - the actor must add money to balance the tables and vice versa
long balance = fromPlayer ? totalCostPlayer - totalCostNpc : totalCostNpc - totalCostPlayer;
if ((balance < 0 && fromInventory) || (balance > 0 && !fromInventory)) {
return min(std::abs(balance), maxQuantity);
}
}
return 1;
}
static bool itemCounterDefaultMax;
static bool itemCounterAutoCaps;
static long __fastcall CalculateDefaultMoveCount(DWORD maybeItem, DWORD retAddr, DWORD maxValue) {
maxValue = min(maxValue, 99999); // capped like in vanilla
if ((GetLoopFlags() & BARTER) != 0) {
if (itemCounterAutoCaps && maxValue > 1) {
bool fromPlayer;
bool fromInventory;
switch (retAddr) {
case 0x474F96: // barter_move_inventory
fromPlayer = true;
fromInventory = true;
break;
case 0x475015: // barter_move_inventory
fromPlayer = false;
fromInventory = true;
break;
case 0x475261: // barter_move_from_table_inventory
fromPlayer = true;
fromInventory = false;
break;
case 0x4752DE: // barter_move_from_table_inventory
fromPlayer = false;
fromInventory = false;
break;
default:
return 1;
}
// maybeItem may not contain object pointer in all cases, but it does in all 4 from above.
return CalculateSuggestedMoveCount((fo::GameObject*)maybeItem, maxValue, fromPlayer, fromInventory);
}
return 1;
}
return itemCounterDefaultMax ? maxValue : 1;
}
static void __declspec(naked) do_move_timer_hack() { static void __declspec(naked) do_move_timer_hack() {
__asm { __asm {
mov ebx, 1; push ecx;
call GetLoopFlags; push ebp; // max
test eax, BARTER; mov edx, dword ptr [esp + 32]; // return address
cmovz ebx, ebp; // set max when not in barter mov ecx, dword ptr [esp + 20]; // item, potentially
call CalculateDefaultMoveCount;
mov ebx, eax;
pop ecx;
retn; retn;
} }
} }
@@ -753,7 +808,9 @@ void Inventory::init() {
skipFromContainer = IniReader::GetConfigInt("Input", "FastMoveFromContainer", 0); skipFromContainer = IniReader::GetConfigInt("Input", "FastMoveFromContainer", 0);
} }
if (IniReader::GetConfigInt("Misc", "ItemCounterDefaultMax", 0)) { itemCounterDefaultMax = (IniReader::GetConfigInt("Misc", "ItemCounterDefaultMax", 0) != 0);
itemCounterAutoCaps = (IniReader::GetConfigInt("Misc", "ItemCounterAutoCaps", 0) != 0);
if (itemCounterDefaultMax || itemCounterAutoCaps) {
MakeCall(0x4768A3, do_move_timer_hack); MakeCall(0x4768A3, do_move_timer_hack);
} }