Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ItemCounterAutoCaps #537

Merged
merged 2 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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