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_USEANIMOBJ (32)
|
||||||
#define HOOK_EXPLOSIVETIMER (33)
|
#define HOOK_EXPLOSIVETIMER (33)
|
||||||
#define HOOK_DESCRIPTIONOBJ (34)
|
#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
|
// Valid arguments to list_begin
|
||||||
#define LIST_CRITTERS (0)
|
#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_max(stat) sfall_func2("get_stat_max", stat, 1)
|
||||||
#define get_npc_stat_min(stat) sfall_func2("get_stat_min", 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_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_terrain_name(x, y) sfall_func2("get_terrain_name", x, y)
|
||||||
#define get_text_width(text) sfall_func1("get_text_width", text)
|
#define get_text_width(text) sfall_func1("get_text_width", text)
|
||||||
#define hide_win sfall_func0("hide_window")
|
#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
|
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_sight_blocking_at_, 0x48BB88)
|
||||||
FUNC(obj_top_environment_, 0x48B304)
|
FUNC(obj_top_environment_, 0x48B304)
|
||||||
FUNC(obj_turn_off_, 0x48AE68) // int aObj<eax>, int ???<edx>
|
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_, 0x48ADF0)
|
||||||
|
FUNC(obj_turn_on_light_, 0x48AD48)
|
||||||
|
FUNC(obj_turn_on_outline_, 0x48AEE4)
|
||||||
FUNC(obj_unjam_lock_, 0x49D480)
|
FUNC(obj_unjam_lock_, 0x49D480)
|
||||||
FUNC(obj_use_book_, 0x49B9F0)
|
FUNC(obj_use_book_, 0x49B9F0)
|
||||||
FUNC(obj_use_power_on_car_, 0x49BDE8)
|
FUNC(obj_use_power_on_car_, 0x49BDE8)
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
#define FO_VAR_edit_win 0x57060C
|
#define FO_VAR_edit_win 0x57060C
|
||||||
#define FO_VAR_Educated 0x57082C
|
#define FO_VAR_Educated 0x57082C
|
||||||
#define FO_VAR_elevation 0x631D2C
|
#define FO_VAR_elevation 0x631D2C
|
||||||
|
#define FO_VAR_EncounterMapID 0x672E4C
|
||||||
#define FO_VAR_endgame_subtitle_done 0x570BD0
|
#define FO_VAR_endgame_subtitle_done 0x570BD0
|
||||||
#define FO_VAR_endgame_subtitle_characters 0x51866C
|
#define FO_VAR_endgame_subtitle_characters 0x51866C
|
||||||
#define FO_VAR_endgame_voiceover_loaded 0x570AB8
|
#define FO_VAR_endgame_voiceover_loaded 0x570AB8
|
||||||
@@ -206,6 +207,8 @@
|
|||||||
#define FO_VAR_obj_seen 0x662445
|
#define FO_VAR_obj_seen 0x662445
|
||||||
#define FO_VAR_objectTable 0x639DA0
|
#define FO_VAR_objectTable 0x639DA0
|
||||||
#define FO_VAR_objItemOutlineState 0x519798
|
#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_optionRect 0x58ECC0
|
||||||
#define FO_VAR_optionsButtonDown 0x59D400
|
#define FO_VAR_optionsButtonDown 0x59D400
|
||||||
#define FO_VAR_optionsButtonDown1 0x570518
|
#define FO_VAR_optionsButtonDown1 0x570518
|
||||||
|
|||||||
@@ -97,6 +97,15 @@ static HooksInjectInfo injectHooks[] = {
|
|||||||
{HOOK_USEANIMOBJ, Inject_UseAnimateObjHook, false},
|
{HOOK_USEANIMOBJ, Inject_UseAnimateObjHook, false},
|
||||||
{HOOK_EXPLOSIVETIMER, Inject_ExplosiveTimerHook, false},
|
{HOOK_EXPLOSIVETIMER, Inject_ExplosiveTimerHook, false},
|
||||||
{HOOK_DESCRIPTIONOBJ, Inject_DescriptionObjHook, 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) {
|
void HookScripts::InjectingHook(int hookId) {
|
||||||
|
|||||||
@@ -60,6 +60,11 @@ enum HookType
|
|||||||
HOOK_USEANIMOBJ = 32,
|
HOOK_USEANIMOBJ = 32,
|
||||||
HOOK_EXPLOSIVETIMER = 33,
|
HOOK_EXPLOSIVETIMER = 33,
|
||||||
HOOK_DESCRIPTIONOBJ = 34,
|
HOOK_DESCRIPTIONOBJ = 34,
|
||||||
|
HOOK_SNEAK = 39,
|
||||||
|
HOOK_STDPROCEDURE = 40,
|
||||||
|
HOOK_STDPROCEDURE_END = 41,
|
||||||
|
HOOK_TARGETOBJECT = 42,
|
||||||
|
HOOK_ENCOUNTER = 43,
|
||||||
HOOK_COUNT
|
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() {
|
void Inject_ToHitHook() {
|
||||||
@@ -381,6 +441,14 @@ void Inject_AmmoCostHook() {
|
|||||||
HookCall(0x423A7C, AmmoCostHook); // compute_attack_
|
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() {
|
void InitCombatHookScripts() {
|
||||||
HookScripts::LoadHookScript("hs_tohit", HOOK_TOHIT);
|
HookScripts::LoadHookScript("hs_tohit", HOOK_TOHIT);
|
||||||
HookScripts::LoadHookScript("hs_afterhitroll", HOOK_AFTERHITROLL);
|
HookScripts::LoadHookScript("hs_afterhitroll", HOOK_AFTERHITROLL);
|
||||||
@@ -389,6 +457,7 @@ void InitCombatHookScripts() {
|
|||||||
HookScripts::LoadHookScript("hs_findtarget", HOOK_FINDTARGET);
|
HookScripts::LoadHookScript("hs_findtarget", HOOK_FINDTARGET);
|
||||||
HookScripts::LoadHookScript("hs_itemdamage", HOOK_ITEMDAMAGE);
|
HookScripts::LoadHookScript("hs_itemdamage", HOOK_ITEMDAMAGE);
|
||||||
HookScripts::LoadHookScript("hs_ammocost", HOOK_AMMOCOST);
|
HookScripts::LoadHookScript("hs_ammocost", HOOK_AMMOCOST);
|
||||||
|
HookScripts::LoadHookScript("hs_targetobject", HOOK_TARGETOBJECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ void Inject_CombatDamageHook();
|
|||||||
void Inject_FindTargetHook();
|
void Inject_FindTargetHook();
|
||||||
void Inject_ItemDamageHook();
|
void Inject_ItemDamageHook();
|
||||||
void Inject_AmmoCostHook();
|
void Inject_AmmoCostHook();
|
||||||
|
void Inject_TargetObjectHook();
|
||||||
|
|
||||||
int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD &rounds);
|
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) {
|
static __forceinline long PerceptionRangeHook_Script(fo::GameObject* watcher, fo::GameObject* target, int type, long result) {
|
||||||
BeginHook();
|
BeginHook();
|
||||||
argCount = 4;
|
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() {
|
void Inject_BarterPriceHook() {
|
||||||
const DWORD barterPriceHkAddr[] = {
|
const DWORD barterPriceHkAddr[] = {
|
||||||
0x474D4C, // barter_attempt_transaction_ (offers button)
|
0x474D4C, // barter_attempt_transaction_ (offers button)
|
||||||
@@ -455,6 +573,10 @@ void Inject_StealCheckHook() {
|
|||||||
HookCalls(StealCheckHook, stealCheckHkAddr);
|
HookCalls(StealCheckHook, stealCheckHkAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Inject_SneakCheckHook() {
|
||||||
|
HookCall(0x42E3D9, SneakCheckHook);
|
||||||
|
}
|
||||||
|
|
||||||
void Inject_WithinPerceptionHook() {
|
void Inject_WithinPerceptionHook() {
|
||||||
const DWORD perceptionRngHkAddr[] = {
|
const DWORD perceptionRngHkAddr[] = {
|
||||||
0x42B4ED,
|
0x42B4ED,
|
||||||
@@ -488,15 +610,22 @@ void Inject_ExplosiveTimerHook() {
|
|||||||
HookCall(0x49BDC4, ExplosiveTimerHook);
|
HookCall(0x49BDC4, ExplosiveTimerHook);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Inject_EncounterHook() {
|
||||||
|
HookCall(0x4C02AF, wmWorldMap_hook);
|
||||||
|
HookCall(0x4C095C, wmRndEncounterOccurred_hook);
|
||||||
|
}
|
||||||
|
|
||||||
void InitMiscHookScripts() {
|
void InitMiscHookScripts() {
|
||||||
HookScripts::LoadHookScript("hs_barterprice", HOOK_BARTERPRICE);
|
HookScripts::LoadHookScript("hs_barterprice", HOOK_BARTERPRICE);
|
||||||
HookScripts::LoadHookScript("hs_useskill", HOOK_USESKILL);
|
HookScripts::LoadHookScript("hs_useskill", HOOK_USESKILL);
|
||||||
HookScripts::LoadHookScript("hs_steal", HOOK_STEAL);
|
HookScripts::LoadHookScript("hs_steal", HOOK_STEAL);
|
||||||
|
HookScripts::LoadHookScript("hs_sneak", HOOK_SNEAK);
|
||||||
HookScripts::LoadHookScript("hs_withinperception", HOOK_WITHINPERCEPTION);
|
HookScripts::LoadHookScript("hs_withinperception", HOOK_WITHINPERCEPTION);
|
||||||
HookScripts::LoadHookScript("hs_cartravel", HOOK_CARTRAVEL);
|
HookScripts::LoadHookScript("hs_cartravel", HOOK_CARTRAVEL);
|
||||||
HookScripts::LoadHookScript("hs_setglobalvar", HOOK_SETGLOBALVAR);
|
HookScripts::LoadHookScript("hs_setglobalvar", HOOK_SETGLOBALVAR);
|
||||||
HookScripts::LoadHookScript("hs_resttimer", HOOK_RESTTIMER);
|
HookScripts::LoadHookScript("hs_resttimer", HOOK_RESTTIMER);
|
||||||
HookScripts::LoadHookScript("hs_explosivetimer", HOOK_EXPLOSIVETIMER);
|
HookScripts::LoadHookScript("hs_explosivetimer", HOOK_EXPLOSIVETIMER);
|
||||||
|
HookScripts::LoadHookScript("hs_encounter", HOOK_ENCOUNTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ void InitMiscHookScripts();
|
|||||||
void Inject_BarterPriceHook();
|
void Inject_BarterPriceHook();
|
||||||
void Inject_UseSkillHook();
|
void Inject_UseSkillHook();
|
||||||
void Inject_StealCheckHook();
|
void Inject_StealCheckHook();
|
||||||
|
void Inject_SneakCheckHook();
|
||||||
void Inject_WithinPerceptionHook();
|
void Inject_WithinPerceptionHook();
|
||||||
void Inject_CarTravelHook();
|
void Inject_CarTravelHook();
|
||||||
void Inject_SetGlobalVarHook();
|
void Inject_SetGlobalVarHook();
|
||||||
void Inject_RestTimerHook();
|
void Inject_RestTimerHook();
|
||||||
void Inject_ExplosiveTimerHook();
|
void Inject_ExplosiveTimerHook();
|
||||||
|
void Inject_EncounterHook();
|
||||||
|
|
||||||
long PerceptionRangeHook_Invoke(fo::GameObject* watcher, fo::GameObject* target, long type, long result);
|
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() {
|
void Inject_UseObjOnHook() {
|
||||||
const DWORD useObjOnHkAddr[] = {0x49C606, 0x473619};
|
const DWORD useObjOnHkAddr[] = {0x49C606, 0x473619};
|
||||||
HookCalls(UseObjOnHook, useObjOnHkAddr);
|
HookCalls(UseObjOnHook, useObjOnHkAddr);
|
||||||
@@ -212,10 +282,20 @@ void Inject_DescriptionObjHook() {
|
|||||||
HookCall(0x49AE28, DescriptionObjHook);
|
HookCall(0x49AE28, DescriptionObjHook);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Inject_ScriptProcedureHook() {
|
||||||
|
MakeCall(0x4A491F, ScriptStdProcedureHook);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inject_ScriptProcedureHook2() {
|
||||||
|
HookCall(0x4A49A7, After_ScriptStdProcedureHook);
|
||||||
|
}
|
||||||
|
|
||||||
void InitObjectHookScripts() {
|
void InitObjectHookScripts() {
|
||||||
HookScripts::LoadHookScript("hs_useobjon", HOOK_USEOBJON);
|
HookScripts::LoadHookScript("hs_useobjon", HOOK_USEOBJON);
|
||||||
HookScripts::LoadHookScript("hs_useobj", HOOK_USEOBJ);
|
HookScripts::LoadHookScript("hs_useobj", HOOK_USEOBJ);
|
||||||
HookScripts::LoadHookScript("hs_descriptionobj", HOOK_DESCRIPTIONOBJ);
|
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_UseObjHook();
|
||||||
void Inject_UseAnimateObjHook();
|
void Inject_UseAnimateObjHook();
|
||||||
void Inject_DescriptionObjHook();
|
void Inject_DescriptionObjHook();
|
||||||
|
void Inject_ScriptProcedureHook();
|
||||||
|
void Inject_ScriptProcedureHook2();
|
||||||
|
|
||||||
long UseObjOnHook_Invoke(fo::GameObject* source, fo::GameObject* item, fo::GameObject* target);
|
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 {
|
__asm {
|
||||||
push idle;
|
push idle;
|
||||||
call Sleep2;
|
call Sleep2;
|
||||||
cmp ds:[FO_VAR_GNW95_isActive], 0;
|
cmp dword ptr ds:[FO_VAR_GNW95_isActive], 0;
|
||||||
retn;
|
retn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ static void __declspec(naked) wmRndEncounterOccurred_hack() {
|
|||||||
__asm {
|
__asm {
|
||||||
test ForceEncounterFlags, 0x1; // _NoCar flag
|
test ForceEncounterFlags, 0x1; // _NoCar flag
|
||||||
jnz noCar;
|
jnz noCar;
|
||||||
cmp ds:[FO_VAR_Move_on_Car], 0;
|
cmp dword ptr ds:[FO_VAR_Move_on_Car], 0;
|
||||||
jz noCar;
|
jz noCar;
|
||||||
mov edx, FO_VAR_carCurrentArea;
|
mov edx, FO_VAR_carCurrentArea;
|
||||||
mov eax, ForceEncounterMapID;
|
mov eax, ForceEncounterMapID;
|
||||||
|
|||||||
Reference in New Issue
Block a user