From 6e10f624a3ff2df373005a945d3cbfd4e337ab3e Mon Sep 17 00:00:00 2001 From: NovaRain Date: Tue, 8 Feb 2022 22:30:06 +0800 Subject: [PATCH] Backported the code injection system for game hooks from 4.1 Split HookScripts.cpp/h to multiple sets in HookScripts\ as 4.x. Reverted tile_num to stdcall (commit a5f57d4) --- artifacts/ddraw.ini | 5 +- sfall/FalloutEngine/Functions.cpp | 7 +- sfall/FalloutEngine/Functions.h | 2 +- sfall/Game/combatAI.cpp | 2 +- sfall/Game/inventory.cpp | 3 +- sfall/Game/items.cpp | 4 +- sfall/Game/objects.cpp | 2 +- sfall/Game/tilemap.cpp | 2 +- sfall/InputFuncs.cpp | 2 +- sfall/Modules/BugFixes.cpp | 3 +- sfall/Modules/Combat.cpp | 15 +- sfall/Modules/HookScripts.cpp | 1959 +------------------ sfall/Modules/HookScripts.h | 39 +- sfall/Modules/HookScripts/CombatHs.cpp | 394 ++++ sfall/Modules/HookScripts/CombatHs.h | 22 + sfall/Modules/HookScripts/Common.cpp | 199 ++ sfall/Modules/HookScripts/Common.h | 52 + sfall/Modules/HookScripts/DeathHs.cpp | 165 ++ sfall/Modules/HookScripts/DeathHs.h | 14 + sfall/Modules/HookScripts/HexBlockingHs.cpp | 145 ++ sfall/Modules/HookScripts/HexBlockingHs.h | 15 + sfall/Modules/HookScripts/InventoryHs.cpp | 704 +++++++ sfall/Modules/HookScripts/InventoryHs.h | 24 + sfall/Modules/HookScripts/MiscHs.cpp | 271 +++ sfall/Modules/HookScripts/MiscHs.h | 15 + sfall/Modules/HookScripts/ObjectHs.cpp | 136 ++ sfall/Modules/HookScripts/ObjectHs.h | 13 + sfall/Modules/LoadGameHook.cpp | 2 +- sfall/Modules/ScriptExtender.cpp | 2 +- sfall/Modules/Scripting/Handlers/Core.cpp | 2 +- sfall/ddraw.vcxproj | 14 + sfall/ddraw.vcxproj.filters | 45 + 32 files changed, 2360 insertions(+), 1919 deletions(-) create mode 100644 sfall/Modules/HookScripts/CombatHs.cpp create mode 100644 sfall/Modules/HookScripts/CombatHs.h create mode 100644 sfall/Modules/HookScripts/Common.cpp create mode 100644 sfall/Modules/HookScripts/Common.h create mode 100644 sfall/Modules/HookScripts/DeathHs.cpp create mode 100644 sfall/Modules/HookScripts/DeathHs.h create mode 100644 sfall/Modules/HookScripts/HexBlockingHs.cpp create mode 100644 sfall/Modules/HookScripts/HexBlockingHs.h create mode 100644 sfall/Modules/HookScripts/InventoryHs.cpp create mode 100644 sfall/Modules/HookScripts/InventoryHs.h create mode 100644 sfall/Modules/HookScripts/MiscHs.cpp create mode 100644 sfall/Modules/HookScripts/MiscHs.h create mode 100644 sfall/Modules/HookScripts/ObjectHs.cpp create mode 100644 sfall/Modules/HookScripts/ObjectHs.h diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index b3b85dc8..a01a0276 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -321,7 +321,7 @@ Movie17=credits.mve ;To change the limit of the distance away from the player to which you're allowed to scroll the local maps, uncomment the next two lines ;Defaults are 480 in the x direction and 400 in the y direction. -;Not compatible with the res patch! +;Not compatible with the hi-res patch! ;LocalMapXLimit=480 ;LocalMapYLimit=400 @@ -852,6 +852,9 @@ MapGridToggleKey=0 ;Has pretty nasty side effects when saving/reloading, so don't use for regular gameplay DontDeleteProtos=0 +;Set to 1 to force sfall to inject all hooks code into the game, even if corresponding hook scripts don't exist +InjectAllGameHooks=0 + ;Set to 1 to force sfall to search for global/hook scripts every time the game loads rather than only the first time AlwaysFindScripts=0 diff --git a/sfall/FalloutEngine/Functions.cpp b/sfall/FalloutEngine/Functions.cpp index 6286baab..36f9bcf5 100644 --- a/sfall/FalloutEngine/Functions.cpp +++ b/sfall/FalloutEngine/Functions.cpp @@ -150,9 +150,10 @@ long __fastcall db_init(const char* path_dat, const char* path_patches) { WRAP_WATCOM_FCALL2(db_init_, path_dat, path_patches); } -long __fastcall tile_num(long x, long y) { - __asm xor ebx, ebx; // don't delete (bug in tile_num_) - WRAP_WATCOM_FCALL2(tile_num_, x, y); +long __stdcall tile_num(long x, long y) { + __asm push ebx; // don't delete (bug in tile_num_) + WRAP_WATCOM_CALL2(tile_num_, x, y); + __asm pop ebx; } GameObject* __fastcall obj_blocking_at_wrapper(GameObject* obj, DWORD tile, DWORD elevation, void* func) { diff --git a/sfall/FalloutEngine/Functions.h b/sfall/FalloutEngine/Functions.h index 1eb4fff4..10edd5fa 100644 --- a/sfall/FalloutEngine/Functions.h +++ b/sfall/FalloutEngine/Functions.h @@ -52,7 +52,7 @@ void __declspec() interpretError(const char* fmt, ...); long __fastcall db_init(const char* path_dat, const char* path_patches); -long __fastcall tile_num(long x, long y); +long __stdcall tile_num(long x, long y); GameObject* __fastcall obj_blocking_at_wrapper(GameObject* obj, DWORD tile, DWORD elevation, void* func); diff --git a/sfall/Game/combatAI.cpp b/sfall/Game/combatAI.cpp index b49e5a8e..09a34cd4 100644 --- a/sfall/Game/combatAI.cpp +++ b/sfall/Game/combatAI.cpp @@ -7,7 +7,7 @@ #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" -#include "..\Modules\HookScripts.h" +#include "..\Modules\HookScripts\CombatHs.h" #include "items.h" diff --git a/sfall/Game/inventory.cpp b/sfall/Game/inventory.cpp index a1a97e4f..925fe0b3 100644 --- a/sfall/Game/inventory.cpp +++ b/sfall/Game/inventory.cpp @@ -8,9 +8,10 @@ #include "..\FalloutEngine\Fallout2.h" #include "..\Modules\HeroAppearance.h" -#include "..\Modules\HookScripts.h" #include "..\Modules\PartyControl.h" +#include "..\Modules\HookScripts\InventoryHs.h" + #include "inventory.h" namespace game diff --git a/sfall/Game/items.cpp b/sfall/Game/items.cpp index 7d7ac902..513eedd8 100644 --- a/sfall/Game/items.cpp +++ b/sfall/Game/items.cpp @@ -9,7 +9,9 @@ #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" -#include "..\Modules\HookScripts.h" +#include "..\Modules\HookScripts\CombatHs.h" +#include "..\Modules\HookScripts\InventoryHs.h" +#include "..\Modules\HookScripts\ObjectHs.h" #include "..\Modules\Perks.h" #include "..\Modules\Unarmed.h" diff --git a/sfall/Game/objects.cpp b/sfall/Game/objects.cpp index 78a3b94e..84ab589b 100644 --- a/sfall/Game/objects.cpp +++ b/sfall/Game/objects.cpp @@ -7,7 +7,7 @@ #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" -#include "..\Modules\HookScripts.h" +#include "..\Modules\HookScripts\MiscHs.h" #include "objects.h" diff --git a/sfall/Game/tilemap.cpp b/sfall/Game/tilemap.cpp index 942f1b91..76bea8c5 100644 --- a/sfall/Game/tilemap.cpp +++ b/sfall/Game/tilemap.cpp @@ -16,7 +16,7 @@ namespace sf = sfall; //static std::vector buildLineTiles; -//static bool __stdcall TileExists(long tile) { +//static bool TileExists(long tile) { // return (std::find(buildLineTiles.cbegin(), buildLineTiles.cend(), tile) != buildLineTiles.cend()); //} diff --git a/sfall/InputFuncs.cpp b/sfall/InputFuncs.cpp index 2966be56..838f2fde 100644 --- a/sfall/InputFuncs.cpp +++ b/sfall/InputFuncs.cpp @@ -27,7 +27,7 @@ #include "FalloutEngine\Fallout2.h" #include "Modules\DebugEditor.h" #include "Modules\Graphics.h" -#include "Modules\HookScripts.h" +#include "Modules\HookScripts\Common.h" #include "Modules\Inventory.h" #include "Modules\ScriptExtender.h" diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index 9a81d91d..043d4721 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -2,9 +2,10 @@ #include "..\FalloutEngine\Fallout2.h" #include "..\Translate.h" +#include "HookScripts\InventoryHs.h" + #include "..\Game\objects.h" -#include "HookScripts.h" #include "LoadGameHook.h" #include "ScriptExtender.h" #include "Worldmap.h" diff --git a/sfall/Modules/Combat.cpp b/sfall/Modules/Combat.cpp index 7ea6fa61..7fa7f089 100644 --- a/sfall/Modules/Combat.cpp +++ b/sfall/Modules/Combat.cpp @@ -20,10 +20,11 @@ #include "..\FalloutEngine\Fallout2.h" #include "..\SimplePatch.h" #include "..\Translate.h" - #include "HookScripts.h" #include "Objects.h" +#include "HookScripts\CombatHS.h" + #include "Combat.h" namespace sfall @@ -193,7 +194,11 @@ long __fastcall Combat::check_item_ammo_cost(fo::GameObject* weapon, fo::AttackT DWORD newRounds = rounds; - AmmoCostHook_Script(1, weapon, newRounds); // newRounds returns the new "ammo" value multiplied by the cost + if (HookScripts::IsInjectHook(HOOK_AMMOCOST)) { + AmmoCostHook_Script(1, weapon, newRounds); // newRounds returns the new "ammo" value multiplied by the cost + } else if (rounds == 1) { // NOTE: it doesn't make sense to call item_w_compute_ammo_cost for rounds greater than one + fo::func::item_w_compute_ammo_cost(weapon, &newRounds); + } // calculate the cost long cost = (newRounds != rounds) ? newRounds / rounds : 1; // 1 - default cost @@ -253,8 +258,10 @@ tryAttack: static long __fastcall divide_burst_rounds_by_ammo_cost(long currAmmo, fo::GameObject* weapon, long burstRounds) { DWORD roundsCost = 1; // default burst cost - roundsCost = burstRounds; // rounds in burst (the number of rounds fired in the burst) - AmmoCostHook_Script(2, weapon, roundsCost); // roundsCost returns the new cost + if (HookScripts::IsInjectHook(HOOK_AMMOCOST)) { + roundsCost = burstRounds; // rounds in burst (the number of rounds fired in the burst) + AmmoCostHook_Script(2, weapon, roundsCost); // roundsCost returns the new cost + } long cost = burstRounds * roundsCost; // amount of ammo required for this burst (multiplied by 1 or by the value returned from HOOK_AMMOCOST) if (cost > currAmmo) cost = currAmmo; // if cost ammo more than current ammo, set it to current diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index 8761967c..f43c7a04 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -21,11 +21,15 @@ #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" -#include "..\Logging.h" -#include "Combat.h" #include "LoadGameHook.h" -#include "PartyControl.h" -#include "ScriptExtender.h" + +#include "HookScripts\Common.h" +#include "HookScripts\CombatHs.h" +#include "HookScripts\DeathHs.h" +#include "HookScripts\HexBlockingHs.h" +#include "HookScripts\InventoryHs.h" +#include "HookScripts\ObjectHs.h" +#include "HookScripts\MiscHs.h" #include "HookScripts.h" @@ -35,1681 +39,73 @@ namespace sfall // Number of types of hooks static const int numHooks = HOOK_COUNT; +static bool injectAllHooks; + DWORD HookScripts::initingHookScripts; -// Struct for registered hook script -struct HookScript { - ScriptProgram prog; - int callback; // procedure position in script's proc table - bool isGlobalScript; // false for hs_* scripts, true for gl* scripts -}; - -static std::vector hooks[numHooks]; - -static const int maxArgs = 16; // Maximum number of hook arguments -static const int maxRets = 8; // Maximum number of return values -static const int maxDepth = 8; // Maximum recursion depth for hook calls - -struct { - DWORD hookID; - DWORD argCount; - DWORD cArg; - DWORD cRet; - DWORD cRetTmp; - DWORD oldArgs[maxArgs]; - DWORD oldRets[maxRets]; -} savedArgs[maxDepth]; - -static DWORD callDepth; -static DWORD currentRunHook = -1; - -static DWORD args[maxArgs]; // current hook arguments -static DWORD rets[maxRets]; // current hook return values - -static DWORD argCount; -static DWORD cArg; // how many arguments were taken by current hook script -static DWORD cRet; // how many return values were set by current hook script -static DWORD cRetTmp; // how many return values were set by specific hook script (when using register_hook) - -struct HookFile { - int id; -// std::string filePath; - std::string name; -}; - static std::vector hookScriptFilesList; +typedef void(*HookInjectFunc)(); + +struct HooksInjectInfo { + int id; + HookInjectFunc inject; + bool injectState; +}; + static struct HooksPositionInfo { long hsPosition; // index of the hs_* script, or the beginning of the position for registering scripts using register_hook // long positionShift; // offset to the last script registered by register_hook bool hasHsScript; + + HooksPositionInfo() : hsPosition(0), /*positionShift(0),*/ hasHsScript(false) {} } hooksInfo[numHooks]; -#define HookBegin pushadc __asm call BeginHook popadc -#define HookEnd pushadc __asm call EndHook popadc - -DWORD HookCommon::GetHSArgCount() { - return argCount; -} - -DWORD HookCommon::GetHSArg() { - return (cArg == argCount) ? 0 : args[cArg++]; -} - -void HookCommon::SetHSArg(DWORD id, DWORD value) { - if (id < argCount) args[id] = value; -} - -DWORD* HookCommon::GetHSArgs() { - return args; -} - -DWORD HookCommon::GetHSArgAt(DWORD id) { - return args[id]; -} - -void __stdcall HookCommon::SetHSReturn(DWORD value) { - if (cRetTmp < maxRets) { - rets[cRetTmp++] = value; - } - if (cRetTmp > cRet) { - cRet = cRetTmp; - } -} - -static void __stdcall BeginHook() { - if (callDepth && callDepth <= maxDepth) { - // save all values of the current hook if another hook was called during the execution of the current hook - int cDepth = callDepth - 1; - savedArgs[cDepth].hookID = currentRunHook; - savedArgs[cDepth].argCount = argCount; // number of arguments of the current hook - savedArgs[cDepth].cArg = cArg; // current count of taken arguments - savedArgs[cDepth].cRet = cRet; // number of return values for the current hook - savedArgs[cDepth].cRetTmp = cRetTmp; - std::memcpy(&savedArgs[cDepth].oldArgs, args, maxArgs * sizeof(DWORD)); // values of the arguments - if (cRet) std::memcpy(&savedArgs[cDepth].oldRets, rets, maxRets * sizeof(DWORD)); // return values - - //devlog_f("\nSaved cArgs/cRet: %d / %d(%d)\n", DL_HOOK, savedArgs[cDepth].argCount, savedArgs[cDepth].cRet, cRetTmp); - //for (unsigned int i = 0; i < maxArgs; i++) { - // devlog_f("Saved Args/Rets: %d / %d\n", DL_HOOK, savedArgs[cDepth].oldArgs[i], ((i < maxRets) ? savedArgs[cDepth].oldRets[i] : -1)); - //} - } - callDepth++; - - devlog_f("Begin running hook, current depth: %d, current executable hook: %d\n", DL_HOOK, callDepth, currentRunHook); -} - -static void __stdcall RunSpecificHookScript(HookScript *hook) { - cArg = 0; - cRetTmp = 0; - if (hook->callback != -1) { - fo::func::executeProcedure(hook->prog.ptr, hook->callback); - } else { - hook->callback = RunScriptStartProc(&hook->prog); // run start - } -} - -static void __stdcall RunHookScript(DWORD hook) { - cRet = 0; - if (!hooks[hook].empty()) { - if (callDepth > 8) { - fo::func::debug_printf("\n[SFALL] The hook ID: %d cannot be executed.", hook); - dlog_f("The hook %d cannot be executed due to exceeding depth limit\n", DL_MAIN, hook); - return; - } - currentRunHook = hook; - size_t hooksCount = hooks[hook].size(); - dlog_f("Running hook %d, which has %0d entries attached, depth: %d\n", DL_HOOK, hook, hooksCount, callDepth); - for (int i = hooksCount - 1; i >= 0; i--) { - RunSpecificHookScript(&hooks[hook][i]); - - //devlog_f("> Hook: %d, script entry: %d done\n", DL_HOOK, hook, i); - //devlog_f("> Check cArg/cRet: %d / %d(%d)\n", DL_HOOK, cArg, cRet, cRetTmp); - //for (unsigned int i = 0; i < maxArgs; i++) { - // devlog_f("> Check Args/Rets: %d / %d\n", DL_HOOK, args[i], ((i < maxRets) ? rets[i] : -1)); - //} - } - } else { - cArg = 0; - - devlog_f(">>> Try running hook ID: %d\n", DL_HOOK, hook); - } -} - -static void __stdcall EndHook() { - devlog_f("End running hook %d, current depth: %d\n", DL_HOOK, currentRunHook, callDepth); - - callDepth--; - if (callDepth) { - if (callDepth <= maxDepth) { - // restore all saved values of the previous hook - int cDepth = callDepth - 1; - currentRunHook = savedArgs[cDepth].hookID; - argCount = savedArgs[cDepth].argCount; - cArg = savedArgs[cDepth].cArg; - cRet = savedArgs[cDepth].cRet; - cRetTmp = savedArgs[cDepth].cRetTmp; // also restore current count of the number of return values - std::memcpy(args, &savedArgs[cDepth].oldArgs, maxArgs * sizeof(DWORD)); - if (cRet) std::memcpy(rets, &savedArgs[cDepth].oldRets, maxRets * sizeof(DWORD)); - - //devlog_f("Restored cArgs/cRet: %d / %d(%d)\n", DL_HOOK, argCount, cRet, cRetTmp); - //for (unsigned int i = 0; i < maxArgs; i++) { - // devlog_f("Restored Args/Rets: %d / %d\n", DL_HOOK, args[i], ((i < maxRets) ? rets[i] : -1)); - //} - } - } else { - currentRunHook = -1; - } -} - -// BEGIN HOOKS -static void __declspec(naked) ToHitHook() { - __asm { - HookBegin; - mov args[4], eax; // attacker - mov args[8], ebx; // target - mov args[12], ecx; // body part - mov args[16], edx; // source tile - mov eax, [esp + 8]; - mov args[24], eax; // is ranged - push eax; - mov eax, [esp + 8]; - mov args[20], eax; // attack type - push eax; - mov eax, args[4]; // restore - call fo::funcoffs::determine_to_hit_func_; - mov args[0], eax; - mov ebx, eax; - } - argCount = 8; - - args[7] = Combat::determineHitChance; - RunHookScript(HOOK_TOHIT); - - __asm { - cmp cRet, 1; - cmovge ebx, rets[0]; - call EndHook; - mov eax, ebx; - retn 8; - } -} - -static DWORD __fastcall AfterHitRollHook_Script(fo::ComputeAttackResult &ctd, DWORD hitChance, DWORD hit) { - BeginHook(); - argCount = 5; - - args[0] = hit; - args[1] = (DWORD)ctd.attacker; // Attacker - args[2] = (DWORD)ctd.target; // Target - args[3] = ctd.bodyPart; // bodypart - args[4] = hitChance; - - RunHookScript(HOOK_AFTERHITROLL); - if (cRet > 0) { - hit = rets[0]; - if (cRet > 1) { - ctd.bodyPart = rets[1]; - if (cRet > 2) ctd.target = (fo::GameObject*)rets[2]; - } - } - EndHook(); - - return hit; -} - -static void __declspec(naked) AfterHitRollHook() { - using namespace fo; - __asm { - mov ecx, esi; // ctd - mov edx, [esp + 0x18 + 4]; // hit chance - push eax; // was it a hit? - call AfterHitRollHook_Script; - // engine code - mov ebx, eax; - cmp eax, ROLL_FAILURE; - retn; - } -} - -static long __stdcall CalcApCostHook_Script(fo::GameObject* source, long hitMode, long isCalled, long cost, fo::GameObject* weapon) { - BeginHook(); - argCount = 5; - - args[0] = (DWORD)source; - args[1] = hitMode; - args[2] = isCalled; - args[3] = cost; - args[4] = (DWORD)weapon; - - RunHookScript(HOOK_CALCAPCOST); - - if (cRet > 0) cost = rets[0]; - EndHook(); - - return cost; -} - -long __stdcall CalcApCostHook_Invoke(fo::GameObject* source, long hitMode, long isCalled, long cost, fo::GameObject* weapon) { - return (HookScripts::HookHasScript(HOOK_CALCAPCOST)) - ? CalcApCostHook_Script(source, hitMode, isCalled, cost, weapon) - : cost; -} -/* -static void __declspec(naked) CalcApCostHook() { - __asm { - HookBegin; - mov args[0], eax; - mov args[4], edx; - mov args[8], ebx; - call fo::funcoffs::item_w_mp_cost_; - mov args[12], eax; - mov ebx, eax; - push ecx; - } - - argCount = 5; - args[4] = 0; - - RunHookScript(HOOK_CALCAPCOST); - - __asm { - cmp cRet, 1; - cmovge ebx, rets[0]; - call EndHook; - mov eax, ebx; - pop ecx; - retn; - } -} -*/ -// this is for using non-weapon items, always 2 AP in vanilla -static void __declspec(naked) CalcApCostHook2() { - __asm { - HookBegin; - mov args[0], ecx; // critter - mov args[4], edx; // attack type (to determine hand) - mov args[8], ebx; - mov ebx, 2; // vanilla cost value - mov args[12], ebx; - //push ecx; - } - - argCount = 5; - args[4] = 0; - - RunHookScript(HOOK_CALCAPCOST); - - __asm { - cmp cRet, 1; - cmovge ebx, rets[0]; - call EndHook; - mov eax, ebx; - //pop ecx; - retn; - } -} - -static DWORD __fastcall CalcDeathAnimHook_Script(DWORD damage, fo::GameObject* target, fo::GameObject* attacker, fo::GameObject* weapon, int animation, int hitBack) { - BeginHook(); - argCount = 5; // was 4 - - args[1] = (DWORD)attacker; - args[2] = (DWORD)target; - args[3] = damage; - - // weapon_ptr - args[0] = (weapon) ? weapon->protoId : -1; // attack is unarmed - - bool createNewObj = false; - args[4] = -1; // set 'no anim' for combined hooks use - RunHookScript(HOOK_DEATHANIM1); - if (cRet > 0) { - DWORD pid = rets[0]; - args[0] = pid; // replace for HOOK_DEATHANIM2 - fo::GameObject* object = nullptr; - if (fo::func::obj_pid_new((fo::GameObject*)&object, pid) != -1) { // create new object - createNewObj = true; - weapon = object; // replace pointer with newly created weapon object - } - cRet = 0; // reset rets from HOOK_DEATHANIM1 - } - - DWORD animDeath = fo::func::pick_death(attacker, target, weapon, damage, animation, hitBack); // vanilla pick death - - args[4] = animDeath; - RunHookScript(HOOK_DEATHANIM2); - - if (cRet > 0) animDeath = rets[0]; - EndHook(); - - if (createNewObj) fo::func::obj_erase_object(weapon, 0); // delete created object - - return animDeath; -} - -static void __declspec(naked) CalcDeathAnimHook() { - __asm { - push ecx; - push edx; - push [esp + 4 + 12]; // hit_from_back - push [esp + 4 + 12]; // animation - push ebx; // weapon_ptr - push eax; // attacker - call CalcDeathAnimHook_Script; // ecx - damage, edx - target - pop edx; - pop ecx; - retn 8; - } -} - -static void __declspec(naked) CalcDeathAnim2Hook() { - __asm { - call fo::funcoffs::check_death_; // call original function - mov ebx, eax; - call BeginHook; - mov eax, [esp + 60]; - mov args[4], eax; // attacker - mov args[8], esi; // target - mov eax, [esp + 12]; - mov args[12], eax; // dmgAmount - mov args[16], ebx; // calculated animID - } - - argCount = 5; - args[0] = -1; // weaponPid - RunHookScript(HOOK_DEATHANIM2); - - __asm { - cmp cRet, 1; - cmovge ebx, rets[0]; - call EndHook; - mov eax, ebx; - retn; - } -} - -static void __fastcall ComputeDamageHook_Script(fo::ComputeAttackResult &ctd, DWORD rounds, DWORD multiplier) { - BeginHook(); - argCount = 13; - - args[0] = (DWORD)ctd.target; // Target - args[1] = (DWORD)ctd.attacker; // Attacker - args[2] = ctd.targetDamage; // amountTarget - args[3] = ctd.attackerDamage; // amountSource - args[4] = ctd.targetFlags; // flagsTarget - args[5] = ctd.attackerFlags; // flagsSource - args[6] = (DWORD)ctd.weapon; - args[7] = ctd.bodyPart; - args[8] = multiplier; // damage multiplier - args[9] = rounds; // number of rounds - args[10] = ctd.knockbackValue; - args[11] = ctd.hitMode; // attack type - args[12] = (DWORD)&ctd; // main_ctd/shoot_ctd/explosion_ctd - - RunHookScript(HOOK_COMBATDAMAGE); - - if (cRet > 0) { - ctd.targetDamage = rets[0]; - if (cRet > 1) { - ctd.attackerDamage = rets[1]; - if (cRet > 2) { - ctd.targetFlags = rets[2]; // flagsTarget - if (cRet > 3) { - ctd.attackerFlags = rets[3]; // flagsSource - if (cRet > 4) ctd.knockbackValue = rets[4]; - } - } - } - } - EndHook(); -} - -static void __declspec(naked) ComputeDamageHook() { - __asm { - push ecx; - push ebx; // store dmg multiplier args[8] - push edx; // store num of rounds args[9] - push eax; // store ctd - call fo::funcoffs::compute_damage_; - pop ecx; // restore ctd (eax) - pop edx; // restore num of rounds - call ComputeDamageHook_Script; // stack - arg multiplier - pop ecx; - retn; - } -} - -static void __declspec(naked) OnDeathHook() { - using namespace fo::Fields; - __asm { - push edx; - call BeginHook; - mov args[0], esi; - } - - argCount = 1; - RunHookScript(HOOK_ONDEATH); - EndHook(); - - __asm { - pop edx; - // engine code - mov eax, [esi + protoId]; - mov ebp, ebx; - retn; - } -} - -static void __declspec(naked) OnDeathHook2() { - __asm { - call fo::funcoffs::partyMemberRemove_; - HookBegin; - mov args[0], esi; - pushadc; - } - - argCount = 1; - RunHookScript(HOOK_ONDEATH); - EndHook(); - - __asm popadc; - __asm retn; -} - -static void __fastcall FindTargetHook_Script(DWORD* target, fo::GameObject* attacker) { - BeginHook(); - argCount = 5; - - args[0] = (DWORD)attacker; - args[1] = target[0]; - args[2] = target[1]; - args[3] = target[2]; - args[4] = target[3]; - - RunHookScript(HOOK_FINDTARGET); - - if (cRet > 0) { - if (rets[0] != -1) target[0] = rets[0]; - if (cRet > 1 && rets[1] != -1) target[1] = rets[1]; - if (cRet > 2 && rets[2] != -1) target[2] = rets[2]; - if (cRet > 3 && rets[3] != -1) target[3] = rets[3]; - } - EndHook(); -} - -static void __declspec(naked) FindTargetHook() { - __asm { - push eax; - call fo::funcoffs::qsort_; - pop ecx; // targets (base) - mov edx, esi; // attacker - jmp FindTargetHook_Script; - } -} - -static long __stdcall UseObjOnHook_Script(fo::GameObject* source, fo::GameObject* item, fo::GameObject* target) { - BeginHook(); - argCount = 3; - - args[0] = (DWORD)target; // target - args[1] = (DWORD)source; // user - args[2] = (DWORD)item; // item - - RunHookScript(HOOK_USEOBJON); - - long result = (cRet > 0) ? rets[0] : -1; - EndHook(); - - return result; // -1 - default handler -} - -long __stdcall UseObjOnHook_Invoke(fo::GameObject* source, fo::GameObject* item, fo::GameObject* target) { - if (!HookScripts::HookHasScript(HOOK_USEOBJON)) return -1; - return UseObjOnHook_Script(source, item, target); -} - -static void __declspec(naked) UseObjOnHook() { - __asm { - HookBegin; - mov args[0], edx; // target - mov args[4], eax; // user - mov args[8], ebx; // object - pushadc; - } - - argCount = 3; - RunHookScript(HOOK_USEOBJON); - - __asm { - popadc; - cmp cRet, 1; - jl defaultHandler; - cmp rets[0], -1; - je defaultHandler; - mov eax, rets[0]; - HookEnd; - retn; -defaultHandler: - HookEnd; - jmp fo::funcoffs::protinst_use_item_on_; - } -} - -static void __declspec(naked) Drug_UseObjOnHook() { - __asm { - HookBegin; - mov args[0], eax; // target - mov args[4], eax; // user - mov args[8], edx; // object - pushadc; - } - - argCount = 3; - RunHookScript(HOOK_USEOBJON); - - __asm { - popadc; - cmp cRet, 1; - jl defaultHandler; - cmp rets[0], -1; - je defaultHandler; - mov eax, rets[0]; - HookEnd; - retn; -defaultHandler: - HookEnd; - jmp fo::funcoffs::item_d_take_drug_; - } -} - -static void __declspec(naked) UseObjHook() { - __asm { - HookBegin; - mov args[0], eax; // user - mov args[4], edx; // object - pushadc; - } - - argCount = 2; - RunHookScript(HOOK_USEOBJ); - - __asm { - popadc; - cmp cRet, 1; - jl defaultHandler; - cmp rets[0], -1; - je defaultHandler; - mov eax, rets[0]; - HookEnd; - retn; -defaultHandler: - HookEnd; - jmp fo::funcoffs::protinst_use_item_; - } -} -/* -static void RemoveInvenObjHook_Script(fo::GameObject* source, fo::GameObject* item, long count, long rmType) { - BeginHook(); - argCount = 5; - - args[0] = (DWORD)source; - args[1] = (DWORD)item; - args[2] = count; - args[3] = rmType; // RMOBJ_* - args[4] = 0; // target only from item_move_func_ - - RunHookScript(HOOK_REMOVEINVENOBJ); - EndHook(); -} - -void RemoveInvenObjHook_Invoke(fo::GameObject* source, fo::GameObject* item, long count, long rmType) { - if (HookScripts::HookHasScript(HOOK_REMOVEINVENOBJ)) RemoveInvenObjHook_Script(source, item, count, rmType); -} -*/ -static long rmObjType = -1; - -void __stdcall SetRemoveObjectType(long rmType) { - rmObjType = rmType; -} - -static void __declspec(naked) RemoveObjHook() { - static const DWORD RemoveObjHookRet = 0x477497; - __asm { - mov ecx, [esp + 8]; // call addr - cmp rmObjType, -1; - cmovne ecx, rmObjType; - mov rmObjType, -1; - HookBegin; - mov args[0], eax; // source - mov args[4], edx; // item - mov args[8], ebx; // count - mov args[12], ecx; // RMOBJ_* (called func) - xor esi, esi; - xor ecx, 0x47761D; // from item_move_func_ - cmovz esi, ebp; // target - mov args[16], esi; - push edi; - push ebp; - push eax; - push edx; - } - - argCount = 5; - RunHookScript(HOOK_REMOVEINVENOBJ); - EndHook(); - - __asm { - pop edx; - pop eax; - sub esp, 0x0C; - jmp RemoveObjHookRet; - } -} - -// 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(register fo::GameObject* source, register fo::GameObject* target, DWORD callAddr) { - bool barterIsParty = (*fo::ptr::dialog_target_is_party != 0); - long computeCost = fo::func::barter_compute_value(source, target); - - BeginHook(); - argCount = 10; - - args[0] = (DWORD)source; - args[1] = (DWORD)target; - args[2] = !barterIsParty ? computeCost : 0; - - fo::GameObject* bTable = (fo::GameObject*)*fo::ptr::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::ptr::ptable; - args[6] = (DWORD)pTable; - - long pcCost = 0; - if (barterIsParty) { - args[7] = pcCost; - pcCost = fo::func::item_total_weight(pTable); - } else { - args[7] = pcCost = fo::func::item_total_cost(pTable); - } - - args[8] = (DWORD)(callAddr == 0x474D51); // offers button pressed - args[9] = (DWORD)barterIsParty; - - RunHookScript(HOOK_BARTERPRICE); - - bool isPCHook = (callAddr == -1); - long cost = isPCHook ? pcCost : computeCost; - if (!barterIsParty && cRet > 0) { - if (isPCHook) { - if (cRet > 1) cost = rets[1]; // new cost for pc - } else if ((long)rets[0] > -1) { - cost = rets[0]; // new cost for npc - } - } - EndHook(); - return cost; -} - -static void __declspec(naked) BarterPriceHook() { - __asm { - push edx; - push ecx; - //------- - push [esp + 8]; // address on call stack - mov ecx, eax; // source - call BarterPriceHook_Script; // edx - target - pop ecx; - pop edx; - retn; - } -} - -static DWORD offersGoodsCost; // keep last cost for pc -static void __declspec(naked) PC_BarterPriceHook() { - __asm { - push edx; - push ecx; - //------- - push -1; // address on call stack - mov ecx, dword ptr ds:[FO_VAR_obj_dude]; // source - mov edx, dword ptr ds:[FO_VAR_target_stack]; // target - call BarterPriceHook_Script; - pop ecx; - pop edx; - mov offersGoodsCost, eax; - retn; - } -} - -static void __declspec(naked) OverrideCost_BarterPriceHook() { - __asm { - mov eax, offersGoodsCost; - retn; - } -} - -static void __declspec(naked) MoveCostHook() { - __asm { - HookBegin; - mov args[0], eax; - mov args[4], edx; - call fo::funcoffs::critter_compute_ap_from_distance_; - mov args[8], eax; - push ebx; - push ecx; - mov ebx, eax; - } - - argCount = 3; - RunHookScript(HOOK_MOVECOST); - - __asm { - cmp cRet, 1; - cmovge ebx, rets[0]; - call EndHook; - mov eax, ebx; - pop ecx; - pop ebx; - retn; - } -} - -static void __declspec(naked) HexMoveBlockingHook() { - static const DWORD _obj_blocking_at = 0x48B84E; - __asm { - HookBegin; - mov args[0], eax; - mov args[4], edx; - mov args[8], ebx; - push return; - // engine code - push ecx; - push esi; - push edi; - push ebp; - mov ecx, eax; - // end engine code - jmp _obj_blocking_at; -return: - mov args[12], eax; - mov ebx, eax; - push ecx; - } - - argCount = 4; - RunHookScript(HOOK_HEXMOVEBLOCKING); - - __asm { - cmp cRet, 1; - cmovge ebx, rets[0]; - call EndHook; - mov eax, ebx; - pop ecx; - retn; - } -} - -static void __declspec(naked) HexAIBlockingHook() { - __asm { - HookBegin; - mov args[0], eax; - mov args[4], edx; - mov args[8], ebx; - call fo::funcoffs::obj_ai_blocking_at_; - mov args[12], eax; - mov ebx, eax; - push ecx; - } - - argCount = 4; - RunHookScript(HOOK_HEXAIBLOCKING); - - __asm { - cmp cRet, 1; - cmovge ebx, rets[0]; - call EndHook; - mov eax, ebx; - pop ecx; - retn; - } -} - -static void __declspec(naked) HexShootBlockingHook() { - __asm { - HookBegin; - mov args[0], eax; - mov args[4], edx; - mov args[8], ebx; - call fo::funcoffs::obj_shoot_blocking_at_; - mov args[12], eax; - mov ebx, eax; - push ecx; - } - - argCount = 4; - RunHookScript(HOOK_HEXSHOOTBLOCKING); - - __asm { - cmp cRet, 1; - cmovge ebx, rets[0]; - call EndHook; - mov eax, ebx; - pop ecx; - retn; - } -} - -static void __declspec(naked) HexSightBlockingHook() { - __asm { - HookBegin; - mov args[0], eax; - mov args[4], edx; - mov args[8], ebx; - call fo::funcoffs::obj_sight_blocking_at_; - mov args[12], eax; - mov ebx, eax; - push ecx; - } - - argCount = 4; - RunHookScript(HOOK_HEXSIGHTBLOCKING); - - __asm { - cmp cRet, 1; - cmovge ebx, rets[0]; - call EndHook; - mov eax, ebx; - pop ecx; - retn; - } -} - -static void __declspec(naked) ItemDamageHook() { - __asm { - HookBegin; - mov args[0], eax; // min - mov args[4], edx; // max - mov args[8], edi; // weapon - mov args[12], ecx; // critter - mov args[16], esi; // type - mov args[20], ebp; // non-zero for weapon melee attack (add to min/max melee damage) - pushadc; - } - argCount = 6; - - // tweak for 0x4784AA (obsolete) - //if (args[2] == 0) { // weapon arg - // args[4] += 8; // type arg - //} - - RunHookScript(HOOK_ITEMDAMAGE); - - __asm popadc; - if (cRet > 0) { - __asm mov eax, rets[0]; // set min - if (cRet > 1) { - __asm mov edx, rets[4]; // set max - } else { - HookEnd; - __asm retn; // no calc random - } - } - HookEnd; - __asm jmp fo::funcoffs::roll_random_; -} - -int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD &rounds) { - int result = 0; - - BeginHook(); - argCount = 4; - - args[0] = (DWORD)weapon; - args[1] = rounds; // rounds in attack - args[3] = hookType; - - if (hookType == 2) { // burst hook - rounds = 1; // set default multiply for check burst attack - } else { - result = fo::func::item_w_compute_ammo_cost(weapon, &rounds); - if (result == -1) goto failed; // computation failed - } - args[2] = rounds; // rounds as computed by game (cost) - - RunHookScript(HOOK_AMMOCOST); - - if (cRet > 0) rounds = rets[0]; // override rounds - -failed: - EndHook(); - return result; -} - -static void __declspec(naked) AmmoCostHook() { - using namespace fo; - __asm { - xor ecx, ecx; // type of hook (0) - cmp dword ptr [esp + 0x1C + 4], ANIM_fire_burst; - jl skip; - cmp dword ptr [esp + 0x1C + 4], ANIM_fire_continuous; - jg skip; - mov ecx, 3; // hook type burst -skip: - xchg eax, edx; - push eax; // rounds in attack ref - call AmmoCostHook_Script; // edx - weapon - retn; - } -} - -void __stdcall HookCommon::KeyPressHook(DWORD* dxKey, bool pressed, DWORD vKey) { - if (!IsGameLoaded() || !HookScripts::HookHasScript(HOOK_KEYPRESS)) { - return; - } - BeginHook(); - argCount = 3; - args[0] = (DWORD)pressed; - args[1] = *dxKey; - args[2] = vKey; - RunHookScript(HOOK_KEYPRESS); - if (cRet != 0) { - long retKey = rets[0]; - if (retKey > 0 && retKey < 264) *dxKey = retKey; - } - EndHook(); -} - -void __stdcall HookCommon::MouseClickHook(DWORD button, bool pressed) { - if (!IsGameLoaded() || !HookScripts::HookHasScript(HOOK_MOUSECLICK)) { - return; - } - BeginHook(); - argCount = 2; - args[0] = (DWORD)pressed; - args[1] = button; - RunHookScript(HOOK_MOUSECLICK); - EndHook(); -} - -static void __declspec(naked) UseSkillHook() { - __asm { - HookBegin; - mov args[0], eax; // user - mov args[4], edx; // target - mov args[8], ebx; // skill id - mov args[12], ecx; // skill bonus - pushadc; - } - - argCount = 4; - RunHookScript(HOOK_USESKILL); - - __asm { - popadc; - cmp cRet, 1; - jl defaultHandler; - cmp rets[0], -1; - je defaultHandler; - mov eax, rets[0]; - HookEnd; - retn; -defaultHandler: - HookEnd; - jmp fo::funcoffs::skill_use_; - } -} - -static void __declspec(naked) StealCheckHook() { - __asm { - HookBegin; - mov args[0], eax; // thief - mov args[4], edx; // target - mov args[8], ebx; // item - mov args[12], ecx; // is planting - pushadc; - } - - argCount = 4; - RunHookScript(HOOK_STEAL); - - __asm { - popadc; - cmp cRet, 1; - jl defaultHandler; - cmp rets[0], -1; - je defaultHandler; - mov eax, rets[0]; - HookEnd; - retn; -defaultHandler: - HookEnd; - jmp fo::funcoffs::skill_check_stealing_; - } -} - -static __forceinline long __stdcall PerceptionRangeHook_Script(fo::GameObject* watcher, fo::GameObject* target, int type, long result) { - BeginHook(); - argCount = 4; - - args[0] = (DWORD)watcher; - args[1] = (DWORD)target; - args[2] = result; // engine result - args[3] = type; - - RunHookScript(HOOK_WITHINPERCEPTION); - - if (cRet > 0) result = rets[0]; - EndHook(); - - return result; -} - -long __stdcall PerceptionRangeHook_Invoke(fo::GameObject* watcher, fo::GameObject* target, long type, long result) { - if (!HookScripts::HookHasScript(HOOK_WITHINPERCEPTION)) return result; - return PerceptionRangeHook_Script(watcher, target, type, result); -} - -static long __fastcall PerceptionRange_Hook(fo::GameObject* watcher, fo::GameObject* target, int type) { - return PerceptionRangeHook_Script(watcher, target, type, fo::func::is_within_perception(watcher, target)); -} - -static void __declspec(naked) PerceptionRangeHook() { - __asm { - push ecx; - push 0; - mov ecx, eax; - call PerceptionRange_Hook; - pop ecx; - retn; - } -} - -static void __declspec(naked) PerceptionRangeSeeHook() { - __asm { - push ecx; - push 1; - mov ecx, eax; - call PerceptionRange_Hook; - pop ecx; - cmp eax, 2; - jne nevermind; // normal return - dec eax; - mov dword ptr [esp + 0x2C - 0x1C + 4], eax; // set 1, skip blocking check - dec eax; -nevermind: - retn; - } -} - -static void __declspec(naked) PerceptionRangeHearHook() { - __asm { - push ecx; - push 2; - mov ecx, eax; - call PerceptionRange_Hook; - pop ecx; - retn; - } -} - -static void __declspec(naked) PerceptionSearchTargetHook() { - __asm { - push ecx; - push 3; - mov ecx, eax; - call PerceptionRange_Hook; - pop ecx; - retn; - } -} - -static int __fastcall SwitchHandHook_Script(fo::GameObject* item, fo::GameObject* itemReplaced, DWORD addr) { - if (itemReplaced && fo::func::item_get_type(itemReplaced) == fo::item_type_weapon && fo::func::item_get_type(item) == fo::item_type_ammo) { - return -1; // to prevent inappropriate hook call after dropping ammo on weapon - } - - BeginHook(); - argCount = 3; - - args[0] = (addr < 0x47136D) ? 1 : 2; // slot: 1 - left, 2 - right - args[1] = (DWORD)item; - args[2] = (DWORD)itemReplaced; - - RunHookScript(HOOK_INVENTORYMOVE); - int result = PartyControl::SwitchHandHook(item); - if (result != -1) { - cRetTmp = 0; - HookCommon::SetHSReturn(result); - } - result = (cRet > 0) ? rets[0] : -1; - EndHook(); - - return result; -} - -/* - This hook is called every time an item is placed into either hand slot via inventory screen drag&drop - If switch_hand_ function is not called, item is not placed anywhere (it remains in main inventory) -*/ -static void __declspec(naked) SwitchHandHook() { - __asm { - pushadc; - mov ecx, eax; // item being moved - mov edx, [edx]; // other item - mov eax, [esp + 12]; // back address - push eax; - call SwitchHandHook_Script; - cmp eax, -1; // ret value - popadc; - jne skip; - jmp fo::funcoffs::switch_hand_; -skip: - retn; - } -} - -/* Common inventory move hook */ -static int __fastcall InventoryMoveHook_Script(DWORD itemReplace, DWORD item, int type) { - BeginHook(); - argCount = 3; - - args[0] = type; // event type - args[1] = item; // item being dropped - args[2] = itemReplace; // item being replaced here - - RunHookScript(HOOK_INVENTORYMOVE); - - int result = (cRet > 0) ? rets[0] : -1; // -1 - can move - EndHook(); - - return result; -} - -// This hack is called when an armor is dropped into the armor slot at inventory screen -static void __declspec(naked) UseArmorHack() { - static const DWORD UseArmorHack_back = 0x4713AF; // normal operation (old 0x4713A9) - static const DWORD UseArmorHack_skip = 0x471481; // skip code, prevent wearing armor - __asm { - mov ecx, ds:[FO_VAR_i_worn]; // replacement item (override code) - mov edx, [esp + 0x58 - 0x40]; // item - push ecx; - push 3; // event: armor slot - call InventoryMoveHook_Script; // ecx - replacement item - cmp eax, -1; // ret value - pop ecx; - jne skip; - jmp UseArmorHack_back; -skip: - jmp UseArmorHack_skip; - } -} - -static void __declspec(naked) MoveInventoryHook() { - __asm { - pushadc; - xor eax, eax; - mov ecx, eax; // no item replace - cmp dword ptr ds:[FO_VAR_curr_stack], 0; - jle noCont; - mov ecx, eax; // contaner ptr - mov eax, 5; -noCont: - push eax; // event: 0 - main backpack, 5 - contaner - call InventoryMoveHook_Script; // edx - item - cmp eax, -1; // ret value - popadc; - jne skip; - jmp fo::funcoffs::item_add_force_; -skip: - retn; - } -} - -// Hooks into dropping item from inventory to ground -// - allows to prevent item being dropped if 0 is returned with set_sfall_return -// - called for every item when dropping multiple items in stack (except caps) -// - when dropping caps it called always once -// - if 0 is returned while dropping caps, selected amount - 1 will still disappear from inventory (fixed) -static DWORD nextHookDropSkip = 0; -static int dropResult = -1; -static const DWORD InvenActionObjDropRet = 0x473874; -static void __declspec(naked) InvenActionCursorObjDropHook() { - if (nextHookDropSkip) { - nextHookDropSkip = 0; - goto skipHook; - } else { - __asm { - pushadc; - xor ecx, ecx; // no itemReplace - push 6; // event: item drop ground - call InventoryMoveHook_Script; // edx - item - mov dropResult, eax; // ret value - popadc; - cmp dword ptr [esp], 0x47379A + 5; // caps call address - jz capsMultiDrop; - } - } - - if (dropResult == -1) { -skipHook: - __asm call fo::funcoffs::obj_drop_; - } - __asm retn; - -/* for only caps multi drop */ -capsMultiDrop: - if (dropResult == -1) { - nextHookDropSkip = 1; - __asm call fo::funcoffs::item_remove_mult_; - __asm retn; - } - __asm add esp, 4; - __asm jmp InvenActionObjDropRet; // no caps drop -} - -static void __declspec(naked) InvenActionExplosiveDropHack() { - __asm { - pushadc; - xor ecx, ecx; // no itemReplace - push 6; // event: item drop ground - call InventoryMoveHook_Script; // edx - item - cmp eax, -1; // ret value - popadc; - jnz noDrop; - mov dword ptr ds:[FO_VAR_dropped_explosive], ebp; // overwritten engine code (ebp = 1) - mov nextHookDropSkip, ebp; - retn; -noDrop: - add esp, 4; - jmp InvenActionObjDropRet; // no drop - } -} - -static int __fastcall DropIntoContainer(DWORD ptrCont, DWORD item, DWORD addrCall) { - int type = 5; // event: move to container (Crafty compatibility) - - if (addrCall == 0x47147C + 5) { // drop out contaner - ptrCont = 0; - type = 0; // event: move to main backpack - } - return InventoryMoveHook_Script(ptrCont, item, type); -} - -static void __declspec(naked) DropIntoContainerHack() { - static const DWORD DropIntoContainer_back = 0x47649D; // normal operation - static const DWORD DropIntoContainer_skip = 0x476503; // exit drop_into_container_ - __asm { - test ecx, ecx; - js skipDrop; - push ecx; //pushadc; - mov edx, esi; // item - mov ecx, ebp; // contaner ptr - push [esp + 0x10 + 4]; // call address - call DropIntoContainer; - cmp eax, -1; // ret value - pop ecx; //popadc; - jne skipDrop; - jmp DropIntoContainer_back; -skipDrop: - mov eax, -1; - jmp DropIntoContainer_skip; - } -} - -static void __declspec(naked) DropIntoContainerHandSlotHack() { - static const DWORD DropIntoContainerRet = 0x471481; - __asm { - call fo::funcoffs::drop_into_container_; - jmp DropIntoContainerRet; - } -} - -static void __declspec(naked) DropAmmoIntoWeaponHook() { - static const DWORD DropAmmoIntoWeaponHack_return = 0x476643; - __asm { - pushadc; - mov ecx, ebp; // weapon ptr - mov edx, [esp + 16]; // item var: ammo_ - push 4; // event: weapon reloading - call InventoryMoveHook_Script; - cmp eax, -1; // ret value - jne donothing; - popadc; - jmp fo::funcoffs::item_w_can_reload_; -donothing: - add esp, 4*4; // destroy all pushed values and return address - xor eax, eax; // result 0 - jmp DropAmmoIntoWeaponHack_return; - } -} - -static void __declspec(naked) PickupObjectHack() { - __asm { - cmp edi, ds:[FO_VAR_obj_dude]; - je runHook; - xor edx, edx; // engine handler - retn; -runHook: - push eax; - push ecx; - mov edx, ecx; - xor ecx, ecx; // no itemReplace - push 7; // event: item pickup - call InventoryMoveHook_Script; // edx - item - mov edx, eax; // ret value - pop ecx; - pop eax; - inc edx; // 0 - engine handler, otherwise cancel pickup - retn; - } -} - -static void __declspec(naked) InvenPickupHook() { - __asm { - call fo::funcoffs::mouse_click_in_; - test eax, eax; - jnz runHook; - retn; -runHook: - cmp dword ptr ds:[FO_VAR_curr_stack], 0; - jnz skip; - mov edx, [esp + 0x58 - 0x40 + 4]; // item - xor ecx, ecx; // no itemReplace - push 8; // event: drop item on character portrait - call InventoryMoveHook_Script; - cmp eax, -1; // ret value - je skip; - xor eax, eax; // 0 - cancel, otherwise engine handler -skip: - retn; - } -} - -/* Common InvenWield script hooks */ -static long __fastcall InvenWieldHook_Script(fo::GameObject* critter, fo::GameObject* item, long slot, long isWield, long isRemove) { - if (!isWield) { - // for the critter, the right slot is always the active slot - if (slot == fo::INVEN_TYPE_LEFT_HAND && critter != *fo::ptr::obj_dude) return 1; - // check the current active slot for the player - if (slot != fo::INVEN_TYPE_WORN && critter == *fo::ptr::obj_dude) { - long _slot = (slot != fo::INVEN_TYPE_LEFT_HAND); - if (_slot != *fo::ptr::itemCurrentItem) return 1; // item in non-active slot - } - } - BeginHook(); - argCount = 5; - - args[0] = (DWORD)critter; - args[1] = (DWORD)item; - args[2] = slot; - args[3] = isWield; // unwield/wield event - args[4] = isRemove; - - RunHookScript(HOOK_INVENWIELD); - - long result = (cRet == 0 || rets[0] == -1); - EndHook(); - - return result; // 1 - use engine handler -} - -static __declspec(noinline) bool InvenWieldHook_ScriptPart(long isWield, long isRemove = 0) { - argCount = 5; - - args[3] = isWield; // unwield/wield event - args[4] = isRemove; - - RunHookScript(HOOK_INVENWIELD); - - bool result = (cRet == 0 || rets[0] == -1); - EndHook(); - - return result; // true - use engine handler -} - -static void __declspec(naked) InvenWieldFuncHook() { - __asm { - HookBegin; - mov args[0], eax; // critter - mov args[4], edx; // item - mov args[8], ebx; // slot - pushad; - } - - // right hand slot? - if (args[2] != fo::INVEN_TYPE_RIGHT_HAND && fo::func::item_get_type((fo::GameObject*)args[1]) != fo::item_type_armor) { - args[2] = fo::INVEN_TYPE_LEFT_HAND; - } - InvenWieldHook_ScriptPart(1); // wield event - - __asm { - test al, al; - popad; - jz skip; - jmp fo::funcoffs::invenWieldFunc_; -skip: - mov eax, -1; - retn; - } -} - -// called when unwielding weapons -static void __declspec(naked) InvenUnwieldFuncHook() { - __asm { - HookBegin; - mov args[0], eax; // critter - mov args[8], edx; // slot - pushadc; - } - - // set slot - if (args[2] == 0) { // left hand slot? - args[2] = fo::INVEN_TYPE_LEFT_HAND; - } - - // get item - args[1] = (DWORD)fo::util::GetItemPtrSlot((fo::GameObject*)args[0], (fo::InvenType)args[2]); - - InvenWieldHook_ScriptPart(0); // unwield event - - __asm { - test al, al; - popadc; - jz skip; - jmp fo::funcoffs::invenUnwieldFunc_; -skip: - mov eax, -1; - retn; - } -} - -static void __declspec(naked) CorrectFidForRemovedItemHook() { - using namespace fo::ObjectFlag; - __asm { - HookBegin; - mov args[0], eax; // critter - mov args[4], edx; // item - mov args[8], ebx; // item flag - pushadc; - } - - // set slot - if (args[2] & fo::ObjectFlag::Right_Hand) { // right hand slot - args[2] = fo::INVEN_TYPE_RIGHT_HAND; - } else if (args[2] & fo::ObjectFlag::Left_Hand) { // left hand slot - args[2] = fo::INVEN_TYPE_LEFT_HAND; - } else { - args[2] = fo::INVEN_TYPE_WORN; // armor slot - } - - InvenWieldHook_ScriptPart(0, 1); // unwield event (armor by default) - - // engine handler is not overridden - __asm { - popadc; - jmp fo::funcoffs::correctFidForRemovedItem_; - } -} - -long __stdcall InvenWieldHook_Invoke(fo::GameObject* critter, fo::GameObject* item, long flags) { - if (!HookScripts::HookHasScript(HOOK_INVENWIELD)) return 1; - - long slot = fo::INVEN_TYPE_WORN; - if (flags & fo::ObjectFlag::Right_Hand) { // right hand slot - slot = fo::INVEN_TYPE_RIGHT_HAND; - } else if (flags & fo::ObjectFlag::Left_Hand) { // left hand slot - slot = fo::INVEN_TYPE_LEFT_HAND; - } - return InvenWieldHook_Script(critter, item, slot, 0, 0); -} - -static void __declspec(naked) item_drop_all_hack() { - using namespace fo::ObjectFlag; - __asm { - mov ecx, 1; - push eax; - mov [esp + 0x40 - 0x2C + 8], ecx; // itemIsEquipped - push ecx; // remove event - push 0; // unwield event - inc ecx; // INVEN_TYPE_LEFT_HAND (2) - test ah, Left_Hand >> 24; - jnz skip; - test ah, Worn >> 24; - setz cl; // set INVEN_TYPE_WORN or INVEN_TYPE_RIGHT_HAND -skip: - push ecx; // slot - mov edx, esi; // item - mov ecx, edi; // critter - call InvenWieldHook_Script; - //mov [esp + 0x40 - 0x2C + 8], eax; // itemIsEquipped (eax - hook return result) - pop eax; - retn; - } -} - -// called from bugfixes for obj_drop_ -void __declspec(naked) InvenUnwield_HookDrop() { // ecx - critter, edx - item - using namespace fo; - using namespace Fields; - using namespace ObjectFlag; - __asm { - pushadc; - mov eax, INVEN_TYPE_LEFT_HAND; - test byte ptr [edx + flags + 3], Left_Hand >> 24; - jnz isLeft; - test byte ptr [edx + flags + 3], Worn >> 24; - setz al; // set INVEN_TYPE_WORN or INVEN_TYPE_RIGHT_HAND -isLeft: - push 1; // remove event - push 0; // unwield event - push eax; // slot - call InvenWieldHook_Script; // ecx - critter, edx - item - // engine handler is not overridden - popadc; - retn; - } -} - -// called from bugfixes for op_move_obj_inven_to_obj_ -void __declspec(naked) InvenUnwield_HookMove() { // eax - item, edx - critter - __asm { - pushadc; - mov ecx, edx; - mov edx, eax; - push 1; // remove event - xor eax, eax; - push eax; // unwield event - push eax; // slot - call InvenWieldHook_Script; // ecx - critter, edx - item - // engine handler is not overridden - popadc; - retn; - } -} - -// called when unwelding dude weapon and armor -static void __declspec(naked) op_move_obj_inven_to_obj_hook() { - using namespace fo; - using namespace ObjectFlag; - __asm { - cmp eax, ds:[FO_VAR_obj_dude]; - je runHook; - jmp fo::funcoffs::item_move_all_; -runHook: - push eax; - push edx; - mov ecx, eax; // keep source - mov edx, ds:[FO_VAR_itemCurrentItem]; // get player's active slot - test edx, edx; - jz left; - call fo::funcoffs::inven_right_hand_; - jmp skip; -left: - call fo::funcoffs::inven_left_hand_; -skip: - test eax, eax; - jz noWeapon; - push 1; // remove event - push 0; // unwield event - mov ebx, INVEN_TYPE_LEFT_HAND; - sub ebx, edx; - push ebx; // slot: INVEN_TYPE_LEFT_HAND or INVEN_TYPE_RIGHT_HAND - mov edx, eax; // weapon - mov ebx, ecx; // keep source - call InvenWieldHook_Script; // ecx - source - // engine handler is not overridden -noWeapon: - mov edx, [esp + 0x30 - 0x20 + 12]; // armor - test edx, edx; - jz noArmor; - xor eax, eax; - push 1; // remove event - push eax; // unwield event - push eax; // slot: INVEN_TYPE_WORN - mov ecx, ebx; // source - call InvenWieldHook_Script; - // engine handler is not overridden -noArmor: - pop edx; - pop eax; - jmp fo::funcoffs::item_move_all_; - } -} - -void __stdcall AdjustFidHook(DWORD vanillaFid) { - BeginHook(); - argCount = 2; - - args[0] = vanillaFid; - args[1] = *fo::ptr::i_fid; // modified FID by sfall code - RunHookScript(HOOK_ADJUSTFID); - - if (cRet > 0) { - *fo::ptr::i_fid = rets[0]; - } - EndHook(); -} - -static unsigned long previousGameMode = 0; - -void HookCommon::GameModeChangeHook(DWORD exit) { - if (HookScripts::HookHasScript(HOOK_GAMEMODECHANGE)) { - BeginHook(); - argCount = 2; - args[0] = exit; - args[1] = previousGameMode; - RunHookScript(HOOK_GAMEMODECHANGE); - EndHook(); - } - previousGameMode = GetLoopFlags(); -} -// END HOOKS - -void HookCommon::Reset() { - previousGameMode = 0; +static HooksInjectInfo injectHooks[] = { + {HOOK_TOHIT, Inject_ToHitHook, false}, + {HOOK_AFTERHITROLL, Inject_AfterHitRollHook, false}, + {HOOK_CALCAPCOST, Inject_CalcApCostHook, false}, + {HOOK_DEATHANIM1, Inject_DeathAnim1Hook, false}, + {HOOK_DEATHANIM2, Inject_DeathAnim2Hook, false}, + {HOOK_COMBATDAMAGE, Inject_CombatDamageHook, false}, + {HOOK_ONDEATH, Inject_OnDeathHook, false}, + {HOOK_FINDTARGET, Inject_FindTargetHook, false}, + {HOOK_USEOBJON, Inject_UseObjOnHook, false}, + {HOOK_REMOVEINVENOBJ, Inject_RemoveInvenObjHook, false}, + {HOOK_BARTERPRICE, Inject_BarterPriceHook, false}, + {HOOK_MOVECOST, Inject_MoveCostHook, false}, + {HOOK_HEXMOVEBLOCKING, Inject_HexMoveBlockHook, false}, + {HOOK_HEXAIBLOCKING, Inject_HexIABlockHook, false}, + {HOOK_HEXSHOOTBLOCKING, Inject_HexShootBlockHook, false}, + {HOOK_HEXSIGHTBLOCKING, Inject_HexSightBlockHook, false}, + {HOOK_ITEMDAMAGE, Inject_ItemDamageHook, false}, + {HOOK_AMMOCOST, Inject_AmmoCostHook, false}, + {HOOK_USEOBJ, Inject_UseObjHook, false}, + {HOOK_KEYPRESS, nullptr, true}, // no embed code to the engine + {HOOK_MOUSECLICK, nullptr, true}, // no embed code to the engine + {HOOK_USESKILL, Inject_UseSkillHook, false}, + {HOOK_STEAL, Inject_StealCheckHook, false}, + {HOOK_WITHINPERCEPTION, Inject_WithinPerceptionHook, false}, + {HOOK_INVENTORYMOVE, Inject_InventoryMoveHook, false}, + {HOOK_INVENWIELD, Inject_InvenWieldHook, false}, + {HOOK_ADJUSTFID, nullptr, true}, // always embedded to the engine + {-1, nullptr, true}, // dummy + {-1, nullptr, true}, // dummy + {-1, nullptr, true}, // dummy + {-1, nullptr, true}, // dummy + {HOOK_GAMEMODECHANGE, nullptr, true}, // always embedded to the engine +}; + +void HookScripts::InjectingHook(int hookId) { + if (!IsInjectHook(hookId) && injectHooks[hookId].id == hookId) { + injectHooks[hookId].injectState = true; + injectHooks[hookId].inject(); + devlog_f("Inject hook ID: %d\n", DL_INIT, hookId); + } +} + +bool HookScripts::IsInjectHook(int hookId) { + return injectHooks[hookId].injectState; } bool HookScripts::HookHasScript(int hookId) { @@ -1740,6 +136,8 @@ void HookScripts::RegisterHook(fo::Program* script, int id, int procNum, bool sp hooksInfo[id].hsPosition++; } hooks[id].insert(c_it, hook); + + HookScripts::InjectingHook(id); // inject hook to engine code } } @@ -1756,12 +154,18 @@ void HookScripts::LoadHookScript(const char* name, int id) { char filePath[MAX_PATH]; sprintf(filePath, "scripts\\%s.int", name); - if (!fo::func::db_access(filePath)) return; // hook script not found + bool hookHasScript = fo::func::db_access(filePath); - HookFile hookFile = {id, name}; - hookScriptFilesList.push_back(hookFile); + if (hookHasScript || injectAllHooks) { + HookScripts::InjectingHook(id); // inject hook to engine code - dlog_f("Found hook script: %s\n", DL_HOOK, name); + if (!hookHasScript) return; // only inject + + HookFile hookFile = {id, name}; + hookScriptFilesList.push_back(hookFile); + + dlog_f("Found hook script: %s\n", DL_HOOK, name); + } } static void InitHookScriptFile(const char* name, int id) { @@ -1790,33 +194,15 @@ void HookScripts::LoadHookScripts() { if (!hooksFilesLoaded) { // hook files are already put in the list hookScriptFilesList.clear(); - HookScripts::LoadHookScript("hs_tohit", HOOK_TOHIT); - HookScripts::LoadHookScript("hs_afterhitroll", HOOK_AFTERHITROLL); - HookScripts::LoadHookScript("hs_calcapcost", HOOK_CALCAPCOST); - HookScripts::LoadHookScript("hs_deathanim1", HOOK_DEATHANIM1); - HookScripts::LoadHookScript("hs_deathanim2", HOOK_DEATHANIM2); - HookScripts::LoadHookScript("hs_combatdamage", HOOK_COMBATDAMAGE); - HookScripts::LoadHookScript("hs_ondeath", HOOK_ONDEATH); - HookScripts::LoadHookScript("hs_findtarget", HOOK_FINDTARGET); - HookScripts::LoadHookScript("hs_useobjon", HOOK_USEOBJON); - HookScripts::LoadHookScript("hs_removeinvenobj", HOOK_REMOVEINVENOBJ); - HookScripts::LoadHookScript("hs_barterprice", HOOK_BARTERPRICE); - HookScripts::LoadHookScript("hs_movecost", HOOK_MOVECOST); - HookScripts::LoadHookScript("hs_hexmoveblocking", HOOK_HEXMOVEBLOCKING); - HookScripts::LoadHookScript("hs_hexaiblocking", HOOK_HEXAIBLOCKING); - HookScripts::LoadHookScript("hs_hexshootblocking", HOOK_HEXSHOOTBLOCKING); - HookScripts::LoadHookScript("hs_hexsightblocking", HOOK_HEXSIGHTBLOCKING); - HookScripts::LoadHookScript("hs_itemdamage", HOOK_ITEMDAMAGE); - HookScripts::LoadHookScript("hs_ammocost", HOOK_AMMOCOST); - HookScripts::LoadHookScript("hs_useobj", HOOK_USEOBJ); + InitCombatHookScripts(); + InitDeathHookScripts(); + InitHexBlockingHookScripts(); + InitInventoryHookScripts(); + InitObjectHookScripts(); + InitMiscHookScripts(); + HookScripts::LoadHookScript("hs_keypress", HOOK_KEYPRESS); HookScripts::LoadHookScript("hs_mouseclick", HOOK_MOUSECLICK); - HookScripts::LoadHookScript("hs_useskill", HOOK_USESKILL); - HookScripts::LoadHookScript("hs_steal", HOOK_STEAL); - HookScripts::LoadHookScript("hs_withinperception", HOOK_WITHINPERCEPTION); - HookScripts::LoadHookScript("hs_inventorymove", HOOK_INVENTORYMOVE); - HookScripts::LoadHookScript("hs_invenwield", HOOK_INVENWIELD); - HookScripts::LoadHookScript("hs_adjustfid", HOOK_ADJUSTFID); HookScripts::LoadHookScript("hs_gamemodechange", HOOK_GAMEMODECHANGE); hooksFilesLoaded = !alwaysFindScripts; @@ -1851,179 +237,12 @@ void HookScripts::HookScriptClear() { } void HookScripts::init() { - dlogr("Injecting all game hooks.", DL_HOOK|DL_INIT); - // HOOK_TOHIT - const DWORD toHitHkAddr[] = { - 0x421686, // combat_safety_invalidate_weapon_func_ - 0x4231D9, // check_ranged_miss_ - 0x42331F, // shoot_along_path_ - 0x4237FC, // compute_attack_ - 0x424379, // determine_to_hit_ - 0x42438D, // determine_to_hit_no_range_ - 0x42439C, // determine_to_hit_from_tile_ - 0x42679A // combat_to_hit_ - }; - HookCalls(ToHitHook, toHitHkAddr); + injectAllHooks = isDebug && (IniReader::GetIntDefaultConfig("Debugging", "InjectAllGameHooks", 0) != 0); + if (injectAllHooks) dlogr("Injecting all game hooks.", DL_HOOK|DL_INIT); - // HOOK_AFTERHITROLL - MakeCall(0x423893, AfterHitRollHook); - - // HOOK_CALCAPCOST - //const DWORD calcApCostHkAddr[] = { - // 0x42307A, - // 0x42669F, - // 0x42687B, - // 0x42A625, - // 0x42A655, - // 0x42A686, - // 0x42AE32, - // 0x42AE71, - // 0x460048, - // 0x47807B - //}; - //HookCalls(CalcApCostHook, calcApCostHkAddr); - MakeCall(0x478083, CalcApCostHook2); - - // HOOK_DEATHANIM1, HOOK_DEATHANIM2 - HookCall(0x4109DE, CalcDeathAnimHook); // show_damage_to_object_ - const DWORD calcDeathAnim2HkAddr[] = { - 0x410981, // show_damage_to_object_ - 0x4109A1, // show_damage_to_object_ - 0x4109BF // show_damage_to_object_ - }; - HookCalls(CalcDeathAnim2Hook, calcDeathAnim2HkAddr); - - // HOOK_COMBATDAMAGE - const DWORD computeDamageHkAddr[] = { - 0x42326C, // check_ranged_miss() - 0x4233E3, // shoot_along_path() - for extra burst targets - 0x423AB7, // compute_attack() - 0x423BBF, // compute_attack() -// 0x423DE7, // compute_explosion_on_extras() - 0x423E69, // compute_explosion_on_extras() - 0x424220, // attack_crit_failure() - 0x4242FB // attack_crit_failure() - }; - HookCalls(ComputeDamageHook, computeDamageHkAddr); - MakeCall(0x423DEB, ComputeDamageHook); // compute_explosion_on_extras() - for the attacker - - // HOOK_ONDEATH - MakeCall(0x42DA6D, OnDeathHook); // critter_kill_ - HookCall(0x425161, OnDeathHook2); // damage_object_ - - // HOOK_FINDTARGET - HookCall(0x429143, FindTargetHook); - - // HOOK_USEOBJON - const DWORD useObjOnHkAddr[] = {0x49C606, 0x473619}; - HookCalls(UseObjOnHook, useObjOnHkAddr); - // the following hooks allows to catch drug use of AI and from action cursor - const DWORD drugUseObjOnHkAddr[] = { - //0x4285DF, // ai_check_drugs - //0x4286F8, // ai_check_drugs - //0x4287F8, // ai_check_drugs - 0x473573 // inven_action_cursor - }; - HookCalls(Drug_UseObjOnHook, drugUseObjOnHkAddr); - - // HOOK_REMOVEINVENOBJ - MakeJump(0x477492, RemoveObjHook); // old 0x477490 - - // HOOK_BARTERPRICE - const DWORD barterPriceHkAddr[] = { - 0x474D4C, // barter_attempt_transaction_ (offers button) - 0x475735, // display_table_inventories_ (for party members) - 0x475762 // display_table_inventories_ - }; - HookCalls(BarterPriceHook, barterPriceHkAddr); - const DWORD pcBarterPriceHkAddr[] = {0x4754F4, 0X47551A}; // display_table_inventories_ - HookCalls(PC_BarterPriceHook, pcBarterPriceHkAddr); - HookCall(0x474D3F, OverrideCost_BarterPriceHook); // barter_attempt_transaction_ (just overrides cost of offered goods) - - // HOOK_MOVECOST - const DWORD moveCostHkAddr[] = {0x417665, 0x44B88A}; - HookCalls(MoveCostHook, moveCostHkAddr); - - // HOOK_HEXMOVEBLOCKING, HOOK_HEXAIBLOCKING, HOOK_HEXSHOOTBLOCKING, HOOK_HEXSIGHTBLOCKING - SafeWrite32(0x413979, (DWORD)&HexSightBlockingHook); - const DWORD shootBlockingAddr[] = {0x4C1A88, 0x423178, 0x4232D4, 0x423B4D, 0x426CF8, 0x42A570}; - SafeWriteBatch((DWORD)&HexShootBlockingHook, shootBlockingAddr); - SafeWrite32(0x42A0A4, (DWORD)&HexAIBlockingHook); - MakeJump(0x48B848, HexMoveBlockingHook); - - // HOOK_ITEMDAMAGE - HookCall(0x478560, ItemDamageHook); - - // HOOK_AMMOCOST - HookCall(0x423A7C, AmmoCostHook); // compute_attack_ - - // HOOK_USEOBJ - const DWORD useObjHkAddr[] = {0x42AEBF, 0x473607, 0x49C12E}; - HookCalls(UseObjHook, useObjHkAddr); - - // HOOK_USESKILL - const DWORD useSkillHkAddr[] = {0x49C48F, 0x49D12E}; - HookCalls(UseSkillHook, useSkillHkAddr); - - // HOOK_STEAL - const DWORD stealCheckHkAddr[] = {0x4749A2, 0x474A69}; - HookCalls(StealCheckHook, stealCheckHkAddr); - - // HOOK_WITHINPERCEPTION - const DWORD perceptionRngHkAddr[] = { - 0x42B4ED, - 0x42BC87, - 0x42BC9F, - 0x42BD04 - }; - HookCalls(PerceptionRangeHook, perceptionRngHkAddr); - HookCall(0x429157, PerceptionSearchTargetHook); - HookCall(0x456BA2, PerceptionRangeSeeHook); - HookCall(0x458403, PerceptionRangeHearHook); - - // HOOK_INVENTORYMOVE - const DWORD switchHandHkAddr[] = { - 0x4712E3, // left slot - 0x47136D // right slot - }; - HookCalls(SwitchHandHook, switchHandHkAddr); - MakeJump(0x4713A9, UseArmorHack); // old 0x4713A3 - MakeJump(0x476491, DropIntoContainerHack); - const DWORD dropIntoContHandHkAddr[] = {0x471338, 0x4712AB}; - MakeJumps(DropIntoContainerHandSlotHack, dropIntoContHandHkAddr); - HookCall(0x471200, MoveInventoryHook); - HookCall(0x476549, DropAmmoIntoWeaponHook); // old 0x476588 - const DWORD actionCurObjDropHkAddr[] = { - 0x473851, 0x47386F, - 0x47379A // caps multi drop - }; - HookCalls(InvenActionCursorObjDropHook, actionCurObjDropHkAddr); - MakeCall(0x473807, InvenActionExplosiveDropHack, 1); // drop active explosives - MakeCall(0x49B660, PickupObjectHack); - SafeWrite32(0x49B665, 0x850FD285); // test edx, edx - SafeWrite32(0x49B669, 0xC2); // jnz 0x49B72F - SafeWrite8(0x49B66E, 0xFE); // cmp edi > cmp esi - HookCall(0x471457, InvenPickupHook); - - // HOOK_INVENWIELD - const DWORD invWieldFuncHkAddr[] = { - 0x47275E, // inven_wield_ - 0x495FDF // partyMemberCopyLevelInfo_ - }; - HookCalls(InvenWieldFuncHook, invWieldFuncHkAddr); - const DWORD invUnwieldFuncHkAddr[] = { - 0x45967D, // op_metarule_ - 0x472A5A, // inven_unwield_ - 0x495F0B // partyMemberCopyLevelInfo_ - }; - HookCalls(InvenUnwieldFuncHook, invUnwieldFuncHkAddr); - const DWORD fidRemovedItemHkAddr[] = { - 0x45680C, // op_rm_obj_from_inven_ - 0x45C4EA // op_move_obj_inven_to_obj_ - }; - HookCalls(CorrectFidForRemovedItemHook, fidRemovedItemHkAddr); - HookCall(0x45C4F6, op_move_obj_inven_to_obj_hook); - MakeCall(0x4778AF, item_drop_all_hack, 3); +// for (int i = 0; i < HOOK_COUNT; i++) { +// dlog_f("injectHooks[%d]: id(%d), func(0x%x), state(%d)\n", DL_INIT, i, injectHooks[i].id, injectHooks[i].inject, injectHooks[i].injectState); +// } } } diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index d97f9389..caab6edd 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -56,6 +56,12 @@ enum HookType HOOK_COUNT }; +struct HookFile { + int id; +// std::string filePath; + std::string name; +}; + class HookScripts { public: static const char* name() { return "HookScripts"; } @@ -70,40 +76,13 @@ public: static bool HookHasScript(int hookId); + static void InjectingHook(int hookId); + static bool IsInjectHook(int hookId); + // register hook by proc num (special values: -1 - use default (start) procedure, 0 - unregister) static void RegisterHook(fo::Program* script, int id, int procNum, bool specReg); static void RunHookScriptsAtProc(DWORD procId); }; -class HookCommon { -public: - static DWORD GetHSArgCount(); - static DWORD GetHSArg(); - static DWORD GetHSArgAt(DWORD id); - static DWORD* GetHSArgs(); - static void SetHSArg(DWORD id, DWORD value); - static void __stdcall SetHSReturn(DWORD d); - - static void GameModeChangeHook(DWORD exit); - static void __stdcall KeyPressHook(DWORD* dxKey, bool pressed, DWORD vKey); - static void __stdcall MouseClickHook(DWORD button, bool pressed); - - static void Reset(); -}; - -int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD &rounds); - -void InvenUnwield_HookDrop(); -void InvenUnwield_HookMove(); - -void __stdcall SetRemoveObjectType(long rmType); - -void __stdcall AdjustFidHook(DWORD vanillaFid); - -long __stdcall CalcApCostHook_Invoke(fo::GameObject* source, long hitMode, long isCalled, long cost, fo::GameObject* weapon); -long __stdcall UseObjOnHook_Invoke(fo::GameObject* source, fo::GameObject* item, fo::GameObject* target); -long __stdcall PerceptionRangeHook_Invoke(fo::GameObject* watcher, fo::GameObject* target, long type, long result); -long __stdcall InvenWieldHook_Invoke(fo::GameObject* critter, fo::GameObject* item, long flags); - } diff --git a/sfall/Modules/HookScripts/CombatHs.cpp b/sfall/Modules/HookScripts/CombatHs.cpp new file mode 100644 index 00000000..c6656f8b --- /dev/null +++ b/sfall/Modules/HookScripts/CombatHs.cpp @@ -0,0 +1,394 @@ +#include "..\..\FalloutEngine\Fallout2.h" +#include "..\..\SafeWrite.h" +#include "..\Combat.h" +#include "..\DamageMod.h" +#include "..\HookScripts.h" +#include "Common.h" + +#include "CombatHs.h" + +namespace sfall +{ + +static void __declspec(naked) ToHitHook() { + __asm { + HookBegin; + mov args[4], eax; // attacker + mov args[8], ebx; // target + mov args[12], ecx; // body part + mov args[16], edx; // source tile + mov eax, [esp + 8]; + mov args[24], eax; // is ranged + push eax; + mov eax, [esp + 8]; + mov args[20], eax; // attack type + push eax; + mov eax, args[4]; // restore + call fo::funcoffs::determine_to_hit_func_; + mov args[0], eax; + mov ebx, eax; + } + argCount = 8; + + args[7] = Combat::determineHitChance; + RunHookScript(HOOK_TOHIT); + + __asm { + cmp cRet, 1; + cmovge ebx, rets[0]; + call EndHook; + mov eax, ebx; + retn 8; + } +} + +static DWORD __fastcall AfterHitRollHook_Script(fo::ComputeAttackResult &ctd, DWORD hitChance, DWORD hit) { + BeginHook(); + argCount = 5; + + args[0] = hit; + args[1] = (DWORD)ctd.attacker; // Attacker + args[2] = (DWORD)ctd.target; // Target + args[3] = ctd.bodyPart; // bodypart + args[4] = hitChance; + + RunHookScript(HOOK_AFTERHITROLL); + if (cRet > 0) { + hit = rets[0]; + if (cRet > 1) { + ctd.bodyPart = rets[1]; + if (cRet > 2) ctd.target = (fo::GameObject*)rets[2]; + } + } + EndHook(); + + return hit; +} + +static void __declspec(naked) AfterHitRollHook() { + using namespace fo; + __asm { + mov ecx, esi; // ctd + mov edx, [esp + 0x18 + 4]; // hit chance + push eax; // was it a hit? + call AfterHitRollHook_Script; + // engine code + mov ebx, eax; + cmp eax, ROLL_FAILURE; + retn; + } +} + +static long CalcApCostHook_Script(fo::GameObject* source, long hitMode, long isCalled, long cost, fo::GameObject* weapon) { + BeginHook(); + argCount = 5; + + args[0] = (DWORD)source; + args[1] = hitMode; + args[2] = isCalled; + args[3] = cost; + args[4] = (DWORD)weapon; + + RunHookScript(HOOK_CALCAPCOST); + + if (cRet > 0) cost = rets[0]; + EndHook(); + + return cost; +} + +long CalcApCostHook_Invoke(fo::GameObject* source, long hitMode, long isCalled, long cost, fo::GameObject* weapon) { + return (HookScripts::HookHasScript(HOOK_CALCAPCOST)) + ? CalcApCostHook_Script(source, hitMode, isCalled, cost, weapon) + : cost; +} +/* +static void __declspec(naked) CalcApCostHook() { + __asm { + HookBegin; + mov args[0], eax; + mov args[4], edx; + mov args[8], ebx; + call fo::funcoffs::item_w_mp_cost_; + mov args[12], eax; + mov ebx, eax; + push ecx; + } + + argCount = 5; + args[4] = 0; + + RunHookScript(HOOK_CALCAPCOST); + + __asm { + cmp cRet, 1; + cmovge ebx, rets[0]; + call EndHook; + mov eax, ebx; + pop ecx; + retn; + } +} +*/ +// this is for using non-weapon items, always 2 AP in vanilla +static void __declspec(naked) CalcApCostHook2() { + __asm { + HookBegin; + mov args[0], ecx; // critter + mov args[4], edx; // attack type (to determine hand) + mov args[8], ebx; + mov ebx, 2; // vanilla cost value + mov args[12], ebx; + //push ecx; + } + + argCount = 5; + args[4] = 0; + + RunHookScript(HOOK_CALCAPCOST); + + __asm { + cmp cRet, 1; + cmovge ebx, rets[0]; + call EndHook; + mov eax, ebx; + //pop ecx; + retn; + } +} + +static void __fastcall ComputeDamageHook_Script(fo::ComputeAttackResult &ctd, DWORD rounds, DWORD multiplier) { + BeginHook(); + argCount = 13; + + args[0] = (DWORD)ctd.target; // Target + args[1] = (DWORD)ctd.attacker; // Attacker + args[2] = ctd.targetDamage; // amountTarget + args[3] = ctd.attackerDamage; // amountSource + args[4] = ctd.targetFlags; // flagsTarget + args[5] = ctd.attackerFlags; // flagsSource + args[6] = (DWORD)ctd.weapon; + args[7] = ctd.bodyPart; + args[8] = multiplier; // damage multiplier + args[9] = rounds; // number of rounds + args[10] = ctd.knockbackValue; + args[11] = ctd.hitMode; // attack type + args[12] = (DWORD)&ctd; // main_ctd/shoot_ctd/explosion_ctd + + RunHookScript(HOOK_COMBATDAMAGE); + + if (cRet > 0) { + ctd.targetDamage = rets[0]; + if (cRet > 1) { + ctd.attackerDamage = rets[1]; + if (cRet > 2) { + ctd.targetFlags = rets[2]; // flagsTarget + if (cRet > 3) { + ctd.attackerFlags = rets[3]; // flagsSource + if (cRet > 4) ctd.knockbackValue = rets[4]; + } + } + } + } + EndHook(); +} + +static void __declspec(naked) ComputeDamageHook() { + __asm { + push ecx; + push ebx; // store dmg multiplier args[8] + push edx; // store num of rounds args[9] + push eax; // store ctd + call fo::funcoffs::compute_damage_; + pop ecx; // restore ctd (eax) + pop edx; // restore num of rounds + call ComputeDamageHook_Script; // stack - arg multiplier + pop ecx; + retn; + } +} + +static void __fastcall FindTargetHook_Script(DWORD* target, fo::GameObject* attacker) { + BeginHook(); + argCount = 5; + + args[0] = (DWORD)attacker; + args[1] = target[0]; + args[2] = target[1]; + args[3] = target[2]; + args[4] = target[3]; + + RunHookScript(HOOK_FINDTARGET); + + if (cRet > 0) { + if (rets[0] != -1) target[0] = rets[0]; + if (cRet > 1 && rets[1] != -1) target[1] = rets[1]; + if (cRet > 2 && rets[2] != -1) target[2] = rets[2]; + if (cRet > 3 && rets[3] != -1) target[3] = rets[3]; + } + EndHook(); +} + +static void __declspec(naked) FindTargetHook() { + __asm { + push eax; + call fo::funcoffs::qsort_; + pop ecx; // targets (base) + mov edx, esi; // attacker + jmp FindTargetHook_Script; + } +} + +static void __declspec(naked) ItemDamageHook() { + __asm { + HookBegin; + mov args[0], eax; // min + mov args[4], edx; // max + mov args[8], edi; // weapon + mov args[12], ecx; // critter + mov args[16], esi; // type + mov args[20], ebp; // non-zero for weapon melee attack (add to min/max melee damage) + pushadc; + } + argCount = 6; + + // tweak for 0x4784AA (obsolete) + //if (args[2] == 0) { // weapon arg + // args[4] += 8; // type arg + //} + + RunHookScript(HOOK_ITEMDAMAGE); + + __asm popadc; + if (cRet > 0) { + __asm mov eax, rets[0]; // set min + if (cRet > 1) { + __asm mov edx, rets[4]; // set max + } else { + HookEnd; + __asm retn; // no calc random + } + } + HookEnd; + __asm jmp fo::funcoffs::roll_random_; +} + +int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD &rounds) { + int result = 0; + + BeginHook(); + argCount = 4; + + args[0] = (DWORD)weapon; + args[1] = rounds; // rounds in attack + args[3] = hookType; + + if (hookType == 2) { // burst hook + rounds = 1; // set default multiply for check burst attack + } else { + result = fo::func::item_w_compute_ammo_cost(weapon, &rounds); + if (result == -1) goto failed; // computation failed + } + args[2] = rounds; // rounds as computed by game (cost) + + RunHookScript(HOOK_AMMOCOST); + + if (cRet > 0) rounds = rets[0]; // override rounds + +failed: + EndHook(); + return result; +} + +static void __declspec(naked) AmmoCostHook() { + using namespace fo; + __asm { + xor ecx, ecx; // type of hook (0) + cmp dword ptr [esp + 0x1C + 4], ANIM_fire_burst; + jl skip; + cmp dword ptr [esp + 0x1C + 4], ANIM_fire_continuous; + jg skip; + mov ecx, 3; // hook type burst +skip: + xchg eax, edx; + push eax; // rounds in attack ref + call AmmoCostHook_Script; // edx - weapon + retn; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +void Inject_ToHitHook() { + const DWORD toHitHkAddr[] = { + 0x421686, // combat_safety_invalidate_weapon_func_ + 0x4231D9, // check_ranged_miss_ + 0x42331F, // shoot_along_path_ + 0x4237FC, // compute_attack_ + 0x424379, // determine_to_hit_ + 0x42438D, // determine_to_hit_no_range_ + 0x42439C, // determine_to_hit_from_tile_ + 0x42679A // combat_to_hit_ + }; + HookCalls(ToHitHook, toHitHkAddr); +} + +void Inject_AfterHitRollHook() { + MakeCall(0x423893, AfterHitRollHook); +} + +void Inject_CalcApCostHook() { + //const DWORD calcApCostHkAddr[] = { + // 0x42307A, + // 0x42669F, + // 0x42687B, + // 0x42A625, + // 0x42A655, + // 0x42A686, + // 0x42AE32, + // 0x42AE71, + // 0x460048, + // 0x47807B + //}; + //HookCalls(CalcApCostHook, calcApCostHkAddr); + MakeCall(0x478083, CalcApCostHook2); +} + +void Inject_CombatDamageHook() { + const DWORD computeDamageHkAddr[] = { + 0x42326C, // check_ranged_miss() + 0x4233E3, // shoot_along_path() - for extra burst targets + 0x423AB7, // compute_attack() + 0x423BBF, // compute_attack() +// 0x423DE7, // compute_explosion_on_extras() + 0x423E69, // compute_explosion_on_extras() + 0x424220, // attack_crit_failure() + 0x4242FB // attack_crit_failure() + }; + HookCalls(ComputeDamageHook, computeDamageHkAddr); + MakeCall(0x423DEB, ComputeDamageHook); // compute_explosion_on_extras() - fix for the attacker +} + +void Inject_FindTargetHook() { + HookCall(0x429143, FindTargetHook); +} + +void Inject_ItemDamageHook() { + HookCall(0x478560, ItemDamageHook); +} + +void Inject_AmmoCostHook() { + HookCall(0x423A7C, AmmoCostHook); // compute_attack_ +} + +void InitCombatHookScripts() { + HookScripts::LoadHookScript("hs_tohit", HOOK_TOHIT); + HookScripts::LoadHookScript("hs_afterhitroll", HOOK_AFTERHITROLL); + HookScripts::LoadHookScript("hs_calcapcost", HOOK_CALCAPCOST); + HookScripts::LoadHookScript("hs_combatdamage", HOOK_COMBATDAMAGE); + HookScripts::LoadHookScript("hs_findtarget", HOOK_FINDTARGET); + HookScripts::LoadHookScript("hs_itemdamage", HOOK_ITEMDAMAGE); + HookScripts::LoadHookScript("hs_ammocost", HOOK_AMMOCOST); +} + +} diff --git a/sfall/Modules/HookScripts/CombatHs.h b/sfall/Modules/HookScripts/CombatHs.h new file mode 100644 index 00000000..8f2875e7 --- /dev/null +++ b/sfall/Modules/HookScripts/CombatHs.h @@ -0,0 +1,22 @@ +#pragma once + +// Combat-related hook scripts + +namespace sfall +{ + +void InitCombatHookScripts(); + +void Inject_ToHitHook(); +void Inject_AfterHitRollHook(); +void Inject_CalcApCostHook(); +void Inject_CombatDamageHook(); +void Inject_FindTargetHook(); +void Inject_ItemDamageHook(); +void Inject_AmmoCostHook(); + +int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD &rounds); + +long CalcApCostHook_Invoke(fo::GameObject* source, long hitMode, long isCalled, long cost, fo::GameObject* weapon); + +} diff --git a/sfall/Modules/HookScripts/Common.cpp b/sfall/Modules/HookScripts/Common.cpp new file mode 100644 index 00000000..ba6d3e16 --- /dev/null +++ b/sfall/Modules/HookScripts/Common.cpp @@ -0,0 +1,199 @@ +#include "..\..\FalloutEngine\Fallout2.h" +#include "..\LoadGameHook.h" + +#include "Common.h" + +namespace sfall +{ + +static const int maxArgs = 16; // Maximum number of hook arguments +static const int maxRets = 8; // Maximum number of return values +static const int maxDepth = 8; // Maximum recursion depth for hook calls + +struct { + DWORD hookID; + DWORD argCount; + DWORD cArg; + DWORD cRet; + DWORD cRetTmp; + DWORD oldArgs[maxArgs]; + DWORD oldRets[maxRets]; +} savedArgs[maxDepth]; + +static DWORD callDepth; +static DWORD currentRunHook = -1; + +DWORD args[maxArgs]; // current hook arguments +DWORD rets[maxRets]; // current hook return values + +DWORD argCount; +DWORD cArg; // how many arguments were taken by current hook script +DWORD cRet; // how many return values were set by current hook script +DWORD cRetTmp; // how many return values were set by specific hook script (when using register_hook) + +std::vector hooks[HOOK_COUNT]; + +DWORD HookCommon::GetHSArgCount() { + return argCount; +} + +DWORD HookCommon::GetHSArg() { + return (cArg == argCount) ? 0 : args[cArg++]; +} + +void HookCommon::SetHSArg(DWORD id, DWORD value) { + if (id < argCount) args[id] = value; +} + +DWORD* HookCommon::GetHSArgs() { + return args; +} + +DWORD HookCommon::GetHSArgAt(DWORD id) { + return args[id]; +} + +void __stdcall HookCommon::SetHSReturn(DWORD value) { + if (cRetTmp < maxRets) { + rets[cRetTmp++] = value; + } + if (cRetTmp > cRet) { + cRet = cRetTmp; + } +} + +void __stdcall BeginHook() { + if (callDepth && callDepth <= maxDepth) { + // save all values of the current hook if another hook was called during the execution of the current hook + int cDepth = callDepth - 1; + savedArgs[cDepth].hookID = currentRunHook; + savedArgs[cDepth].argCount = argCount; // number of arguments of the current hook + savedArgs[cDepth].cArg = cArg; // current count of taken arguments + savedArgs[cDepth].cRet = cRet; // number of return values for the current hook + savedArgs[cDepth].cRetTmp = cRetTmp; + std::memcpy(&savedArgs[cDepth].oldArgs, args, maxArgs * sizeof(DWORD)); // values of the arguments + if (cRet) std::memcpy(&savedArgs[cDepth].oldRets, rets, maxRets * sizeof(DWORD)); // return values + + //devlog_f("\nSaved cArgs/cRet: %d / %d(%d)\n", DL_HOOK, savedArgs[cDepth].argCount, savedArgs[cDepth].cRet, cRetTmp); + //for (unsigned int i = 0; i < maxArgs; i++) { + // devlog_f("Saved Args/Rets: %d / %d\n", DL_HOOK, savedArgs[cDepth].oldArgs[i], ((i < maxRets) ? savedArgs[cDepth].oldRets[i] : -1)); + //} + } + callDepth++; + + devlog_f("Begin running hook, current depth: %d, current executable hook: %d\n", DL_HOOK, callDepth, currentRunHook); +} + +static void __stdcall RunSpecificHookScript(HookScript *hook) { + cArg = 0; + cRetTmp = 0; + if (hook->callback != -1) { + fo::func::executeProcedure(hook->prog.ptr, hook->callback); + } else { + hook->callback = RunScriptStartProc(&hook->prog); // run start + } +} + +void __stdcall RunHookScript(DWORD hook) { + cRet = 0; + if (!hooks[hook].empty()) { + if (callDepth > 8) { + fo::func::debug_printf("\n[SFALL] The hook ID: %d cannot be executed.", hook); + dlog_f("The hook %d cannot be executed due to exceeding depth limit\n", DL_MAIN, hook); + return; + } + currentRunHook = hook; + size_t hooksCount = hooks[hook].size(); + dlog_f("Running hook %d, which has %0d entries attached, depth: %d\n", DL_HOOK, hook, hooksCount, callDepth); + for (int i = hooksCount - 1; i >= 0; i--) { + RunSpecificHookScript(&hooks[hook][i]); + + //devlog_f("> Hook: %d, script entry: %d done\n", DL_HOOK, hook, i); + //devlog_f("> Check cArg/cRet: %d / %d(%d)\n", DL_HOOK, cArg, cRet, cRetTmp); + //for (unsigned int i = 0; i < maxArgs; i++) { + // devlog_f("> Check Args/Rets: %d / %d\n", DL_HOOK, args[i], ((i < maxRets) ? rets[i] : -1)); + //} + } + } else { + cArg = 0; + + devlog_f(">>> Try running hook ID: %d\n", DL_HOOK, hook); + } +} + +void __stdcall EndHook() { + devlog_f("End running hook %d, current depth: %d\n", DL_HOOK, currentRunHook, callDepth); + + callDepth--; + if (callDepth) { + if (callDepth <= maxDepth) { + // restore all saved values of the previous hook + int cDepth = callDepth - 1; + currentRunHook = savedArgs[cDepth].hookID; + argCount = savedArgs[cDepth].argCount; + cArg = savedArgs[cDepth].cArg; + cRet = savedArgs[cDepth].cRet; + cRetTmp = savedArgs[cDepth].cRetTmp; // also restore current count of the number of return values + std::memcpy(args, &savedArgs[cDepth].oldArgs, maxArgs * sizeof(DWORD)); + if (cRet) std::memcpy(rets, &savedArgs[cDepth].oldRets, maxRets * sizeof(DWORD)); + + //devlog_f("Restored cArgs/cRet: %d / %d(%d)\n", DL_HOOK, argCount, cRet, cRetTmp); + //for (unsigned int i = 0; i < maxArgs; i++) { + // devlog_f("Restored Args/Rets: %d / %d\n", DL_HOOK, args[i], ((i < maxRets) ? rets[i] : -1)); + //} + } + } else { + currentRunHook = -1; + } +} + +// BEGIN HOOKS +void __stdcall HookCommon::KeyPressHook(DWORD* dxKey, bool pressed, DWORD vKey) { + if (!IsGameLoaded() || !HookScripts::HookHasScript(HOOK_KEYPRESS)) { + return; + } + BeginHook(); + argCount = 3; + args[0] = (DWORD)pressed; + args[1] = *dxKey; + args[2] = vKey; + RunHookScript(HOOK_KEYPRESS); + if (cRet != 0) { + long retKey = rets[0]; + if (retKey > 0 && retKey < 264) *dxKey = retKey; + } + EndHook(); +} + +void __stdcall HookCommon::MouseClickHook(DWORD button, bool pressed) { + if (!IsGameLoaded() || !HookScripts::HookHasScript(HOOK_MOUSECLICK)) { + return; + } + BeginHook(); + argCount = 2; + args[0] = (DWORD)pressed; + args[1] = button; + RunHookScript(HOOK_MOUSECLICK); + EndHook(); +} + +static unsigned long previousGameMode = 0; + +void HookCommon::GameModeChangeHook(DWORD exit) { + if (HookScripts::HookHasScript(HOOK_GAMEMODECHANGE)) { + BeginHook(); + argCount = 2; + args[0] = exit; + args[1] = previousGameMode; + RunHookScript(HOOK_GAMEMODECHANGE); + EndHook(); + } + previousGameMode = GetLoopFlags(); +} +// END HOOKS + +void HookCommon::Reset() { + previousGameMode = 0; +} + +} diff --git a/sfall/Modules/HookScripts/Common.h b/sfall/Modules/HookScripts/Common.h new file mode 100644 index 00000000..a093849d --- /dev/null +++ b/sfall/Modules/HookScripts/Common.h @@ -0,0 +1,52 @@ +#pragma once + +#include "..\HookScripts.h" +#include "..\ScriptExtender.h" + +// Common variables and functions for hook script implementations + +namespace sfall +{ + +class HookCommon { +public: + static DWORD GetHSArgCount(); + static DWORD GetHSArg(); + static DWORD GetHSArgAt(DWORD id); + static DWORD* GetHSArgs(); + static void SetHSArg(DWORD id, DWORD value); + static void __stdcall SetHSReturn(DWORD d); + + static void GameModeChangeHook(DWORD exit); + static void __stdcall KeyPressHook(DWORD* dxKey, bool pressed, DWORD vKey); + static void __stdcall MouseClickHook(DWORD button, bool pressed); + + static void Reset(); +}; + +// Struct for registered hook script +struct HookScript { + ScriptProgram prog; + int callback; // procedure position in script's proc table + bool isGlobalScript; // false for hs_* scripts, true for gl* scripts +}; + +// All currently registered hook scripts +extern std::vector hooks[]; + +extern DWORD args[]; // current hook arguments +extern DWORD rets[]; // current hook return values + +extern DWORD argCount; +extern DWORD cArg; // how many arguments were taken by current hook script +extern DWORD cRet; // how many return values were set by current hook script +extern DWORD cRetTmp; // how many return values were set by specific hook script (when using register_hook) + +void __stdcall BeginHook(); +void __stdcall RunHookScript(DWORD hook); +void __stdcall EndHook(); + +#define HookBegin pushadc __asm call BeginHook popadc +#define HookEnd pushadc __asm call EndHook popadc + +} diff --git a/sfall/Modules/HookScripts/DeathHs.cpp b/sfall/Modules/HookScripts/DeathHs.cpp new file mode 100644 index 00000000..d0ef2501 --- /dev/null +++ b/sfall/Modules/HookScripts/DeathHs.cpp @@ -0,0 +1,165 @@ +#include "..\..\FalloutEngine\Fallout2.h" +#include "..\..\SafeWrite.h" +#include "..\HookScripts.h" +#include "Common.h" + +#include "DeathHs.h" + +namespace sfall +{ + +static bool registerHookDeathAnim1 = false; +static bool registerHookDeathAnim2 = false; + +static DWORD __fastcall CalcDeathAnimHook_Script(DWORD damage, fo::GameObject* target, fo::GameObject* attacker, fo::GameObject* weapon, int animation, int hitBack) { + BeginHook(); + argCount = 5; // was 4 + + args[1] = (DWORD)attacker; + args[2] = (DWORD)target; + args[3] = damage; + + // weapon_ptr + args[0] = (weapon) ? weapon->protoId : -1; // attack is unarmed + + bool createNewObj = false; + if (registerHookDeathAnim1) { + args[4] = -1; // set 'no anim' for combined hooks use + RunHookScript(HOOK_DEATHANIM1); + if (cRet > 0) { + DWORD pid = rets[0]; + args[0] = pid; // replace for HOOK_DEATHANIM2 + fo::GameObject* object = nullptr; + if (fo::func::obj_pid_new((fo::GameObject*)&object, pid) != -1) { // create new object + createNewObj = true; + weapon = object; // replace pointer with newly created weapon object + } + cRet = 0; // reset rets from HOOK_DEATHANIM1 + } + } + + DWORD animDeath = fo::func::pick_death(attacker, target, weapon, damage, animation, hitBack); // vanilla pick death + + if (registerHookDeathAnim2) { + args[4] = animDeath; + RunHookScript(HOOK_DEATHANIM2); + + if (cRet > 0) animDeath = rets[0]; + } + EndHook(); + + if (createNewObj) fo::func::obj_erase_object(weapon, 0); // delete created object + + return animDeath; +} + +static void __declspec(naked) CalcDeathAnimHook() { + __asm { + push ecx; + push edx; + push [esp + 4 + 12]; // hit_from_back + push [esp + 4 + 12]; // animation + push ebx; // weapon_ptr + push eax; // attacker + call CalcDeathAnimHook_Script; // ecx - damage, edx - target + pop edx; + pop ecx; + retn 8; + } +} + +static void __declspec(naked) CalcDeathAnim2Hook() { + __asm { + call fo::funcoffs::check_death_; // call original function + mov ebx, eax; + call BeginHook; + mov eax, [esp + 60]; + mov args[4], eax; // attacker + mov args[8], esi; // target + mov eax, [esp + 12]; + mov args[12], eax; // dmgAmount + mov args[16], ebx; // calculated animID + } + + argCount = 5; + args[0] = -1; // weaponPid + RunHookScript(HOOK_DEATHANIM2); + + __asm { + cmp cRet, 1; + cmovge ebx, rets[0]; + call EndHook; + mov eax, ebx; + retn; + } +} + +static void __declspec(naked) OnDeathHook() { + using namespace fo::Fields; + __asm { + push edx; + call BeginHook; + mov args[0], esi; + } + + argCount = 1; + RunHookScript(HOOK_ONDEATH); + EndHook(); + + __asm { + pop edx; + // engine code + mov eax, [esi + protoId]; + mov ebp, ebx; + retn; + } +} + +static void __declspec(naked) OnDeathHook2() { + __asm { + call fo::funcoffs::partyMemberRemove_; + HookBegin; + mov args[0], esi; + pushadc; + } + + argCount = 1; + RunHookScript(HOOK_ONDEATH); + EndHook(); + + __asm popadc; + __asm retn; +} + +void Inject_DeathAnim1Hook() { + registerHookDeathAnim1 = true; + if (!registerHookDeathAnim2) { + HookCall(0x4109DE, CalcDeathAnimHook); + } +} + +void Inject_DeathAnim2Hook() { + const DWORD calcDeathAnim2HkAddr[] = { + 0x410981, // show_damage_to_object_ + 0x4109A1, // show_damage_to_object_ + 0x4109BF // show_damage_to_object_ + }; + HookCalls(CalcDeathAnim2Hook, calcDeathAnim2HkAddr); + registerHookDeathAnim2 = true; + if (!registerHookDeathAnim1) { + HookCall(0x4109DE, CalcDeathAnimHook); // show_damage_to_object_ + } +} + +void Inject_OnDeathHook() { + MakeCall(0x42DA6D, OnDeathHook); // critter_kill_ + HookCall(0x425161, OnDeathHook2); // damage_object_ +} + +void InitDeathHookScripts() { + HookScripts::LoadHookScript("hs_deathanim1", HOOK_DEATHANIM1); + HookScripts::LoadHookScript("hs_deathanim2", HOOK_DEATHANIM2); + HookScripts::LoadHookScript("hs_ondeath", HOOK_ONDEATH); +} + +} diff --git a/sfall/Modules/HookScripts/DeathHs.h b/sfall/Modules/HookScripts/DeathHs.h new file mode 100644 index 00000000..d76d1cfb --- /dev/null +++ b/sfall/Modules/HookScripts/DeathHs.h @@ -0,0 +1,14 @@ +#pragma once + +// Hook scripts related to critter Death + +namespace sfall +{ + +void InitDeathHookScripts(); + +void Inject_DeathAnim1Hook(); +void Inject_DeathAnim2Hook(); +void Inject_OnDeathHook(); + +} diff --git a/sfall/Modules/HookScripts/HexBlockingHs.cpp b/sfall/Modules/HookScripts/HexBlockingHs.cpp new file mode 100644 index 00000000..6124ee8d --- /dev/null +++ b/sfall/Modules/HookScripts/HexBlockingHs.cpp @@ -0,0 +1,145 @@ +#include "..\..\FalloutEngine\Fallout2.h" +#include "..\..\SafeWrite.h" +#include "..\HookScripts.h" +#include "Common.h" + +#include "HexBlockingHs.h" + +namespace sfall +{ + +static void __declspec(naked) HexMoveBlockingHook() { + static const DWORD _obj_blocking_at = 0x48B84E; + __asm { + HookBegin; + mov args[0], eax; + mov args[4], edx; + mov args[8], ebx; + push return; + // engine code + push ecx; + push esi; + push edi; + push ebp; + mov ecx, eax; + // end engine code + jmp _obj_blocking_at; +return: + mov args[12], eax; + mov ebx, eax; + push ecx; + } + + argCount = 4; + RunHookScript(HOOK_HEXMOVEBLOCKING); + + __asm { + cmp cRet, 1; + cmovge ebx, rets[0]; + call EndHook; + mov eax, ebx; + pop ecx; + retn; + } +} + +static void __declspec(naked) HexAIBlockingHook() { + __asm { + HookBegin; + mov args[0], eax; + mov args[4], edx; + mov args[8], ebx; + call fo::funcoffs::obj_ai_blocking_at_; + mov args[12], eax; + mov ebx, eax; + push ecx; + } + + argCount = 4; + RunHookScript(HOOK_HEXAIBLOCKING); + + __asm { + cmp cRet, 1; + cmovge ebx, rets[0]; + call EndHook; + mov eax, ebx; + pop ecx; + retn; + } +} + +static void __declspec(naked) HexShootBlockingHook() { + __asm { + HookBegin; + mov args[0], eax; + mov args[4], edx; + mov args[8], ebx; + call fo::funcoffs::obj_shoot_blocking_at_; + mov args[12], eax; + mov ebx, eax; + push ecx; + } + + argCount = 4; + RunHookScript(HOOK_HEXSHOOTBLOCKING); + + __asm { + cmp cRet, 1; + cmovge ebx, rets[0]; + call EndHook; + mov eax, ebx; + pop ecx; + retn; + } +} + +static void __declspec(naked) HexSightBlockingHook() { + __asm { + HookBegin; + mov args[0], eax; + mov args[4], edx; + mov args[8], ebx; + call fo::funcoffs::obj_sight_blocking_at_; + mov args[12], eax; + mov ebx, eax; + push ecx; + } + + argCount = 4; + RunHookScript(HOOK_HEXSIGHTBLOCKING); + + __asm { + cmp cRet, 1; + cmovge ebx, rets[0]; + call EndHook; + mov eax, ebx; + pop ecx; + retn; + } +} + +void Inject_HexSightBlockHook() { + SafeWrite32(0x413979, (DWORD)&HexSightBlockingHook); +} + +void Inject_HexShootBlockHook() { + const DWORD shootBlockingAddr[] = {0x4C1A88, 0x423178, 0x4232D4, 0x423B4D, 0x426CF8, 0x42A570}; + SafeWriteBatch((DWORD)&HexShootBlockingHook, shootBlockingAddr); +} + +void Inject_HexIABlockHook() { + SafeWrite32(0x42A0A4, (DWORD)&HexAIBlockingHook); +} + +void Inject_HexMoveBlockHook() { + MakeJump(0x48B848, HexMoveBlockingHook); +} + +void InitHexBlockingHookScripts() { + HookScripts::LoadHookScript("hs_hexmoveblocking", HOOK_HEXMOVEBLOCKING); + HookScripts::LoadHookScript("hs_hexaiblocking", HOOK_HEXAIBLOCKING); + HookScripts::LoadHookScript("hs_hexshootblocking", HOOK_HEXSHOOTBLOCKING); + HookScripts::LoadHookScript("hs_hexsightblocking", HOOK_HEXSIGHTBLOCKING); +} + +} diff --git a/sfall/Modules/HookScripts/HexBlockingHs.h b/sfall/Modules/HookScripts/HexBlockingHs.h new file mode 100644 index 00000000..659e4c9f --- /dev/null +++ b/sfall/Modules/HookScripts/HexBlockingHs.h @@ -0,0 +1,15 @@ +#pragma once + +// Hook scripts related to hex blocking checks (during pathfinding, etc.) + +namespace sfall +{ + +void InitHexBlockingHookScripts(); + +void Inject_HexSightBlockHook(); +void Inject_HexShootBlockHook(); +void Inject_HexIABlockHook(); +void Inject_HexMoveBlockHook(); + +} \ No newline at end of file diff --git a/sfall/Modules/HookScripts/InventoryHs.cpp b/sfall/Modules/HookScripts/InventoryHs.cpp new file mode 100644 index 00000000..4e1b9d57 --- /dev/null +++ b/sfall/Modules/HookScripts/InventoryHs.cpp @@ -0,0 +1,704 @@ +#include "..\..\FalloutEngine\Fallout2.h" +#include "..\..\SafeWrite.h" +#include "..\HookScripts.h" +#include "..\Inventory.h" +#include "..\PartyControl.h" +#include "Common.h" + +#include "InventoryHs.h" + +namespace sfall +{ +/* +static void RemoveInvenObjHook_Script(fo::GameObject* source, fo::GameObject* item, long count, long rmType) { + BeginHook(); + argCount = 5; + + args[0] = (DWORD)source; + args[1] = (DWORD)item; + args[2] = count; + args[3] = rmType; // RMOBJ_* + args[4] = 0; // target only from item_move_func_ + + RunHookScript(HOOK_REMOVEINVENOBJ); + EndHook(); +} + +void RemoveInvenObjHook_Invoke(fo::GameObject* source, fo::GameObject* item, long count, long rmType) { + if (HookScripts::HookHasScript(HOOK_REMOVEINVENOBJ)) RemoveInvenObjHook_Script(source, item, count, rmType); +} +*/ + +static long rmObjType = -1; + +void SetRemoveObjectType(long rmType) { + rmObjType = rmType; +} + +static void __declspec(naked) RemoveObjHook() { + static const DWORD RemoveObjHookRet = 0x477497; + __asm { + mov ecx, [esp + 8]; // call addr + cmp rmObjType, -1; + cmovne ecx, rmObjType; + mov rmObjType, -1; + HookBegin; + mov args[0], eax; // source + mov args[4], edx; // item + mov args[8], ebx; // count + mov args[12], ecx; // RMOBJ_* (called func) + xor esi, esi; + xor ecx, 0x47761D; // from item_move_func_ + cmovz esi, ebp; // target + mov args[16], esi; + push edi; + push ebp; + push eax; + push edx; + } + + argCount = 5; + RunHookScript(HOOK_REMOVEINVENOBJ); + EndHook(); + + __asm { + pop edx; + pop eax; + sub esp, 0x0C; + jmp RemoveObjHookRet; + } +} + +static void __declspec(naked) MoveCostHook() { + __asm { + HookBegin; + mov args[0], eax; + mov args[4], edx; + call fo::funcoffs::critter_compute_ap_from_distance_; + mov args[8], eax; + push ebx; + push ecx; + mov ebx, eax; + } + + argCount = 3; + RunHookScript(HOOK_MOVECOST); + + __asm { + cmp cRet, 1; + cmovge ebx, rets[0]; + call EndHook; + mov eax, ebx; + pop ecx; + pop ebx; + retn; + } +} + +static int __fastcall SwitchHandHook_Script(fo::GameObject* item, fo::GameObject* itemReplaced, DWORD addr) { + if (itemReplaced && fo::func::item_get_type(itemReplaced) == fo::item_type_weapon && fo::func::item_get_type(item) == fo::item_type_ammo) { + return -1; // to prevent inappropriate hook call after dropping ammo on weapon + } + + BeginHook(); + argCount = 3; + + args[0] = (addr < 0x47136D) ? 1 : 2; // slot: 1 - left, 2 - right + args[1] = (DWORD)item; + args[2] = (DWORD)itemReplaced; + + RunHookScript(HOOK_INVENTORYMOVE); + int result = PartyControl::SwitchHandHook(item); + if (result != -1) { + cRetTmp = 0; + HookCommon::SetHSReturn(result); + } + result = (cRet > 0) ? rets[0] : -1; + EndHook(); + + return result; +} + +/* + This hook is called every time an item is placed into either hand slot via inventory screen drag&drop + If switch_hand_ function is not called, item is not placed anywhere (it remains in main inventory) +*/ +static void __declspec(naked) SwitchHandHook() { + __asm { + pushadc; + mov ecx, eax; // item being moved + mov edx, [edx]; // other item + mov eax, [esp + 12]; // back address + push eax; + call SwitchHandHook_Script; + cmp eax, -1; // ret value + popadc; + jne skip; + jmp fo::funcoffs::switch_hand_; +skip: + retn; + } +} + +/* Common inventory move hook */ +static int __fastcall InventoryMoveHook_Script(DWORD itemReplace, DWORD item, int type) { + BeginHook(); + argCount = 3; + + args[0] = type; // event type + args[1] = item; // item being dropped + args[2] = itemReplace; // item being replaced here + + RunHookScript(HOOK_INVENTORYMOVE); + + int result = (cRet > 0) ? rets[0] : -1; // -1 - can move + EndHook(); + + return result; +} + +// This hack is called when an armor is dropped into the armor slot at inventory screen +static void __declspec(naked) UseArmorHack() { + static const DWORD UseArmorHack_back = 0x4713AF; // normal operation (old 0x4713A9) + static const DWORD UseArmorHack_skip = 0x471481; // skip code, prevent wearing armor + __asm { + mov ecx, ds:[FO_VAR_i_worn]; // replacement item (override code) + mov edx, [esp + 0x58 - 0x40]; // item + push ecx; + push 3; // event: armor slot + call InventoryMoveHook_Script; // ecx - replacement item + cmp eax, -1; // ret value + pop ecx; + jne skip; + jmp UseArmorHack_back; +skip: + jmp UseArmorHack_skip; + } +} + +static void __declspec(naked) MoveInventoryHook() { + __asm { + pushadc; + xor eax, eax; + mov ecx, eax; // no item replace + cmp dword ptr ds:[FO_VAR_curr_stack], 0; + jle noCont; + mov ecx, eax; // contaner ptr + mov eax, 5; +noCont: + push eax; // event: 0 - main backpack, 5 - contaner + call InventoryMoveHook_Script; // edx - item + cmp eax, -1; // ret value + popadc; + jne skip; + jmp fo::funcoffs::item_add_force_; +skip: + retn; + } +} + +// Hooks into dropping item from inventory to ground +// - allows to prevent item being dropped if 0 is returned with set_sfall_return +// - called for every item when dropping multiple items in stack (except caps) +// - when dropping caps it called always once +// - if 0 is returned while dropping caps, selected amount - 1 will still disappear from inventory (fixed) +static DWORD nextHookDropSkip = 0; +static int dropResult = -1; +static const DWORD InvenActionObjDropRet = 0x473874; +static void __declspec(naked) InvenActionCursorObjDropHook() { + if (nextHookDropSkip) { + nextHookDropSkip = 0; + goto skipHook; + } else { + __asm { + pushadc; + xor ecx, ecx; // no itemReplace + push 6; // event: item drop ground + call InventoryMoveHook_Script; // edx - item + mov dropResult, eax; // ret value + popadc; + cmp dword ptr [esp], 0x47379A + 5; // caps call address + jz capsMultiDrop; + } + } + + if (dropResult == -1) { +skipHook: + __asm call fo::funcoffs::obj_drop_; + } + __asm retn; + +/* for only caps multi drop */ +capsMultiDrop: + if (dropResult == -1) { + nextHookDropSkip = 1; + __asm call fo::funcoffs::item_remove_mult_; + __asm retn; + } + __asm add esp, 4; + __asm jmp InvenActionObjDropRet; // no caps drop +} + +static void __declspec(naked) InvenActionExplosiveDropHack() { + __asm { + pushadc; + xor ecx, ecx; // no itemReplace + push 6; // event: item drop ground + call InventoryMoveHook_Script; // edx - item + cmp eax, -1; // ret value + popadc; + jnz noDrop; + mov dword ptr ds:[FO_VAR_dropped_explosive], ebp; // overwritten engine code (ebp = 1) + mov nextHookDropSkip, ebp; + retn; +noDrop: + add esp, 4; + jmp InvenActionObjDropRet; // no drop + } +} + +static int __fastcall DropIntoContainer(DWORD ptrCont, DWORD item, DWORD addrCall) { + int type = 5; // event: move to container (Crafty compatibility) + + if (addrCall == 0x47147C + 5) { // drop out contaner + ptrCont = 0; + type = 0; // event: move to main backpack + } + return InventoryMoveHook_Script(ptrCont, item, type); +} + +static void __declspec(naked) DropIntoContainerHack() { + static const DWORD DropIntoContainer_back = 0x47649D; // normal operation + static const DWORD DropIntoContainer_skip = 0x476503; // exit drop_into_container_ + __asm { + test ecx, ecx; + js skipDrop; + push ecx; //pushadc; + mov edx, esi; // item + mov ecx, ebp; // contaner ptr + push [esp + 0x10 + 4]; // call address + call DropIntoContainer; + cmp eax, -1; // ret value + pop ecx; //popadc; + jne skipDrop; + jmp DropIntoContainer_back; +skipDrop: + mov eax, -1; + jmp DropIntoContainer_skip; + } +} + +static void __declspec(naked) DropIntoContainerHandSlotHack() { + static const DWORD DropIntoContainerRet = 0x471481; + __asm { + call fo::funcoffs::drop_into_container_; + jmp DropIntoContainerRet; + } +} + +static void __declspec(naked) DropAmmoIntoWeaponHook() { + static const DWORD DropAmmoIntoWeaponHack_return = 0x476643; + __asm { + pushadc; + mov ecx, ebp; // weapon ptr + mov edx, [esp + 16]; // item var: ammo_ + push 4; // event: weapon reloading + call InventoryMoveHook_Script; + cmp eax, -1; // ret value + jne donothing; + popadc; + jmp fo::funcoffs::item_w_can_reload_; +donothing: + add esp, 4*4; // destroy all pushed values and return address + xor eax, eax; // result 0 + jmp DropAmmoIntoWeaponHack_return; + } +} + +static void __declspec(naked) PickupObjectHack() { + __asm { + cmp edi, ds:[FO_VAR_obj_dude]; + je runHook; + xor edx, edx; // engine handler + retn; +runHook: + push eax; + push ecx; + mov edx, ecx; + xor ecx, ecx; // no itemReplace + push 7; // event: item pickup + call InventoryMoveHook_Script; // edx - item + mov edx, eax; // ret value + pop ecx; + pop eax; + inc edx; // 0 - engine handler, otherwise cancel pickup + retn; + } +} + +static void __declspec(naked) InvenPickupHook() { + __asm { + call fo::funcoffs::mouse_click_in_; + test eax, eax; + jnz runHook; + retn; +runHook: + cmp dword ptr ds:[FO_VAR_curr_stack], 0; + jnz skip; + mov edx, [esp + 0x58 - 0x40 + 4]; // item + xor ecx, ecx; // no itemReplace + push 8; // event: drop item on character portrait + call InventoryMoveHook_Script; + cmp eax, -1; // ret value + je skip; + xor eax, eax; // 0 - cancel, otherwise engine handler +skip: + retn; + } +} + +/* Common InvenWield script hooks */ +static long __fastcall InvenWieldHook_Script(fo::GameObject* critter, fo::GameObject* item, long slot, long isWield, long isRemove) { + if (!isWield) { + // for the critter, the right slot is always the active slot + if (slot == fo::INVEN_TYPE_LEFT_HAND && critter != *fo::ptr::obj_dude) return 1; + // check the current active slot for the player + if (slot != fo::INVEN_TYPE_WORN && critter == *fo::ptr::obj_dude) { + long _slot = (slot != fo::INVEN_TYPE_LEFT_HAND); + if (_slot != *fo::ptr::itemCurrentItem) return 1; // item in non-active slot + } + } + BeginHook(); + argCount = 5; + + args[0] = (DWORD)critter; + args[1] = (DWORD)item; + args[2] = slot; + args[3] = isWield; // unwield/wield event + args[4] = isRemove; + + RunHookScript(HOOK_INVENWIELD); + + long result = (cRet == 0 || rets[0] == -1); + EndHook(); + + return result; // 1 - use engine handler +} + +static __declspec(noinline) bool InvenWieldHook_ScriptPart(long isWield, long isRemove = 0) { + argCount = 5; + + args[3] = isWield; // unwield/wield event + args[4] = isRemove; + + RunHookScript(HOOK_INVENWIELD); + + bool result = (cRet == 0 || rets[0] == -1); + EndHook(); + + return result; // true - use engine handler +} + +static void __declspec(naked) InvenWieldFuncHook() { + __asm { + HookBegin; + mov args[0], eax; // critter + mov args[4], edx; // item + mov args[8], ebx; // slot + pushad; + } + + // right hand slot? + if (args[2] != fo::INVEN_TYPE_RIGHT_HAND && fo::func::item_get_type((fo::GameObject*)args[1]) != fo::item_type_armor) { + args[2] = fo::INVEN_TYPE_LEFT_HAND; + } + InvenWieldHook_ScriptPart(1); // wield event + + __asm { + test al, al; + popad; + jz skip; + jmp fo::funcoffs::invenWieldFunc_; +skip: + mov eax, -1; + retn; + } +} + +// called when unwielding weapons +static void __declspec(naked) InvenUnwieldFuncHook() { + __asm { + HookBegin; + mov args[0], eax; // critter + mov args[8], edx; // slot + pushadc; + } + + // set slot + if (args[2] == 0) { // left hand slot? + args[2] = fo::INVEN_TYPE_LEFT_HAND; + } + + // get item + args[1] = (DWORD)fo::util::GetItemPtrSlot((fo::GameObject*)args[0], (fo::InvenType)args[2]); + + InvenWieldHook_ScriptPart(0); // unwield event + + __asm { + test al, al; + popadc; + jz skip; + jmp fo::funcoffs::invenUnwieldFunc_; +skip: + mov eax, -1; + retn; + } +} + +static void __declspec(naked) CorrectFidForRemovedItemHook() { + using namespace fo::ObjectFlag; + __asm { + HookBegin; + mov args[0], eax; // critter + mov args[4], edx; // item + mov args[8], ebx; // item flag + pushadc; + } + + // set slot + if (args[2] & fo::ObjectFlag::Right_Hand) { // right hand slot + args[2] = fo::INVEN_TYPE_RIGHT_HAND; + } else if (args[2] & fo::ObjectFlag::Left_Hand) { // left hand slot + args[2] = fo::INVEN_TYPE_LEFT_HAND; + } else { + args[2] = fo::INVEN_TYPE_WORN; // armor slot + } + + InvenWieldHook_ScriptPart(0, 1); // unwield event (armor by default) + + // engine handler is not overridden + __asm { + popadc; + jmp fo::funcoffs::correctFidForRemovedItem_; + } +} + +long InvenWieldHook_Invoke(fo::GameObject* critter, fo::GameObject* item, long flags) { + if (!HookScripts::HookHasScript(HOOK_INVENWIELD)) return 1; + + long slot = fo::INVEN_TYPE_WORN; + if (flags & fo::ObjectFlag::Right_Hand) { // right hand slot + slot = fo::INVEN_TYPE_RIGHT_HAND; + } else if (flags & fo::ObjectFlag::Left_Hand) { // left hand slot + slot = fo::INVEN_TYPE_LEFT_HAND; + } + return InvenWieldHook_Script(critter, item, slot, 0, 0); +} + +static void __declspec(naked) item_drop_all_hack() { + using namespace fo::ObjectFlag; + __asm { + mov ecx, 1; + push eax; + mov [esp + 0x40 - 0x2C + 8], ecx; // itemIsEquipped + push ecx; // remove event + push 0; // unwield event + inc ecx; // INVEN_TYPE_LEFT_HAND (2) + test ah, Left_Hand >> 24; + jnz skip; + test ah, Worn >> 24; + setz cl; // set INVEN_TYPE_WORN or INVEN_TYPE_RIGHT_HAND +skip: + push ecx; // slot + mov edx, esi; // item + mov ecx, edi; // critter + call InvenWieldHook_Script; + //mov [esp + 0x40 - 0x2C + 8], eax; // itemIsEquipped (eax - hook return result) + pop eax; + retn; + } +} + +static bool hookInvenWieldIsInject = false; + +// called from bugfixes for obj_drop_ +void __declspec(naked) InvenUnwield_HookDrop() { // ecx - critter, edx - item + using namespace fo; + using namespace Fields; + using namespace ObjectFlag; + __asm { + cmp hookInvenWieldIsInject, 1; + je runHook; + retn; +runHook: + pushadc; + mov eax, INVEN_TYPE_LEFT_HAND; + test byte ptr [edx + flags + 3], Left_Hand >> 24; + jnz isLeft; + test byte ptr [edx + flags + 3], Worn >> 24; + setz al; // set INVEN_TYPE_WORN or INVEN_TYPE_RIGHT_HAND +isLeft: + push 1; // remove event + push 0; // unwield event + push eax; // slot + call InvenWieldHook_Script; // ecx - critter, edx - item + // engine handler is not overridden + popadc; + retn; + } +} + +// called from bugfixes for op_move_obj_inven_to_obj_ +void __declspec(naked) InvenUnwield_HookMove() { // eax - item, edx - critter + __asm { + cmp hookInvenWieldIsInject, 1; + je runHook; + retn; +runHook: + pushadc; + mov ecx, edx; + mov edx, eax; + push 1; // remove event + xor eax, eax; + push eax; // unwield event + push eax; // slot + call InvenWieldHook_Script; // ecx - critter, edx - item + // engine handler is not overridden + popadc; + retn; + } +} + +// called when unwelding dude weapon and armor +static void __declspec(naked) op_move_obj_inven_to_obj_hook() { + using namespace fo; + using namespace ObjectFlag; + __asm { + cmp eax, ds:[FO_VAR_obj_dude]; + je runHook; + jmp fo::funcoffs::item_move_all_; +runHook: + push eax; + push edx; + mov ecx, eax; // keep source + mov edx, ds:[FO_VAR_itemCurrentItem]; // get player's active slot + test edx, edx; + jz left; + call fo::funcoffs::inven_right_hand_; + jmp skip; +left: + call fo::funcoffs::inven_left_hand_; +skip: + test eax, eax; + jz noWeapon; + push 1; // remove event + push 0; // unwield event + mov ebx, INVEN_TYPE_LEFT_HAND; + sub ebx, edx; + push ebx; // slot: INVEN_TYPE_LEFT_HAND or INVEN_TYPE_RIGHT_HAND + mov edx, eax; // weapon + mov ebx, ecx; // keep source + call InvenWieldHook_Script; // ecx - source + // engine handler is not overridden +noWeapon: + mov edx, [esp + 0x30 - 0x20 + 12]; // armor + test edx, edx; + jz noArmor; + xor eax, eax; + push 1; // remove event + push eax; // unwield event + push eax; // slot: INVEN_TYPE_WORN + mov ecx, ebx; // source + call InvenWieldHook_Script; + // engine handler is not overridden +noArmor: + pop edx; + pop eax; + jmp fo::funcoffs::item_move_all_; + } +} + +void AdjustFidHook(DWORD vanillaFid) { + if (!HookScripts::HookHasScript(HOOK_ADJUSTFID)) return; + + BeginHook(); + argCount = 2; + + args[0] = vanillaFid; + args[1] = *fo::ptr::i_fid; // modified FID by sfall code + RunHookScript(HOOK_ADJUSTFID); + + if (cRet > 0) { + *fo::ptr::i_fid = rets[0]; + } + EndHook(); +} + +void Inject_RemoveInvenObjHook() { + MakeJump(0x477492, RemoveObjHook); // old 0x477490 +} + +void Inject_MoveCostHook() { + const DWORD moveCostHkAddr[] = {0x417665, 0x44B88A}; + HookCalls(MoveCostHook, moveCostHkAddr); +} + +void Inject_InventoryMoveHook() { + const DWORD switchHandHkAddr[] = { + 0x4712E3, // left slot + 0x47136D // right slot + }; + HookCalls(SwitchHandHook, switchHandHkAddr); + MakeJump(0x4713A9, UseArmorHack); // old 0x4713A3 + MakeJump(0x476491, DropIntoContainerHack); + const DWORD dropIntoContHandHkAddr[] = {0x471338, 0x4712AB}; + MakeJumps(DropIntoContainerHandSlotHack, dropIntoContHandHkAddr); + HookCall(0x471200, MoveInventoryHook); + HookCall(0x476549, DropAmmoIntoWeaponHook); // old 0x476588 + const DWORD actionCurObjDropHkAddr[] = { + 0x473851, 0x47386F, + 0x47379A // caps multi drop + }; + HookCalls(InvenActionCursorObjDropHook, actionCurObjDropHkAddr); + MakeCall(0x473807, InvenActionExplosiveDropHack, 1); // drop active explosives + + MakeCall(0x49B660, PickupObjectHack); + SafeWrite32(0x49B665, 0x850FD285); // test edx, edx + SafeWrite32(0x49B669, 0xC2); // jnz 0x49B72F + SafeWrite8(0x49B66E, 0xFE); // cmp edi > cmp esi + + HookCall(0x471457, InvenPickupHook); +} + +void Inject_InvenWieldHook() { + const DWORD invWieldFuncHkAddr[] = { + 0x47275E, // inven_wield_ + 0x495FDF // partyMemberCopyLevelInfo_ + }; + HookCalls(InvenWieldFuncHook, invWieldFuncHkAddr); + const DWORD invUnwieldFuncHkAddr[] = { + 0x45967D, // op_metarule_ + 0x472A5A, // inven_unwield_ + 0x495F0B // partyMemberCopyLevelInfo_ + }; + HookCalls(InvenUnwieldFuncHook, invUnwieldFuncHkAddr); + const DWORD fidRemovedItemHkAddr[] = { + 0x45680C, // op_rm_obj_from_inven_ + 0x45C4EA // op_move_obj_inven_to_obj_ + }; + HookCalls(CorrectFidForRemovedItemHook, fidRemovedItemHkAddr); + HookCall(0x45C4F6, op_move_obj_inven_to_obj_hook); + MakeCall(0x4778AF, item_drop_all_hack, 3); + + hookInvenWieldIsInject = true; +} + +void InitInventoryHookScripts() { + HookScripts::LoadHookScript("hs_removeinvenobj", HOOK_REMOVEINVENOBJ); + HookScripts::LoadHookScript("hs_movecost", HOOK_MOVECOST); + HookScripts::LoadHookScript("hs_inventorymove", HOOK_INVENTORYMOVE); + HookScripts::LoadHookScript("hs_invenwield", HOOK_INVENWIELD); + HookScripts::LoadHookScript("hs_adjustfid", HOOK_ADJUSTFID); +} + +} diff --git a/sfall/Modules/HookScripts/InventoryHs.h b/sfall/Modules/HookScripts/InventoryHs.h new file mode 100644 index 00000000..4219cacb --- /dev/null +++ b/sfall/Modules/HookScripts/InventoryHs.h @@ -0,0 +1,24 @@ +#pragma once + +// Inventory-Related hook scripts + +namespace sfall +{ + +void InitInventoryHookScripts(); + +void Inject_RemoveInvenObjHook(); +void Inject_MoveCostHook(); +void Inject_InventoryMoveHook(); +void Inject_InvenWieldHook(); + +long InvenWieldHook_Invoke(fo::GameObject* critter, fo::GameObject* item, long flags); + +void InvenUnwield_HookDrop(); +void InvenUnwield_HookMove(); + +void SetRemoveObjectType(long rmType); + +void AdjustFidHook(DWORD vanillaFid); + +} diff --git a/sfall/Modules/HookScripts/MiscHs.cpp b/sfall/Modules/HookScripts/MiscHs.cpp new file mode 100644 index 00000000..980503e0 --- /dev/null +++ b/sfall/Modules/HookScripts/MiscHs.cpp @@ -0,0 +1,271 @@ +#include "..\..\FalloutEngine\Fallout2.h" +#include "..\..\SafeWrite.h" +#include "..\HookScripts.h" +#include "..\Karma.h" +#include "..\LoadGameHook.h" +#include "Common.h" + +#include "MiscHs.h" + +namespace sfall +{ + +// 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(register fo::GameObject* source, register fo::GameObject* target, DWORD callAddr) { + bool barterIsParty = (*fo::ptr::dialog_target_is_party != 0); + long computeCost = fo::func::barter_compute_value(source, target); + + BeginHook(); + argCount = 10; + + args[0] = (DWORD)source; + args[1] = (DWORD)target; + args[2] = !barterIsParty ? computeCost : 0; + + fo::GameObject* bTable = (fo::GameObject*)*fo::ptr::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::ptr::ptable; + args[6] = (DWORD)pTable; + + long pcCost = 0; + if (barterIsParty) { + args[7] = pcCost; + pcCost = fo::func::item_total_weight(pTable); + } else { + args[7] = pcCost = fo::func::item_total_cost(pTable); + } + + args[8] = (DWORD)(callAddr == 0x474D51); // offers button pressed + args[9] = (DWORD)barterIsParty; + + RunHookScript(HOOK_BARTERPRICE); + + bool isPCHook = (callAddr == -1); + long cost = isPCHook ? pcCost : computeCost; + if (!barterIsParty && cRet > 0) { + if (isPCHook) { + if (cRet > 1) cost = rets[1]; // new cost for pc + } else if ((long)rets[0] > -1) { + cost = rets[0]; // new cost for npc + } + } + EndHook(); + return cost; +} + +static void __declspec(naked) BarterPriceHook() { + __asm { + push edx; + push ecx; + //------- + push [esp + 8]; // address on call stack + mov ecx, eax; // source + call BarterPriceHook_Script; // edx - target + pop ecx; + pop edx; + retn; + } +} + +static DWORD offersGoodsCost; // keep last cost for pc +static void __declspec(naked) PC_BarterPriceHook() { + __asm { + push edx; + push ecx; + //------- + push -1; // address on call stack + mov ecx, dword ptr ds:[FO_VAR_obj_dude]; // source + mov edx, dword ptr ds:[FO_VAR_target_stack]; // target + call BarterPriceHook_Script; + pop ecx; + pop edx; + mov offersGoodsCost, eax; + retn; + } +} + +static void __declspec(naked) OverrideCost_BarterPriceHook() { + __asm { + mov eax, offersGoodsCost; + retn; + } +} + +static void __declspec(naked) UseSkillHook() { + __asm { + HookBegin; + mov args[0], eax; // user + mov args[4], edx; // target + mov args[8], ebx; // skill id + mov args[12], ecx; // skill bonus + pushadc; + } + + argCount = 4; + RunHookScript(HOOK_USESKILL); + + __asm { + popadc; + cmp cRet, 1; + jl defaultHandler; + cmp rets[0], -1; + je defaultHandler; + mov eax, rets[0]; + HookEnd; + retn; +defaultHandler: + HookEnd; + jmp fo::funcoffs::skill_use_; + } +} + +static void __declspec(naked) StealCheckHook() { + __asm { + HookBegin; + mov args[0], eax; // thief + mov args[4], edx; // target + mov args[8], ebx; // item + mov args[12], ecx; // is planting + pushadc; + } + + argCount = 4; + RunHookScript(HOOK_STEAL); + + __asm { + popadc; + cmp cRet, 1; + jl defaultHandler; + cmp rets[0], -1; + je defaultHandler; + mov eax, rets[0]; + HookEnd; + retn; +defaultHandler: + HookEnd; + jmp fo::funcoffs::skill_check_stealing_; + } +} + +static __forceinline long PerceptionRangeHook_Script(fo::GameObject* watcher, fo::GameObject* target, int type, long result) { + BeginHook(); + argCount = 4; + + args[0] = (DWORD)watcher; + args[1] = (DWORD)target; + args[2] = result; // engine result + args[3] = type; + + RunHookScript(HOOK_WITHINPERCEPTION); + + if (cRet > 0) result = rets[0]; + EndHook(); + + return result; +} + +long PerceptionRangeHook_Invoke(fo::GameObject* watcher, fo::GameObject* target, long type, long result) { + if (!HookScripts::HookHasScript(HOOK_WITHINPERCEPTION)) return result; + return PerceptionRangeHook_Script(watcher, target, type, result); +} + +static long __fastcall PerceptionRange_Hook(fo::GameObject* watcher, fo::GameObject* target, int type) { + return PerceptionRangeHook_Script(watcher, target, type, fo::func::is_within_perception(watcher, target)); +} + +static void __declspec(naked) PerceptionRangeHook() { + __asm { + push ecx; + push 0; + mov ecx, eax; + call PerceptionRange_Hook; + pop ecx; + retn; + } +} + +static void __declspec(naked) PerceptionRangeSeeHook() { + __asm { + push ecx; + push 1; + mov ecx, eax; + call PerceptionRange_Hook; + pop ecx; + cmp eax, 2; + jne nevermind; // normal return + dec eax; + mov dword ptr [esp + 0x2C - 0x1C + 4], eax; // set 1, skip blocking check + dec eax; +nevermind: + retn; + } +} + +static void __declspec(naked) PerceptionRangeHearHook() { + __asm { + push ecx; + push 2; + mov ecx, eax; + call PerceptionRange_Hook; + pop ecx; + retn; + } +} + +static void __declspec(naked) PerceptionSearchTargetHook() { + __asm { + push ecx; + push 3; + mov ecx, eax; + call PerceptionRange_Hook; + pop ecx; + retn; + } +} + +void Inject_BarterPriceHook() { + const DWORD barterPriceHkAddr[] = { + 0x474D4C, // barter_attempt_transaction_ (offers button) + 0x475735, // display_table_inventories_ (for party members) + 0x475762 // display_table_inventories_ + }; + HookCalls(BarterPriceHook, barterPriceHkAddr); + const DWORD pcBarterPriceHkAddr[] = {0x4754F4, 0X47551A}; // display_table_inventories_ + HookCalls(PC_BarterPriceHook, pcBarterPriceHkAddr); + HookCall(0x474D3F, OverrideCost_BarterPriceHook); // barter_attempt_transaction_ (just overrides cost of offered goods) +} + +void Inject_UseSkillHook() { + const DWORD useSkillHkAddr[] = {0x49C48F, 0x49D12E}; + HookCalls(UseSkillHook, useSkillHkAddr); +} + +void Inject_StealCheckHook() { + const DWORD stealCheckHkAddr[] = {0x4749A2, 0x474A69}; + HookCalls(StealCheckHook, stealCheckHkAddr); +} + +void Inject_WithinPerceptionHook() { + const DWORD perceptionRngHkAddr[] = { + 0x42B4ED, + 0x42BC87, + 0x42BC9F, + 0x42BD04 + }; + HookCalls(PerceptionRangeHook, perceptionRngHkAddr); + HookCall(0x429157, PerceptionSearchTargetHook); + HookCall(0x456BA2, PerceptionRangeSeeHook); + HookCall(0x458403, PerceptionRangeHearHook); +} + +void InitMiscHookScripts() { + HookScripts::LoadHookScript("hs_barterprice", HOOK_BARTERPRICE); + HookScripts::LoadHookScript("hs_useskill", HOOK_USESKILL); + HookScripts::LoadHookScript("hs_steal", HOOK_STEAL); + HookScripts::LoadHookScript("hs_withinperception", HOOK_WITHINPERCEPTION); +} + +} diff --git a/sfall/Modules/HookScripts/MiscHs.h b/sfall/Modules/HookScripts/MiscHs.h new file mode 100644 index 00000000..c267a138 --- /dev/null +++ b/sfall/Modules/HookScripts/MiscHs.h @@ -0,0 +1,15 @@ +#pragma once + +namespace sfall +{ + +void InitMiscHookScripts(); + +void Inject_BarterPriceHook(); +void Inject_UseSkillHook(); +void Inject_StealCheckHook(); +void Inject_WithinPerceptionHook(); + +long PerceptionRangeHook_Invoke(fo::GameObject* watcher, fo::GameObject* target, long type, long result); + +} diff --git a/sfall/Modules/HookScripts/ObjectHs.cpp b/sfall/Modules/HookScripts/ObjectHs.cpp new file mode 100644 index 00000000..9452433a --- /dev/null +++ b/sfall/Modules/HookScripts/ObjectHs.cpp @@ -0,0 +1,136 @@ +#include "..\..\FalloutEngine\Fallout2.h" +#include "..\..\SafeWrite.h" +#include "..\HookScripts.h" +#include "Common.h" + +#include "ObjectHs.h" + +// Object hook scripts +namespace sfall +{ + +static long UseObjOnHook_Script(fo::GameObject* source, fo::GameObject* item, fo::GameObject* target) { + BeginHook(); + argCount = 3; + + args[0] = (DWORD)target; // target + args[1] = (DWORD)source; // user + args[2] = (DWORD)item; // item + + RunHookScript(HOOK_USEOBJON); + + long result = (cRet > 0) ? rets[0] : -1; + EndHook(); + + return result; // -1 - default handler +} + +long UseObjOnHook_Invoke(fo::GameObject* source, fo::GameObject* item, fo::GameObject* target) { + if (!HookScripts::HookHasScript(HOOK_USEOBJON)) return -1; + return UseObjOnHook_Script(source, item, target); +} + +static void __declspec(naked) UseObjOnHook() { + __asm { + HookBegin; + mov args[0], edx; // target + mov args[4], eax; // user + mov args[8], ebx; // object + pushadc; + } + + argCount = 3; + RunHookScript(HOOK_USEOBJON); + + __asm { + popadc; + cmp cRet, 1; + jl defaultHandler; + cmp rets[0], -1; + je defaultHandler; + mov eax, rets[0]; + HookEnd; + retn; +defaultHandler: + HookEnd; + jmp fo::funcoffs::protinst_use_item_on_; + } +} + +static void __declspec(naked) Drug_UseObjOnHook() { + __asm { + HookBegin; + mov args[0], eax; // target + mov args[4], eax; // user + mov args[8], edx; // object + pushadc; + } + + argCount = 3; + RunHookScript(HOOK_USEOBJON); + + __asm { + popadc; + cmp cRet, 1; + jl defaultHandler; + cmp rets[0], -1; + je defaultHandler; + mov eax, rets[0]; + HookEnd; + retn; +defaultHandler: + HookEnd; + jmp fo::funcoffs::item_d_take_drug_; + } +} + +static void __declspec(naked) UseObjHook() { + __asm { + HookBegin; + mov args[0], eax; // user + mov args[4], edx; // object + pushadc; + } + + argCount = 2; + RunHookScript(HOOK_USEOBJ); + + __asm { + popadc; + cmp cRet, 1; + jl defaultHandler; + cmp rets[0], -1; + je defaultHandler; + mov eax, rets[0]; + HookEnd; + retn; +defaultHandler: + HookEnd; + jmp fo::funcoffs::protinst_use_item_; + } +} + +void Inject_UseObjOnHook() { + const DWORD useObjOnHkAddr[] = {0x49C606, 0x473619}; + HookCalls(UseObjOnHook, useObjOnHkAddr); + // the following hooks allows to catch drug use of AI and from action cursor + const DWORD drugUseObjOnHkAddr[] = { + //0x4285DF, // ai_check_drugs + //0x4286F8, // ai_check_drugs + //0x4287F8, // ai_check_drugs + 0x473573 // inven_action_cursor + }; + HookCalls(Drug_UseObjOnHook, drugUseObjOnHkAddr); +} + +void Inject_UseObjHook() { + const DWORD useObjHkAddr[] = {0x42AEBF, 0x473607, 0x49C12E}; + HookCalls(UseObjHook, useObjHkAddr); +} + +void InitObjectHookScripts() { + HookScripts::LoadHookScript("hs_useobjon", HOOK_USEOBJON); + HookScripts::LoadHookScript("hs_useobj", HOOK_USEOBJ); +} + +} diff --git a/sfall/Modules/HookScripts/ObjectHs.h b/sfall/Modules/HookScripts/ObjectHs.h new file mode 100644 index 00000000..c06bb684 --- /dev/null +++ b/sfall/Modules/HookScripts/ObjectHs.h @@ -0,0 +1,13 @@ +#pragma once + +namespace sfall +{ + +void InitObjectHookScripts(); + +void Inject_UseObjOnHook(); +void Inject_UseObjHook(); + +long UseObjOnHook_Invoke(fo::GameObject* source, fo::GameObject* item, fo::GameObject* target); + +} diff --git a/sfall/Modules/LoadGameHook.cpp b/sfall/Modules/LoadGameHook.cpp index f9cf6a49..5cd32fe2 100644 --- a/sfall/Modules/LoadGameHook.cpp +++ b/sfall/Modules/LoadGameHook.cpp @@ -35,7 +35,7 @@ #include "FileSystem.h" #include "Graphics.h" #include "HeroAppearance.h" -#include "HookScripts.h" +#include "HookScripts\Common.h" #include "Interface.h" #include "Inventory.h" #include "LoadOrder.h" diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index cbf2033a..fee77b6e 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -1168,7 +1168,7 @@ void ScriptExtender::init() { } alwaysFindScripts = isDebug && (IniReader::GetIntDefaultConfig("Debugging", "AlwaysFindScripts", 0) != 0); - if (alwaysFindScripts) dlogr("Always searching for global scripts behavior enabled.", DL_SCRIPT); + if (alwaysFindScripts) dlogr("Always searching for global/hook scripts behavior enabled.", DL_SCRIPT); MakeJump(0x4A390C, scr_find_sid_from_program_hack); MakeJump(0x4A5E34, scr_ptr_hack); diff --git a/sfall/Modules/Scripting/Handlers/Core.cpp b/sfall/Modules/Scripting/Handlers/Core.cpp index e17c8c9b..8b75dbb3 100644 --- a/sfall/Modules/Scripting/Handlers/Core.cpp +++ b/sfall/Modules/Scripting/Handlers/Core.cpp @@ -20,7 +20,7 @@ #include "..\..\..\FalloutEngine\Fallout2.h" #include "..\..\..\Version.h" -#include "..\..\HookScripts.h" +#include "..\..\HookScripts\Common.h" #include "..\..\ScriptExtender.h" #include "..\Arrays.h" diff --git a/sfall/ddraw.vcxproj b/sfall/ddraw.vcxproj index 4b5124e8..1a54dadb 100644 --- a/sfall/ddraw.vcxproj +++ b/sfall/ddraw.vcxproj @@ -277,6 +277,13 @@ + + + + + + + @@ -376,6 +383,13 @@ + + + + + + + diff --git a/sfall/ddraw.vcxproj.filters b/sfall/ddraw.vcxproj.filters index 09cac4fd..9a8ea14d 100644 --- a/sfall/ddraw.vcxproj.filters +++ b/sfall/ddraw.vcxproj.filters @@ -16,6 +16,9 @@ {70a7a0e2-5bd4-4802-a8db-ad2d49ba6afd} + + {e1d81f47-5517-4cdf-95e4-d3fd5ef4adef} + {c9051acd-801a-4856-bd62-b2c95ac33c2e} @@ -82,6 +85,27 @@ Modules + + Modules\HookScripts + + + Modules\HookScripts + + + Modules\HookScripts + + + Modules\HookScripts + + + Modules\HookScripts + + + Modules\HookScripts + + + Modules\HookScripts + Modules @@ -382,6 +406,27 @@ Modules + + Modules\HookScripts + + + Modules\HookScripts + + + Modules\HookScripts + + + Modules\HookScripts + + + Modules\HookScripts + + + Modules\HookScripts + + + Modules\HookScripts + Modules