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)
This commit is contained in:
NovaRain
2022-02-08 22:30:06 +08:00
parent bc380b978d
commit 6e10f624a3
32 changed files with 2360 additions and 1919 deletions
+4 -1
View File
@@ -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
+4 -3
View File
@@ -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) {
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -7,7 +7,7 @@
#include "..\main.h"
#include "..\FalloutEngine\Fallout2.h"
#include "..\Modules\HookScripts.h"
#include "..\Modules\HookScripts\CombatHs.h"
#include "items.h"
+2 -1
View File
@@ -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
+3 -1
View File
@@ -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"
+1 -1
View File
@@ -7,7 +7,7 @@
#include "..\main.h"
#include "..\FalloutEngine\Fallout2.h"
#include "..\Modules\HookScripts.h"
#include "..\Modules\HookScripts\MiscHs.h"
#include "objects.h"
+1 -1
View File
@@ -16,7 +16,7 @@ namespace sf = sfall;
//static std::vector<int> buildLineTiles;
//static bool __stdcall TileExists(long tile) {
//static bool TileExists(long tile) {
// return (std::find(buildLineTiles.cbegin(), buildLineTiles.cend(), tile) != buildLineTiles.cend());
//}
+1 -1
View File
@@ -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"
+2 -1
View File
@@ -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"
+8 -1
View File
@@ -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;
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
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
File diff suppressed because it is too large Load Diff
+9 -30
View File
@@ -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);
}
+394
View File
@@ -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);
}
}
+22
View File
@@ -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);
}
+199
View File
@@ -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<HookScript> 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;
}
}
+52
View File
@@ -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<HookScript> 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
}
+165
View File
@@ -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);
}
}
+14
View File
@@ -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();
}
+145
View File
@@ -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>((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);
}
}

Some files were not shown because too many files have changed in this diff Show More