Skip to content

Commit

Permalink
Merge pull request #537 from sfall-team/feature/barter-auto-calc-money
Browse files Browse the repository at this point in the history
Added ItemCounterAutoCaps that would pre-fill money count box with "correct" amount when in barter.
  • Loading branch information
NovaRain committed May 23, 2024
2 parents 6b818e7 + 522219d commit 08a5f86
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 14 deletions.
3 changes: 3 additions & 0 deletions artifacts/ddraw.ini
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,9 @@ ReloadReserve=-1
;Set to 1 to change the counter in the 'Move Items' window to start with maximum number, except in the barter screen
ItemCounterDefaultMax=0

;Set to 1 to enable caps auto-balancing: when dragging caps between tables in the barter screen, 'Move Items' window will be shown with correct number pre-filled that balances the tables
ItemCounterAutoCaps=0

;Set to 1 to leave the music playing in dialogue with talking heads
EnableMusicInDialogue=0

Expand Down
1 change: 1 addition & 0 deletions sfall/FalloutEngine/Fallout2.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/

#include "Enums.h"
#include "GamePids.h"
#include "FunctionOffsets.h"
#include "Structs.h"
#include "EngineUtils.h"
Expand Down
8 changes: 4 additions & 4 deletions sfall/FalloutEngine/Variables_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ VAR_(bckgnd, BYTE*)
VAR_(black_palette, DWORD)
VAR_(BlueColor, BYTE)
VAR_(bottom_line, DWORD)
VAR_(btable, DWORD)
VAR_(btable, fo::GameObject*)
VAR_(btncnt, DWORD)
VAR_(buf_length_2, long)
VARD(cap, fo::AIcap) // dynamic array
Expand Down Expand Up @@ -198,7 +198,7 @@ VARA(procTableStrs, const char*, (int)fo::Scripts::ScriptProc::coun
VAR_(proto_main_msg_file, fo::MessageList)
VARA(proto_msg_files, fo::MessageList, 6) // array of 6 elements
VARA(protoLists, fo::ProtoList, 11)
VAR_(ptable, DWORD)
VAR_(ptable, fo::GameObject*)
VAR_(pud, DWORD)
VAR_(queue, fo::Queue*)
VAR_(quick_done, DWORD)
Expand All @@ -222,7 +222,7 @@ VAR_(speech_volume, DWORD)
VARA(square, DWORD*, 3) // use (square && 0xFFF) to get ground fid, and ((square >> 16) && 0xFFF) to get roof
VAR_(square_rect, fo::SquareRect) // _square_y
VAR_(squares, DWORD*)
VARA(stack, DWORD, 10)
VARA(stack, fo::GameObject*, 10)
VARA(stack_offset, DWORD, 10)
VARA(stat_data, fo::StatInfo, fo::STAT_real_max_stat)
VAR_(stat_flag, DWORD)
Expand All @@ -232,7 +232,7 @@ VAR_(Tag_, DWORD)
VAR_(tag_skill, DWORD)
VAR_(target_curr_stack, DWORD)
VAR_(target_pud, DWORD*)
VARA(target_stack, DWORD, 10)
VARA(target_stack, fo::GameObject*, 10)
VARA(target_stack_offset, DWORD, 10)
VAR_(target_str, DWORD)
VAR_(target_xpos, DWORD)
Expand Down
26 changes: 21 additions & 5 deletions sfall/Modules/HookScripts/MiscHs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

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
static DWORD __fastcall BarterPriceHook_Script(fo::GameObject* source, fo::GameObject* target, DWORD callAddr) {
Expand All @@ -22,12 +25,12 @@ static DWORD __fastcall BarterPriceHook_Script(fo::GameObject* source, fo::GameO
args[1] = (DWORD)target;
args[2] = !barterIsParty ? computeCost : 0;

fo::GameObject* bTable = (fo::GameObject*)fo::var::btable;
fo::GameObject* bTable = fo::var::btable;
args[3] = (DWORD)bTable;
args[4] = fo::func::item_caps_total(bTable);
args[5] = fo::func::item_total_cost(bTable);

fo::GameObject* pTable = (fo::GameObject*)fo::var::ptable;
fo::GameObject* pTable = fo::var::ptable;
args[6] = (DWORD)pTable;

long pcCost = 0;
Expand All @@ -52,6 +55,11 @@ static DWORD __fastcall BarterPriceHook_Script(fo::GameObject* source, fo::GameO
cost = rets[0]; // new cost for npc
}
}
if (isPCHook) {
lastTableCostPC = cost;
} else {
lastTableCostNPC = cost;
}
EndHook();
return cost;
}
Expand All @@ -70,7 +78,6 @@ static void __declspec(naked) BarterPriceHook() {
}
}

static DWORD offersGoodsCost; // keep last cost for pc
static void __declspec(naked) PC_BarterPriceHook() {
__asm {
push edx;
Expand All @@ -82,18 +89,27 @@ static void __declspec(naked) PC_BarterPriceHook() {
call BarterPriceHook_Script;
pop ecx;
pop edx;
mov offersGoodsCost, eax;
retn;
}
}

static void __declspec(naked) OverrideCost_BarterPriceHook() {
__asm {
mov eax, offersGoodsCost;
mov eax, lastTableCostPC;
retn;
}
}

void BarterPriceHook_GetLastCosts(long& outPcTableCost, long& outNpcTableCost) {
if (!HookScripts::HookHasScript(HOOK_BARTERPRICE)) {
outPcTableCost = fo::func::item_total_cost(fo::var::ptable);
outNpcTableCost = fo::func::barter_compute_value(fo::var::obj_dude, fo::var::target_stack[0]);
return;
}
outPcTableCost = lastTableCostPC;
outNpcTableCost = lastTableCostNPC;
}

static fo::GameObject* sourceSkillOn = nullptr;
void SourceUseSkillOnInit() { sourceSkillOn = fo::var::obj_dude; }

Expand Down
2 changes: 2 additions & 0 deletions sfall/Modules/HookScripts/MiscHs.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ void Inject_RollCheckHook();

long PerceptionRangeHook_Invoke(fo::GameObject* watcher, fo::GameObject* target, long type, long result);

void BarterPriceHook_GetLastCosts(long& outPcTableCost, long& outNpcTableCost);

}
68 changes: 63 additions & 5 deletions sfall/Modules/Inventory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "..\Translate.h"

#include "LoadGameHook.h"
#include "HookScripts\MiscHs.h"

#include "..\Game\inventory.h"
#include "..\Game\items.h"
Expand Down Expand Up @@ -618,12 +619,67 @@ static void __declspec(naked) do_move_timer_hook() {
}
}

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::var::dialog_target_is_party) {
// Calculate change money automatically
long totalCostPlayer;
long 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() {
__asm {
mov ebx, 1;
call GetLoopFlags;
test eax, BARTER;
cmovz ebx, ebp; // set max when not in barter
push ecx;
push ebp; // max
mov edx, dword ptr[esp + 32]; // return address
mov ecx, dword ptr[esp + 20]; // item, potentially
call CalculateDefaultMoveCount;
mov ebx, eax;
pop ecx;
retn;
}
}
Expand Down Expand Up @@ -755,7 +811,9 @@ void Inventory::init() {
skipFromContainer = IniReader::GetConfigInt("Input", "FastMoveFromContainer", 0);
}

if (IniReader::GetConfigInt("Misc", "ItemCounterDefaultMax", 0)) {
itemCounterDefaultMax = IniReader::GetConfigInt("Misc", "ItemCounterDefaultMax", 0);
itemCounterAutoCaps = IniReader::GetConfigInt("Misc", "ItemCounterAutoCaps", 0);
if (itemCounterDefaultMax || itemCounterAutoCaps) {
MakeCall(0x4768A3, do_move_timer_hack);
}

Expand Down

0 comments on commit 08a5f86

Please sign in to comment.