mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Backported 5 game hooks from 4.1/4.2:
* SNEAK, STDPROCEDURE, STDPROCEDURE_END, TARGETOBJECT, ENCOUNTER. Updated documents and example mods.
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Example implementation of the algorithm of how the game engine checks the Sneak skill
|
||||
*/
|
||||
|
||||
#include "..\headers\define.h"
|
||||
#include "..\headers\command.h"
|
||||
#include "..\headers\sfall\sfall.h"
|
||||
|
||||
procedure start;
|
||||
|
||||
procedure start begin
|
||||
if game_loaded then begin
|
||||
register_hook(HOOK_SNEAK);
|
||||
end else begin
|
||||
variable sneakIsSuccess, time, level;
|
||||
|
||||
level := critter_skill_level(dude_obj, SKILL_SNEAK);
|
||||
if (roll_vs_skill(dude_obj, SKILL_SNEAK, 0) < ROLL_SUCCESS) then begin
|
||||
sneakIsSuccess := false;
|
||||
time := ONE_GAME_MINUTE;
|
||||
if (level <= 250) then
|
||||
if (level <= 200) then
|
||||
if (level <= 170) then
|
||||
if (level <= 135) then
|
||||
if (level <= 100) then
|
||||
if (level > 80) then
|
||||
time := 400; // 40 secs
|
||||
else
|
||||
time := 300; // 30 secs
|
||||
else
|
||||
time := 200; // 20 secs
|
||||
else
|
||||
time := 150; // 15 secs
|
||||
else
|
||||
time := 120; // 12 secs
|
||||
else
|
||||
time := 100; // 10 secs for skill level > 250
|
||||
end
|
||||
else begin
|
||||
sneakIsSuccess := true;
|
||||
time := ONE_GAME_MINUTE;
|
||||
end
|
||||
set_sfall_return(sneakIsSuccess);
|
||||
set_sfall_return(time);
|
||||
end
|
||||
end
|
||||
@@ -60,6 +60,11 @@
|
||||
#define HOOK_USEANIMOBJ (32)
|
||||
#define HOOK_EXPLOSIVETIMER (33)
|
||||
#define HOOK_DESCRIPTIONOBJ (34)
|
||||
#define HOOK_SNEAK (39)
|
||||
#define HOOK_STDPROCEDURE (40)
|
||||
#define HOOK_STDPROCEDURE_END (41)
|
||||
#define HOOK_TARGETOBJECT (42)
|
||||
#define HOOK_ENCOUNTER (43)
|
||||
|
||||
// Valid arguments to list_begin
|
||||
#define LIST_CRITTERS (0)
|
||||
@@ -321,6 +326,7 @@
|
||||
#define get_npc_stat_max(stat) sfall_func2("get_stat_max", stat, 1)
|
||||
#define get_npc_stat_min(stat) sfall_func2("get_stat_min", stat, 1)
|
||||
#define get_sfall_arg_at(argNum) sfall_func1("get_sfall_arg_at", argNum)
|
||||
#define get_string_pointer(text) sfall_func1("get_string_pointer", text)
|
||||
#define get_terrain_name(x, y) sfall_func2("get_terrain_name", x, y)
|
||||
#define get_text_width(text) sfall_func1("get_text_width", text)
|
||||
#define hide_win sfall_func0("hide_window")
|
||||
|
||||
@@ -676,3 +676,67 @@ Obj arg0 - the object
|
||||
|
||||
int ret0 - a pointer to the new text received by using the get_string_pointer function
|
||||
```
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
#### `HOOK_SNEAK (hs_sneak.int)`
|
||||
|
||||
Runs when the Sneak skill is activated, or when the game rolls another Sneak check after the duration for the current one is over.\
|
||||
You can override the result of a random Sneak check or the duration time for the current result.
|
||||
|
||||
```
|
||||
int arg0 - Sneak check result: 1 - success, 0 - failure
|
||||
int arg1 - the duration in ticks for the current Sneak check (time depends on Sneak skill level)
|
||||
Critter arg2 - the critter (usually dude_obj)
|
||||
|
||||
int ret0 - overrides the result of the Sneak check
|
||||
int ret1 - overrides the duration time for the current result
|
||||
```
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
#### `HOOK_STDPROCEDURE, HOOK_STDPROCEDURE_END (hs_stdprocedure.int)`
|
||||
|
||||
Runs before or after Fallout engine executes a standard procedure (handler) in any script of any object.
|
||||
|
||||
__NOTE:__ This hook will not be executed for `start`, `critter_p_proc`, `timed_event_p_proc`, and `map_update_p_proc` procedures.
|
||||
|
||||
```
|
||||
int arg0 - the number of the standard script handler (see define.h)
|
||||
Obj arg1 - the object that owns this handler (self_obj)
|
||||
Obj arg2 - the object that called this handler (source_obj, can be 0)
|
||||
int arg3 - 1 after procedure execution (for HOOK_STDPROCEDURE_END), 0 otherwise
|
||||
|
||||
int ret0 - pass -1 to cancel the execution of the handler (only for HOOK_STDPROCEDURE)
|
||||
```
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
#### `HOOK_TARGETOBJECT (hs_targetobject.int)`
|
||||
|
||||
Runs when the targeting cursor hovers over an object, or when the player tries to attack the target object.\
|
||||
You can override the target object or prevent the player from attacking the chosen target.
|
||||
|
||||
```
|
||||
int arg0 - event type: 0 - when the targeting cursor hovers over the object, 1 - when trying to attack the target object
|
||||
int arg1 - 1 when the target object is valid to attack, 0 otherwise
|
||||
Obj arg2 - the target object
|
||||
|
||||
mixed ret0 - overrides the target object, or pass -1 to prevent the player from attacking the object
|
||||
```
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
#### `HOOK_ENCOUNTER (hs_encounter.int)`
|
||||
|
||||
Runs whenever a random encounter occurs (except the Horrigan meeting and scripted encounters), or when the player enters a local map from the world map.\
|
||||
You can override the map for loading or the encounter.
|
||||
|
||||
```
|
||||
int arg0 - event type: 0 - when a random encounter occurs, 1 - when the player enters from the world map
|
||||
int arg1 - the map ID that the encounter will load (see MAPS.h or Maps.txt)
|
||||
int arg2 - 1 when the encounter occurs is a special encounter, 0 otherwise
|
||||
|
||||
int ret0 - overrides the map ID, or pass -1 for event type 0 to cancel the encounter and continue traveling
|
||||
int ret1 - pass 1 to cancel the encounter and load the specified map from the ret0 (only for event type 0)
|
||||
```
|
||||
|
||||
@@ -539,7 +539,11 @@ FUNC(obj_shoot_blocking_at_, 0x48B930)
|
||||
FUNC(obj_sight_blocking_at_, 0x48BB88)
|
||||
FUNC(obj_top_environment_, 0x48B304)
|
||||
FUNC(obj_turn_off_, 0x48AE68) // int aObj<eax>, int ???<edx>
|
||||
FUNC(obj_turn_off_light_, 0x48AD9C)
|
||||
FUNC(obj_turn_off_outline_, 0x48AF00)
|
||||
FUNC(obj_turn_on_, 0x48ADF0)
|
||||
FUNC(obj_turn_on_light_, 0x48AD48)
|
||||
FUNC(obj_turn_on_outline_, 0x48AEE4)
|
||||
FUNC(obj_unjam_lock_, 0x49D480)
|
||||
FUNC(obj_use_book_, 0x49B9F0)
|
||||
FUNC(obj_use_power_on_car_, 0x49BDE8)
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
#define FO_VAR_edit_win 0x57060C
|
||||
#define FO_VAR_Educated 0x57082C
|
||||
#define FO_VAR_elevation 0x631D2C
|
||||
#define FO_VAR_EncounterMapID 0x672E4C
|
||||
#define FO_VAR_endgame_subtitle_done 0x570BD0
|
||||
#define FO_VAR_endgame_subtitle_characters 0x51866C
|
||||
#define FO_VAR_endgame_voiceover_loaded 0x570AB8
|
||||
@@ -206,6 +207,8 @@
|
||||
#define FO_VAR_obj_seen 0x662445
|
||||
#define FO_VAR_objectTable 0x639DA0
|
||||
#define FO_VAR_objItemOutlineState 0x519798
|
||||
#define FO_VAR_old_world_xpos 0x672E5C
|
||||
#define FO_VAR_old_world_ypos 0x672E60
|
||||
#define FO_VAR_optionRect 0x58ECC0
|
||||
#define FO_VAR_optionsButtonDown 0x59D400
|
||||
#define FO_VAR_optionsButtonDown1 0x570518
|
||||
|
||||
@@ -97,6 +97,15 @@ static HooksInjectInfo injectHooks[] = {
|
||||
{HOOK_USEANIMOBJ, Inject_UseAnimateObjHook, false},
|
||||
{HOOK_EXPLOSIVETIMER, Inject_ExplosiveTimerHook, false},
|
||||
{HOOK_DESCRIPTIONOBJ, Inject_DescriptionObjHook, false},
|
||||
{-1, nullptr, false}, // dummy
|
||||
{-1, nullptr, false}, // dummy
|
||||
{-1, nullptr, false}, // dummy
|
||||
{-1, nullptr, false}, // dummy
|
||||
{HOOK_SNEAK, Inject_SneakCheckHook, false},
|
||||
{HOOK_STDPROCEDURE, Inject_ScriptProcedureHook, false},
|
||||
{HOOK_STDPROCEDURE_END, Inject_ScriptProcedureHook2, false},
|
||||
{HOOK_TARGETOBJECT, Inject_TargetObjectHook, false},
|
||||
{HOOK_ENCOUNTER, Inject_EncounterHook, false},
|
||||
};
|
||||
|
||||
void HookScripts::InjectingHook(int hookId) {
|
||||
|
||||
@@ -60,6 +60,11 @@ enum HookType
|
||||
HOOK_USEANIMOBJ = 32,
|
||||
HOOK_EXPLOSIVETIMER = 33,
|
||||
HOOK_DESCRIPTIONOBJ = 34,
|
||||
HOOK_SNEAK = 39,
|
||||
HOOK_STDPROCEDURE = 40,
|
||||
HOOK_STDPROCEDURE_END = 41,
|
||||
HOOK_TARGETOBJECT = 42,
|
||||
HOOK_ENCOUNTER = 43,
|
||||
HOOK_COUNT
|
||||
};
|
||||
|
||||
|
||||
@@ -317,6 +317,66 @@ skip:
|
||||
}
|
||||
}
|
||||
|
||||
static DWORD targetRet = 0;
|
||||
static bool targetObjHookHasRet = false;
|
||||
|
||||
static long __fastcall TargetObjectHook(DWORD isValid, DWORD object, long type) {
|
||||
if (isValid > 1) isValid = 1;
|
||||
|
||||
BeginHook();
|
||||
argCount = 3;
|
||||
|
||||
args[0] = type; // 0 - mouse hovering over target, 1 - mouse clicking on target
|
||||
args[1] = isValid; // 1 - target is valid
|
||||
args[2] = object; // target object
|
||||
|
||||
if (isValid == 0) object = 0; // it is necessary for the proper operation of the engine code
|
||||
if (type == 0) targetRet = 0; // unset ret from the previous execution of the hook
|
||||
|
||||
RunHookScript(HOOK_TARGETOBJECT);
|
||||
|
||||
if (cRet > 0) {
|
||||
targetRet = (rets[0] != 0) ? rets[0] : object; // 0 - default object, -1 - invalid target, or object override
|
||||
object = (targetRet != -1) ? targetRet : 0; // object can't be -1
|
||||
targetObjHookHasRet = true;
|
||||
} else if (targetObjHookHasRet && type == 1) {
|
||||
targetObjHookHasRet = false;
|
||||
if (targetRet != -1) object = targetRet;
|
||||
}
|
||||
EndHook();
|
||||
return object; // null or object
|
||||
}
|
||||
|
||||
static void __declspec(naked) gmouse_bk_process_hook() {
|
||||
__asm {
|
||||
push 0; // type
|
||||
mov ecx, eax; // 1 - valid(object) or 0 - invalid
|
||||
mov edx, edi; // object under mouse
|
||||
call TargetObjectHook;
|
||||
mov edi, eax;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
static void __declspec(naked) gmouse_handle_event_hook() {
|
||||
__asm {
|
||||
push 1; // type
|
||||
mov ecx, eax; // 1 - valid(object) or 0 - invalid
|
||||
cmp dword ptr ds:[targetRet], 0;
|
||||
je default;
|
||||
// override
|
||||
mov ecx, 1;
|
||||
xor eax, eax;
|
||||
cmp dword ptr ds:[targetRet], -1;
|
||||
cmove ecx, eax; // if true - set invalid
|
||||
cmovne edx, targetRet; // if false - set override object
|
||||
default:
|
||||
call TargetObjectHook;
|
||||
mov edx, eax;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Inject_ToHitHook() {
|
||||
@@ -381,6 +441,14 @@ void Inject_AmmoCostHook() {
|
||||
HookCall(0x423A7C, AmmoCostHook); // compute_attack_
|
||||
}
|
||||
|
||||
void Inject_TargetObjectHook() {
|
||||
MakeCall(0x44BB16, gmouse_bk_process_hook, 1);
|
||||
SafeWrite8(0x44BB00, 0x15);
|
||||
|
||||
MakeCall(0x44C286, gmouse_handle_event_hook, 1);
|
||||
SafeWrite8(0x44C26E, 0x17);
|
||||
}
|
||||
|
||||
void InitCombatHookScripts() {
|
||||
HookScripts::LoadHookScript("hs_tohit", HOOK_TOHIT);
|
||||
HookScripts::LoadHookScript("hs_afterhitroll", HOOK_AFTERHITROLL);
|
||||
@@ -389,6 +457,7 @@ void InitCombatHookScripts() {
|
||||
HookScripts::LoadHookScript("hs_findtarget", HOOK_FINDTARGET);
|
||||
HookScripts::LoadHookScript("hs_itemdamage", HOOK_ITEMDAMAGE);
|
||||
HookScripts::LoadHookScript("hs_ammocost", HOOK_AMMOCOST);
|
||||
HookScripts::LoadHookScript("hs_targetobject", HOOK_TARGETOBJECT);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ void Inject_CombatDamageHook();
|
||||
void Inject_FindTargetHook();
|
||||
void Inject_ItemDamageHook();
|
||||
void Inject_AmmoCostHook();
|
||||
void Inject_TargetObjectHook();
|
||||
|
||||
int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD &rounds);
|
||||
|
||||
|
||||
@@ -150,6 +150,32 @@ defaultHandler:
|
||||
}
|
||||
}
|
||||
|
||||
static void __declspec(naked) SneakCheckHook() {
|
||||
__asm {
|
||||
HookBegin;
|
||||
mov esi, ds:[FO_VAR_sneak_working];
|
||||
mov args[0], esi; // 1 = successful sneak
|
||||
mov args[4], eax; // timer
|
||||
mov args[8], edx; // critter
|
||||
pushadc;
|
||||
}
|
||||
|
||||
argCount = 3;
|
||||
RunHookScript(HOOK_SNEAK);
|
||||
|
||||
__asm {
|
||||
popadc;
|
||||
cmp cRet, 1;
|
||||
jb skip;
|
||||
mov esi, rets[0];
|
||||
mov ds:[FO_VAR_sneak_working], esi;
|
||||
cmova eax, rets[4]; // override timer
|
||||
skip:
|
||||
HookEnd;
|
||||
jmp fo::funcoffs::queue_add_;
|
||||
}
|
||||
}
|
||||
|
||||
static __forceinline long PerceptionRangeHook_Script(fo::GameObject* watcher, fo::GameObject* target, int type, long result) {
|
||||
BeginHook();
|
||||
argCount = 4;
|
||||
@@ -433,6 +459,98 @@ end:
|
||||
}
|
||||
}
|
||||
|
||||
// Hook does not work for scripted encounters and meeting Horrigan
|
||||
static long __fastcall EncounterHook_Script(long encounterMapID, long eventType, long encType) {
|
||||
BeginHook();
|
||||
argCount = 3;
|
||||
|
||||
args[0] = eventType; // 1 - enter local map from the world map
|
||||
args[1] = encounterMapID;
|
||||
args[2] = (encType == 3); // 1 - special random encounter
|
||||
|
||||
RunHookScript(HOOK_ENCOUNTER);
|
||||
|
||||
if (cRet) {
|
||||
encounterMapID = rets[0];
|
||||
if (encounterMapID < -1) encounterMapID = -1;
|
||||
if (eventType == 0 && cRet > 1 && encounterMapID != -1 && rets[1] == 1) { // 1 - cancel the encounter and load the specified map
|
||||
encounterMapID = -encounterMapID - 2; // map number in negative value
|
||||
}
|
||||
if (encounterMapID < 0) {
|
||||
// set the coordinates of the last encounter
|
||||
fo::var::setInt(FO_VAR_old_world_xpos) = *fo::ptr::world_xpos;
|
||||
fo::var::setInt(FO_VAR_old_world_ypos) = *fo::ptr::world_ypos;
|
||||
}
|
||||
}
|
||||
EndHook();
|
||||
return encounterMapID;
|
||||
}
|
||||
|
||||
static void __declspec(naked) wmWorldMap_hook() {
|
||||
__asm {
|
||||
mov ebx, eax; // keep map id
|
||||
mov edx, 1;
|
||||
mov ecx, eax;
|
||||
push 0;
|
||||
call EncounterHook_Script;
|
||||
test eax, eax;
|
||||
cmovl eax, ebx; // restore map if map < 0
|
||||
jmp fo::funcoffs::map_load_idx_; // eax - map id
|
||||
}
|
||||
}
|
||||
|
||||
static void __declspec(naked) wmRndEncounterOccurred_hook() {
|
||||
static long hkEncounterMapID = -1;
|
||||
__asm {
|
||||
cmp hkEncounterMapID, -1;
|
||||
jnz blinkIcon;
|
||||
test edx, edx;
|
||||
jz hookRun;
|
||||
jmp fo::funcoffs::wmInterfaceRefresh_;
|
||||
hookRun: /////////////////////////////////
|
||||
push edx;
|
||||
push ecx;
|
||||
push ecx; // encType
|
||||
xor edx, edx;
|
||||
mov ecx, dword ptr ds:[FO_VAR_EncounterMapID];
|
||||
call EncounterHook_Script;
|
||||
pop ecx;
|
||||
pop edx;
|
||||
mov dword ptr ds:[FO_VAR_EncounterMapID], eax;
|
||||
cmp eax, -1;
|
||||
je cancelEnc;
|
||||
jl clearEnc;
|
||||
jmp fo::funcoffs::wmInterfaceRefresh_;
|
||||
clearEnc: /////////////////////////////////
|
||||
mov dword ptr ds:[FO_VAR_EncounterMapID], -1;
|
||||
neg eax;
|
||||
sub eax, 2; // recover correct map number from negative value
|
||||
mov hkEncounterMapID, eax;
|
||||
dec edx;
|
||||
blinkIcon:
|
||||
cmp edx, 6; // counter of blinking
|
||||
jge break;
|
||||
jmp fo::funcoffs::wmInterfaceRefresh_;
|
||||
break:
|
||||
mov eax, hkEncounterMapID;
|
||||
cmp dword ptr ds:[FO_VAR_Move_on_Car], 0;
|
||||
je noCar;
|
||||
mov edx, FO_VAR_carCurrentArea;
|
||||
call fo::funcoffs::wmMatchAreaContainingMapIdx_;
|
||||
mov eax, hkEncounterMapID;
|
||||
noCar:
|
||||
call fo::funcoffs::map_load_idx_;
|
||||
xor eax, eax;
|
||||
mov hkEncounterMapID, -1;
|
||||
cancelEnc:
|
||||
inc eax; // 0 - continue movement, 1 - interrupt
|
||||
mov dword ptr ds:[FO_VAR_wmEncounterIconShow], 0;
|
||||
add esp, 4;
|
||||
mov ebx, 0x4C0BC7;
|
||||
jmp ebx;
|
||||
}
|
||||
}
|
||||
|
||||
void Inject_BarterPriceHook() {
|
||||
const DWORD barterPriceHkAddr[] = {
|
||||
0x474D4C, // barter_attempt_transaction_ (offers button)
|
||||
@@ -455,6 +573,10 @@ void Inject_StealCheckHook() {
|
||||
HookCalls(StealCheckHook, stealCheckHkAddr);
|
||||
}
|
||||
|
||||
void Inject_SneakCheckHook() {
|
||||
HookCall(0x42E3D9, SneakCheckHook);
|
||||
}
|
||||
|
||||
void Inject_WithinPerceptionHook() {
|
||||
const DWORD perceptionRngHkAddr[] = {
|
||||
0x42B4ED,
|
||||
@@ -488,15 +610,22 @@ void Inject_ExplosiveTimerHook() {
|
||||
HookCall(0x49BDC4, ExplosiveTimerHook);
|
||||
}
|
||||
|
||||
void Inject_EncounterHook() {
|
||||
HookCall(0x4C02AF, wmWorldMap_hook);
|
||||
HookCall(0x4C095C, wmRndEncounterOccurred_hook);
|
||||
}
|
||||
|
||||
void InitMiscHookScripts() {
|
||||
HookScripts::LoadHookScript("hs_barterprice", HOOK_BARTERPRICE);
|
||||
HookScripts::LoadHookScript("hs_useskill", HOOK_USESKILL);
|
||||
HookScripts::LoadHookScript("hs_steal", HOOK_STEAL);
|
||||
HookScripts::LoadHookScript("hs_sneak", HOOK_SNEAK);
|
||||
HookScripts::LoadHookScript("hs_withinperception", HOOK_WITHINPERCEPTION);
|
||||
HookScripts::LoadHookScript("hs_cartravel", HOOK_CARTRAVEL);
|
||||
HookScripts::LoadHookScript("hs_setglobalvar", HOOK_SETGLOBALVAR);
|
||||
HookScripts::LoadHookScript("hs_resttimer", HOOK_RESTTIMER);
|
||||
HookScripts::LoadHookScript("hs_explosivetimer", HOOK_EXPLOSIVETIMER);
|
||||
HookScripts::LoadHookScript("hs_encounter", HOOK_ENCOUNTER);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,11 +8,13 @@ void InitMiscHookScripts();
|
||||
void Inject_BarterPriceHook();
|
||||
void Inject_UseSkillHook();
|
||||
void Inject_StealCheckHook();
|
||||
void Inject_SneakCheckHook();
|
||||
void Inject_WithinPerceptionHook();
|
||||
void Inject_CarTravelHook();
|
||||
void Inject_SetGlobalVarHook();
|
||||
void Inject_RestTimerHook();
|
||||
void Inject_ExplosiveTimerHook();
|
||||
void Inject_EncounterHook();
|
||||
|
||||
long PerceptionRangeHook_Invoke(fo::GameObject* watcher, fo::GameObject* target, long type, long result);
|
||||
|
||||
|
||||
@@ -185,6 +185,76 @@ skip:
|
||||
}
|
||||
}
|
||||
|
||||
static DWORD __fastcall StdProcedureHook_Script(long numHandler, fo::ScriptInstance* script, DWORD procTable) {
|
||||
BeginHook();
|
||||
argCount = 4;
|
||||
|
||||
args[0] = numHandler;
|
||||
args[1] = (DWORD)script->selfObject;
|
||||
args[2] = (DWORD)script->sourceObject;
|
||||
|
||||
if (procTable) {
|
||||
args[3] = 0;
|
||||
RunHookScript(HOOK_STDPROCEDURE);
|
||||
|
||||
if (cRet > 0) {
|
||||
long retval = rets[0];
|
||||
if (retval == -1) procTable = retval;
|
||||
}
|
||||
} else {
|
||||
args[3] = 1;
|
||||
RunHookScript(HOOK_STDPROCEDURE_END);
|
||||
}
|
||||
EndHook();
|
||||
return procTable;
|
||||
}
|
||||
|
||||
static void __declspec(naked) ScriptStdProcedureHook() {
|
||||
using namespace fo::Scripts;
|
||||
__asm {
|
||||
mov eax, [eax + 0x54]; // Script.procedure_table
|
||||
test eax, eax;
|
||||
jle end;
|
||||
cmp ecx, critter_p_proc;
|
||||
je skip;
|
||||
cmp ecx, timed_event_p_proc;
|
||||
je skip;
|
||||
cmp ecx, map_update_p_proc;
|
||||
je skip;
|
||||
cmp ecx, start;
|
||||
jle skip;
|
||||
push ecx;
|
||||
push eax; // procTable
|
||||
mov edx, esi; // script
|
||||
call StdProcedureHook_Script; // ecx - numHandler
|
||||
pop ecx;
|
||||
skip:
|
||||
test eax, eax;
|
||||
end:
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
static void __declspec(naked) After_ScriptStdProcedureHook() {
|
||||
using namespace fo::Scripts;
|
||||
__asm {
|
||||
call fo::funcoffs::executeProcedure_;
|
||||
cmp ecx, critter_p_proc;
|
||||
je skip;
|
||||
cmp ecx, timed_event_p_proc;
|
||||
je skip;
|
||||
cmp ecx, map_update_p_proc;
|
||||
je skip;
|
||||
cmp ecx, start;
|
||||
jle skip;
|
||||
mov edx, [esp + 0x28 - 0x18 + 4]; // script
|
||||
push 0; // procTable
|
||||
call StdProcedureHook_Script; // ecx - numHandler
|
||||
skip:
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
void Inject_UseObjOnHook() {
|
||||
const DWORD useObjOnHkAddr[] = {0x49C606, 0x473619};
|
||||
HookCalls(UseObjOnHook, useObjOnHkAddr);
|
||||
@@ -212,10 +282,20 @@ void Inject_DescriptionObjHook() {
|
||||
HookCall(0x49AE28, DescriptionObjHook);
|
||||
}
|
||||
|
||||
void Inject_ScriptProcedureHook() {
|
||||
MakeCall(0x4A491F, ScriptStdProcedureHook);
|
||||
}
|
||||
|
||||
void Inject_ScriptProcedureHook2() {
|
||||
HookCall(0x4A49A7, After_ScriptStdProcedureHook);
|
||||
}
|
||||
|
||||
void InitObjectHookScripts() {
|
||||
HookScripts::LoadHookScript("hs_useobjon", HOOK_USEOBJON);
|
||||
HookScripts::LoadHookScript("hs_useobj", HOOK_USEOBJ);
|
||||
HookScripts::LoadHookScript("hs_descriptionobj", HOOK_DESCRIPTIONOBJ);
|
||||
HookScripts::LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE); // combo hook
|
||||
HookScripts::LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE_END);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ void Inject_UseObjOnHook();
|
||||
void Inject_UseObjHook();
|
||||
void Inject_UseAnimateObjHook();
|
||||
void Inject_DescriptionObjHook();
|
||||
void Inject_ScriptProcedureHook();
|
||||
void Inject_ScriptProcedureHook2();
|
||||
|
||||
long UseObjOnHook_Invoke(fo::GameObject* source, fo::GameObject* item, fo::GameObject* target);
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ static void __declspec(naked) GNW95_process_message_hack() {
|
||||
__asm {
|
||||
push idle;
|
||||
call Sleep2;
|
||||
cmp ds:[FO_VAR_GNW95_isActive], 0;
|
||||
cmp dword ptr ds:[FO_VAR_GNW95_isActive], 0;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ static void __declspec(naked) wmRndEncounterOccurred_hack() {
|
||||
__asm {
|
||||
test ForceEncounterFlags, 0x1; // _NoCar flag
|
||||
jnz noCar;
|
||||
cmp ds:[FO_VAR_Move_on_Car], 0;
|
||||
cmp dword ptr ds:[FO_VAR_Move_on_Car], 0;
|
||||
jz noCar;
|
||||
mov edx, FO_VAR_carCurrentArea;
|
||||
mov eax, ForceEncounterMapID;
|
||||
|
||||
Reference in New Issue
Block a user