Backported HOOK_ADJUSTFID from 4.2

This commit is contained in:
NovaRain
2021-01-29 23:03:26 +08:00
parent 87af67346e
commit 21b88c3cf0
11 changed files with 178 additions and 95 deletions
+1
View File
@@ -51,6 +51,7 @@
#define HOOK_WITHINPERCEPTION (23)
#define HOOK_INVENTORYMOVE (24)
#define HOOK_INVENWIELD (25)
#define HOOK_ADJUSTFID (26)
#define HOOK_GAMEMODECHANGE (31)
//Valid arguments to list_begin
+14
View File
@@ -465,6 +465,20 @@ int ret0 - overrides hard-coded handler (-1 - use engine handler, any other
-------------------------------------------
HOOK_ADJUSTFID (hs_adjustfid.int)
Runs after calculating character figure FID on the inventory screen, whenever the game decides that character appearance might change.
Also happens on other screens, like barter.
NOTE: FID has following format: 0x0ABBCDDD, where A is object type, BB - animation code (always 0 in this case), C - weapon code, DDD - FRM index in LST file.
int arg0 - the vanilla FID calculated by the engine according to critter base FID and armor/weapon being used
int arg1 - the modified FID calculated by the internal sfall code (like Hero Appearance Mod)
int ret0 - overrides the calculated FID with provided value
-------------------------------------------
HOOK_GAMEMODECHANGE (hs_gamemodechange.int)
Runs once every time when the game mode was changed, like opening/closing the inventory, character screen, pipboy, etc.
+1
View File
@@ -552,6 +552,7 @@ FUNC(win_register_button_disable_, 0x4D8674)
FUNC(win_register_button_sound_func_, 0x4D87F8)
FUNC(win_show_, 0x4D6DAC)
FUNC(win_width_, 0x4D7918)
FUNC(windowCheckRegion_, 0x4B6858)
FUNC(windowDisplayBuf_, 0x4B8EF0)
FUNC(windowDisplayTransBuf_, 0x4B8F64)
FUNC(windowGetBuffer_, 0x4B82DC)
+1
View File
@@ -118,6 +118,7 @@ WRAP_WATCOM_FUNC2(long, interpretFindProcedure, TProgram*, scriptPtr, const char
WRAP_WATCOM_FUNC1(DWORD, interpretPopShort, TProgram*, scriptPtr) // Pops value type from Data stack (must be followed by InterpretPopLong)
WRAP_WATCOM_FUNC1(DWORD, interpretPopLong, TProgram*, scriptPtr) // Pops value from Data stack (must be preceded by InterpretPopShort)
WRAP_WATCOM_FUNC2(long, intface_get_attack, DWORD*, hitMode, DWORD*, isSecondary)
WRAP_WATCOM_FUNC0(long, intface_is_item_right_hand)
WRAP_WATCOM_FUNC0(long, intface_is_hidden)
WRAP_WATCOM_FUNC0(void, intface_redraw) // Redraws the main game interface windows (useful after changing some data like active hand, etc.)
WRAP_WATCOM_FUNC0(void, intface_toggle_item_state)
+42 -81
View File
@@ -20,6 +20,7 @@
#include "main.h"
#include "FalloutEngine.h"
#include "Inventory.h"
#include "LoadGameHook.h"
#include "LoadOrder.h"
#include "Message.h"
@@ -266,21 +267,12 @@ static void __declspec(naked) AdjustHeroBaseArt() {
}
// adjust armor art if num below hero art range
static void __declspec(naked) AdjustHeroArmorArt() {
__asm {
pop ebx; // get ret addr
mov dword ptr ss:[esp], ebx; // reinsert ret addr in old (push 0)
xor ebx, ebx;
push 0;
call art_id_; // call load frm func
mov dx, ax;
and dh, 0x0F; // mask out current weapon flag
cmp edx, critterListSize; // check if critter art in PC range
jg endFunc;
add eax, critterListSize; // shift critter art index up into hero range
endFunc:
//mov dword ptr ds:[FO_VAR_i_fid], eax;
retn;
void __stdcall AdjustHeroArmorArt(DWORD fid) {
if ((fid & 0xF000000) == (OBJ_TYPE_CRITTER << 24) && !PartyControl_IsNpcControlled()) {
DWORD fidBase = fid & 0xFFF;
if (fidBase <= critterListSize) {
*ptr_i_fid += critterListSize;
}
}
}
@@ -367,76 +359,45 @@ static void DrawPC() {
}
// scan inventory items for armor and weapons currently being worn or wielded and setup matching FrmID for PC
void __stdcall RefreshPCArt() {
__asm {
call proto_dude_update_gender_; // refresh PC base model art
void UpdateHeroArt() {
TGameObj* iD = *ptr_inven_dude;
TGameObj* iR = *ptr_i_rhand;
TGameObj* iL = *ptr_i_lhand;
TGameObj* iW = *ptr_i_worn;
push dword ptr ds:[FO_VAR_inven_dude];
push dword ptr ds:[FO_VAR_i_rhand]; // item2
push dword ptr ds:[FO_VAR_i_lhand]; // item1
push dword ptr ds:[FO_VAR_i_worn]; // armor
*ptr_i_rhand = 0;
*ptr_i_lhand = 0;
*ptr_i_worn = 0;
mov eax, dword ptr ds:[FO_VAR_obj_dude]; // PC state struct
mov dword ptr ds:[FO_VAR_inven_dude], eax; // inventory temp pointer to PC state struct
mov eax, dword ptr ds:[FO_VAR_inven_dude];
lea edx, dword ptr ds:[eax + 0x2C];
mov dword ptr ds:[FO_VAR_pud], edx; // PC inventory
*ptr_inven_dude = *ptr_obj_dude;
int invenSize = (*ptr_obj_dude)->invenSize;
//*ptr_pud = invenSize;
xor eax, eax;
xor edx, edx; // itemListOffset
xor ebx, ebx; // itemNum
for (int itemNum = 0; itemNum < invenSize; itemNum++) {
TGameObj* item = (*ptr_obj_dude)->invenTable[itemNum].object; // PC inventory item list + itemListOffset
mov dword ptr ds:[FO_VAR_i_rhand], eax; // item2
mov dword ptr ds:[FO_VAR_i_lhand], eax; // item1
mov dword ptr ds:[FO_VAR_i_worn], eax; // armor
jmp LoopStart;
CheckNextItem:
mov eax, dword ptr ds:[eax + 0x8]; // PC inventory item list
mov eax, dword ptr ds:[edx + eax]; // PC inventory item list + itemListOffset
test byte ptr ds:[eax + 0x27], 1; // if item in item1 slot
jnz IsItem1;
test byte ptr ds:[eax + 0x27], 2; // if item in item2 slot
jnz IsItem2;
test byte ptr ds:[eax + 0x27], 4; // if item in armor slot
jnz IsArmor;
jmp SetNextItem;
IsItem1:
mov dword ptr ds:[FO_VAR_i_lhand], eax; // set item1
test byte ptr ds:[eax + 0x27], 2; // check if same item type also in item2 slot
jz SetNextItem;
IsItem2:
mov dword ptr ds:[FO_VAR_i_rhand], eax; // set item2
jmp SetNextItem;
IsArmor:
mov dword ptr ds:[FO_VAR_i_worn], eax; // set armor
SetNextItem:
inc ebx; // itemNum++
add edx, 8; // itemListOffset + itemsize
LoopStart:
mov eax, dword ptr ds:[FO_VAR_pud]; // PC inventory
cmp ebx, dword ptr ds:[eax]; // size of item list
jl CheckNextItem;
// inventory function - setup pc FrmID and store at address _i_fid
call adjust_fid_;
// copy new FrmID to hero state struct
mov edx, dword ptr ds:[FO_VAR_i_fid];
mov eax, dword ptr ds:[FO_VAR_inven_dude];
mov dword ptr ds:[eax + 0x20], edx;
//call obj_change_fid_;
pop dword ptr ds:[FO_VAR_i_worn]; // armor
pop dword ptr ds:[FO_VAR_i_lhand]; // item1
pop dword ptr ds:[FO_VAR_i_rhand]; // item2
pop dword ptr ds:[FO_VAR_inven_dude];
if (item->flags & ObjectFlag::Right_Hand) {
*ptr_i_rhand = item;
} else if (item->flags & ObjectFlag::Left_Hand) {
*ptr_i_lhand = item;
} else if (item->flags & ObjectFlag::Worn) {
*ptr_i_worn = item;
}
}
// inventory function - setup pc FrmID and store at address _i_fid
(*ptr_obj_dude)->artFid = Inventory_adjust_fid(); // adjust_fid_
*ptr_inven_dude = iD;
*ptr_i_rhand = iR;
*ptr_i_lhand = iL;
*ptr_i_worn = iW;
}
void __stdcall RefreshPCArt() {
fo_proto_dude_update_gender(); // refresh PC base model art
UpdateHeroArt();
DrawPC();
}
@@ -1462,7 +1423,7 @@ static void EnableHeroAppearanceMod() {
MakeCall(0x49F9DA, AdjustHeroBaseArt);
// Adjust hero art index offset when changing armor (adjust_fid_)
HookCall(0x4717D1, AdjustHeroArmorArt);
/* HookCall(0x4717D1, AdjustHeroArmorArt); - the engine function is replaced by the sfall code */
// Hijack Save Hero State Structure fuction address 9CD54800
// Return hero art index offset back to normal before saving
+4
View File
@@ -18,9 +18,13 @@
#pragma once
extern bool appModEnabled;
void HeroAppearance_Init();
void HeroAppearance_Exit();
void __stdcall AdjustHeroArmorArt(DWORD fid);
void __stdcall HeroSelectWindow(int raceStyleFlag);
void __stdcall SetHeroStyle(int newStyleVal);
void __stdcall SetHeroRace(int newRaceVal);
+17 -1
View File
@@ -1586,6 +1586,21 @@ long __stdcall CorrectFidForRemovedItem_wHook(TGameObj* critter, TGameObj* item,
return result;
}
// 4.x backport
void __stdcall AdjustFidHook(DWORD vanillaFid) {
BeginHook();
argCount = 2;
args[0] = vanillaFid;
args[1] = *ptr_i_fid; // modified FID by sfall code
RunHookScript(HOOK_ADJUSTFID);
if (cRet > 0) {
*ptr_i_fid = rets[0];
}
EndHook();
}
// 4.x backport
static unsigned long previousGameMode = 0;
@@ -1635,7 +1650,7 @@ void __stdcall SetHSReturn(DWORD value) {
}
void __stdcall RegisterHook(TProgram* script, int id, int procNum, bool specReg) {
if (id >= numHooks || (id > HOOK_INVENWIELD && id < HOOK_GAMEMODECHANGE)) return;
if (id >= numHooks || (id > HOOK_ADJUSTFID && id < HOOK_GAMEMODECHANGE)) return;
for (std::vector<sHookScript>::iterator it = hooks[id].begin(); it != hooks[id].end(); ++it) {
if (it->prog.ptr == script) {
if (procNum == 0) hooks[id].erase(it); // unregister
@@ -1873,6 +1888,7 @@ static void HookScriptInit() {
HookCall(0x45C4F6, op_move_obj_inven_to_obj_hook);
MakeCall(0x4778AF, item_drop_all_hack, 3);
LoadHookScript("hs_adjustfid", HOOK_ADJUSTFID);
LoadHookScript("hs_gamemodechange", HOOK_GAMEMODECHANGE);
fo_db_free_file_list(&filenames, 0);
+5 -1
View File
@@ -46,6 +46,7 @@ enum HookType
HOOK_WITHINPERCEPTION = 23,
HOOK_INVENTORYMOVE = 24,
HOOK_INVENWIELD = 25,
HOOK_ADJUSTFID = 26, // 4.x backport
HOOK_GAMEMODECHANGE = 31, // 4.x backport
HOOK_COUNT
};
@@ -65,17 +66,20 @@ void HookScriptClear();
extern DWORD initingHookScripts;
int __fastcall AmmoCostHook_Script(DWORD hookType, TGameObj* weapon, DWORD &rounds);
void __stdcall RunHookScriptsAtProc(DWORD procId);
void __stdcall GameModeChangeHook(DWORD exit);
void __stdcall KeyPressHook(DWORD* dxKey, bool pressed, DWORD vKey);
void __stdcall MouseClickHook(DWORD button, bool pressed);
int __fastcall AmmoCostHook_Script(DWORD hookType, TGameObj* weapon, DWORD &rounds);
long __stdcall CorrectFidForRemovedItem_wHook(TGameObj* critter, TGameObj* item, long flags);
void InvenUnwield_HookDrop();
void InvenUnwield_HookMove();
void __stdcall AdjustFidHook(DWORD vanillaFid);
// Implementation of item_w_mp_cost_ engine function with the hook
long __fastcall sf_item_w_mp_cost(TGameObj* source, long hitMode, long isCalled);
+81 -10
View File
@@ -20,8 +20,11 @@
#include "main.h"
#include "FalloutEngine.h"
#include "HeroAppearance.h"
#include "HookScripts.h"
#include "InputFuncs.h"
#include "LoadGameHook.h"
#include "PartyControl.h"
#include "Inventory.h"
@@ -58,9 +61,9 @@ void InventoryKeyPressedHook(DWORD dxKey, bool pressed) {
}
}
/////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
DWORD __stdcall sf_item_total_size(TGameObj* critter) {
DWORD __stdcall Inventory_item_total_size(TGameObj* critter) {
int totalSize = fo_item_c_curr_size(critter);
if (critter->TypeFid() == OBJ_TYPE_CRITTER) {
@@ -82,6 +85,70 @@ DWORD __stdcall sf_item_total_size(TGameObj* critter) {
return totalSize;
}
// Reimplementation of adjust_fid engine function
// Differences from vanilla:
// - doesn't use art_vault_guy_num as default art, uses current critter FID instead
// - calls AdjustFidHook that allows to hook into FID calculation
DWORD __stdcall Inventory_adjust_fid() {
DWORD fid;
if ((*ptr_inven_dude)->TypeFid() == OBJ_TYPE_CRITTER) {
DWORD indexNum;
DWORD weaponAnimCode = 0;
if (PartyControl_IsNpcControlled()) {
// if NPC is under control, use current FID of critter
indexNum = (*ptr_inven_dude)->artFid & 0xFFF;
} else {
// vanilla logic:
indexNum = *ptr_art_vault_guy_num;
auto critterPro = GetProto(*ptr_inven_pid);
if (critterPro != nullptr) {
indexNum = critterPro->fid & 0xFFF;
}
if (*ptr_i_worn != nullptr) {
auto armorPro = GetProto((*ptr_i_worn)->protoId);
DWORD armorFid = fo_stat_level(*ptr_inven_dude, STAT_gender) == GENDER_FEMALE
? armorPro->item.armor.femaleFID
: armorPro->item.armor.maleFID;
if (armorFid != -1) {
indexNum = armorFid;
}
}
}
auto itemInHand = fo_intface_is_item_right_hand()
? *ptr_i_rhand
: *ptr_i_lhand;
if (itemInHand != nullptr) {
auto itemPro = GetProto(itemInHand->protoId);
if (itemPro->item.type == item_type_weapon) {
weaponAnimCode = itemPro->item.weapon.animationCode;
}
}
fid = fo_art_id(OBJ_TYPE_CRITTER, indexNum, 0, weaponAnimCode, 0);
} else {
fid = (*ptr_inven_dude)->artFid;
}
*ptr_i_fid = fid;
// OnAdjustFid
if (appModEnabled) AdjustHeroArmorArt(fid);
AdjustFidHook(fid); // should be called last
return *ptr_i_fid;
}
static void __declspec(naked) adjust_fid_hack() {
__asm {
push ecx;
push edx;
call Inventory_adjust_fid; // return fid
pop edx;
pop ecx;
retn;
}
}
////////////////////////////////////////////////////////////////////////////////
static int __stdcall CritterGetMaxSize(TGameObj* critter) {
if (critter == *ptr_obj_dude) return invSizeMaxLimit;
@@ -108,7 +175,7 @@ static __declspec(naked) void critterIsOverloaded_hack() {
jz skip;
push ebx;
mov ebx, eax; // ebx = MaxSize
call sf_item_total_size;
call Inventory_item_total_size;
cmp eax, ebx;
setg al; // if CurrSize > MaxSize
and eax, 0xFF;
@@ -122,8 +189,8 @@ end:
static int __fastcall CanAddedItems(TGameObj* critter, TGameObj* item, int count) {
int sizeMax = CritterGetMaxSize(critter);
if (sizeMax > 0) {
int itemsSize = fo_item_size(item) * count;
if (itemsSize + (int)sf_item_total_size(critter) > sizeMax) return -6; // TODO: Switch this to a lower number, and add custom error messages.
int totalSize = Inventory_item_total_size(critter) + (fo_item_size(item) * count);
if (totalSize > sizeMax) return -6; // TODO: Switch this to a lower number, and add custom error messages.
}
return 0;
}
@@ -168,10 +235,10 @@ static int __fastcall BarterAttemptTransaction(TGameObj* critter, TGameObj* tabl
int size = CritterGetMaxSize(critter);
if (size == 0) return 1;
int sizeTable = sf_item_total_size(table);
int sizeTable = Inventory_item_total_size(table);
if (sizeTable == 0) return 1;
size -= sf_item_total_size(critter);
size -= Inventory_item_total_size(critter);
return (sizeTable <= size) ? 1 : 0;
}
@@ -246,7 +313,7 @@ static void __cdecl DisplaySizeStats(TGameObj* critter, const char* &message, DW
}
sizeMax = limitMax;
size = sf_item_total_size(critter);
size = Inventory_item_total_size(critter);
const char* msg = MsgSearch(ptr_inventry_message_file, 35);
message = (msg != nullptr) ? msg : "";
@@ -312,14 +379,15 @@ static void __declspec(naked) gdControlUpdateInfo_hack() {
call CritterGetMaxSize;
push eax; // sizeMax
push ebx;
call sf_item_total_size;
call Inventory_item_total_size;
push eax; // size
mov eax, ebx;
mov edx, STAT_carry_amt;
jmp ControlUpdateInfoRet;
}
}
/////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
static char superStimMsg[128];
static int __fastcall SuperStimFix(TGameObj* item, TGameObj* target) {
@@ -634,6 +702,9 @@ void InventoryReset() {
}
void Inventory_Init() {
// Replace adjust_fid_ function
MakeJump(adjust_fid_, adjust_fid_hack); // 0x4716E8
long widthWeight = 135;
sizeLimitMode = GetConfigInt("Misc", "CritterInvSizeLimitMode", 0);
+11 -1
View File
@@ -24,4 +24,14 @@ void InventoryKeyPressedHook(DWORD dxKey, bool pressed);
long __stdcall GetInvenApCost();
void __fastcall SetInvenApCost(int cost);
DWORD __stdcall sf_item_total_size(TGameObj* critter);
// This function returns the size of the occupied inventory space for the object or critter
// - difference from the item_c_curr_size_ function: includes the size of equipped items for the critter
// - does not return the size of nested items
DWORD __stdcall Inventory_item_total_size(TGameObj* critter);
// Reimplementation of adjust_fid engine function
// Differences from vanilla:
// - doesn't use art_vault_guy_num as default art, uses current critter FID instead
// - calls AdjustFidHook that allows to hook into FID calculation
DWORD __stdcall Inventory_adjust_fid();
+1 -1
View File
@@ -524,7 +524,7 @@ static void mf_set_unjam_locks_time() {
static void mf_get_current_inven_size() {
TGameObj* obj = opHandler.arg(0).asObject();
if (obj) {
opHandler.setReturn(sf_item_total_size(obj));
opHandler.setReturn(Inventory_item_total_size(obj));
} else {
OpcodeInvalidArgs("get_current_inven_size");
opHandler.setReturn(0);