diff --git a/artifacts/example_mods/WithinPerception/hs_withinperception.ssl b/artifacts/example_mods/WithinPerception/hs_withinperception.ssl new file mode 100644 index 00000000..2e8fd870 --- /dev/null +++ b/artifacts/example_mods/WithinPerception/hs_withinperception.ssl @@ -0,0 +1,58 @@ +/* + Example engine algorithm. + + * The AllowUnsafeScripting option must be enabled. +*/ + +#include "define_lite.h" +#include "command.h" + +#include "sfall.h" +#include "define_extra.h" + + +#define can_see_(source, target) call_offset_r2(0x412BEC, source, target) +#define obj_dist_(target, source) call_offset_r2(0x48BBD4, target, source) + +procedure start; + +procedure start begin + if not(init_hook) then begin + + variable source := get_sfall_arg, + target := get_sfall_arg, + original := get_sfall_arg, + type := get_sfall_arg, /* new arg */ + result := 0, + distance; + + if target then begin + distance := get_critter_stat(source, STAT_pe); + + if can_see_(source, target) then begin + distance *= 5; + if (get_flags(target) bwand FLAG_TRANSGLASS) then distance /= 2; + end + else if combat_is_initialized then distance *= 2; + + if target == dude_obj then begin + if sneak_success then begin + distance /= 4; + if has_skill(target, SKILL_SNEAK) > 120 then distance -= 1; + end + else if dude_is_sneaking then distance := distance * 2 / 3; + end + + if obj_dist_(target, source) <= distance then result := 1; + + // example + if (result) then begin + display_msg("hs_withinperception: " + obj_name(source) + " does he see > " + obj_name(target) + " [original: " + original + " script: " + result + "]"); + end else begin + display_msg("hs_withinperception: " + obj_name(source) + " he does not see > " + obj_name(target) + " [original: " + original + " script: " + result + "]"); + end + end + + //set_sfall_return(result); + end +end diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 2effe6dd..11532475 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -401,6 +401,7 @@ This is fired after the default calculation is made. Critter arg1 - Watcher object Obj arg2 - Target object int arg3 - Result of vanilla function: 1 - within perception range, 0 - otherwise +int arg4 - The type of the called hook: 1 - from the script function obj_can_see_obj, 2 - from the script function obj_can_hear_obj (need option CanHearObjFix enabled), 0 for all other cases int ret1 - overrides the returned result of the function: 0 - not in range (can't see), 1 - in range (will see if not blocked), 2 - forced detection (will see regardless, only used in obj_can_see_obj scripting function which is called by every critter in the game) diff --git a/sfall/Modules/BugFixes.cpp b/sfall/Modules/BugFixes.cpp index d69b8985..d42e1348 100644 --- a/sfall/Modules/BugFixes.cpp +++ b/sfall/Modules/BugFixes.cpp @@ -1292,6 +1292,30 @@ skip: } } +static void __declspec(naked) op_obj_can_see_obj_hack() { + __asm { + mov eax, [esp + 0x2C - 0x28 + 4]; // source + mov ecx, [eax + tile]; // source.tile + cmp ecx, -1; + jz end; + mov eax, [eax + elevation]; + mov edi, [edx + elevation]; // target.elev + cmp eax, edi; // check source.elev == target.elev + retn; +end: + dec ecx; // reset ZF + retn; + } +} + +static void __declspec(naked) op_obj_can_hear_obj_hack() { + __asm { + mov eax, [esp + 0x28 - 0x28 + 4]; // target + mov edx, [esp + 0x28 - 0x24 + 4]; // source + retn; + } +} + void BugFixes::init() { @@ -1648,6 +1672,20 @@ void BugFixes::init() HookCall(0x4C0AEB, wmRndEncounterOccurred_hook); dlogr(" Done", DL_INIT); } + + // Fix op_obj_can_see_obj_ + MakeCall(0x456B63, op_obj_can_see_obj_hack); + SafeWrite16(0x456B76, 0x23EB); // jz > jmp (skip unused engine code) + + //Fix broken op_obj_can_hear_obj_ (enable a function) + if (GetConfigInt("Misc", "CanHearObjFix", 0) != 0) { + dlog("Applying obj_can_hear_obj opcode fix.", DL_INIT); + SafeWrite8(0x4583D8, 0x3B); // change jz offset + SafeWrite8(0x4583DE, 0x74); // jnz > jz + MakeCall(0x4583E0, op_obj_can_hear_obj_hack); + SafeWrite8(0x4583E5, 0x90); + dlogr(" Done", DL_INIT); + } } } diff --git a/sfall/Modules/HookScripts/MiscHs.cpp b/sfall/Modules/HookScripts/MiscHs.cpp index 1b0c3716..c1bd4937 100644 --- a/sfall/Modules/HookScripts/MiscHs.cpp +++ b/sfall/Modules/HookScripts/MiscHs.cpp @@ -139,39 +139,62 @@ defaultHandler: } } -static void __declspec(naked) PerceptionRangeHook() { +static void __stdcall PerceptionRangeHook_Script(int type) { __asm { HookBegin; mov args[0], eax; // watcher mov args[4], edx; // target call fo::funcoffs::is_within_perception_; mov args[8], eax; // check result - pushad; + push eax; } - argCount = 3; + args[3] = type; + + argCount = 4; RunHookScript(HOOK_WITHINPERCEPTION); EndHook(); __asm { - popad; + pop eax; cmp cRet, 1; cmovnb eax, rets[0]; + } +} + +static void __declspec(naked) PerceptionRangeHook() { + __asm { + push ecx; + push 0; + call PerceptionRangeHook_Script; + pop ecx; retn; } } -static const DWORD PerceptionRangeBonusHack_back = 0x456BA7; -static const DWORD PerceptionRangeBonusHack_skip = 0x456BDC; -static void __declspec(naked) PerceptionRangeBonusHack() { +static void __declspec(naked) PerceptionRangeSeeHook() { __asm { - call PerceptionRangeHook; + push ecx; + push 1; + call PerceptionRangeHook_Script; + pop ecx; cmp eax, 2; - jne nevermind; // return - mov dword ptr[esp + 16], 1; - jmp PerceptionRangeBonusHack_skip; // skip blocking check + jne nevermind; // normal return + dec eax; + mov dword ptr[esp + 0x2C - 0x1C + 4], eax; // set 1, skip blocking check + dec eax; nevermind: - jmp PerceptionRangeBonusHack_back; + retn; + } +} + +static void __declspec(naked) PerceptionRangeHearHook() { + __asm { + push ecx; + push 2; + call PerceptionRangeHook_Script; + pop ecx; + retn; } } @@ -396,9 +419,9 @@ void Inject_WithinPerceptionHook() { 0x42BC87, 0x42BC9F, 0x42BD04, - 0x458403 }); - MakeJump(0x456BA2, PerceptionRangeBonusHack); + HookCall(0x456BA2, PerceptionRangeSeeHook); + HookCall(0x458403, PerceptionRangeHearHook); } void Inject_CarTravelHook() {