diff --git a/artifacts/scripting/headers/lib.math.h b/artifacts/scripting/headers/lib.math.h index 0d5588c1..9f3191d4 100644 --- a/artifacts/scripting/headers/lib.math.h +++ b/artifacts/scripting/headers/lib.math.h @@ -5,6 +5,17 @@ Numbers... */ +#define above(a, b) (unsigned_comp(a, b) > 0) +#define above_equal(a, b) (unsigned_comp(a, b) >= 0) +#define below(a, b) (unsigned_comp(a, b) < 0) +#define below_equal(a, b) (unsigned_comp(a, b) <= 0) + +procedure unsigned_comp(variable a, variable b) begin + if (a bwxor b) then return 0; + if (b == 0) then return 1; + return 1 if (a / b) else -1; +end + #define MAX(x, y) ((x > y) * x + (x <= y) * y) #define MIN(x, y) ((x < y) * x + (x >= y) * y) #define in_range(x, from, to) (x >= from and x <= to) diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 111b5f40..3578e617 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -65,6 +65,7 @@ #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) diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index 4760fe67..9e49bc4f 100644 --- a/artifacts/scripting/hookscripts.txt +++ b/artifacts/scripting/hookscripts.txt @@ -669,3 +669,17 @@ int arg2 - 1 when the target object is valid to attack, 0 otherwise Obj arg3 - the target object mixed ret1 - 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 arg1 - event type: 0 - when a random encounter occurs, 1 - when the player enters from the world map +int arg2 - the map ID that the encounter will load (see MAPS.h or Maps.txt) +int arg3 - 1 when the encounter occurs is a special encounter, 0 otherwise + +int ret1 - overrides the map ID, or pass -1 to cancel the encounter and continue traveling (only for event type 0) +int ret2 - pass 1 to cancel the encounter and load the specified map from the ret1 (only for event type 0) diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index 3094d6c9..0f1eb2c3 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -60,6 +60,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_Experience_ 0x6681B4 #define FO_VAR_fade_steps 0x6642D0 #define FO_VAR_fallout_game_time 0x51C720 @@ -158,6 +159,8 @@ #define FO_VAR_obj_seen_check 0x6610BC #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 diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp index 09c9400f..400beb05 100644 --- a/sfall/Modules/HookScripts.cpp +++ b/sfall/Modules/HookScripts.cpp @@ -93,6 +93,7 @@ static HooksInjectInfo injectHooks[] = { {HOOK_STDPROCEDURE, Inject_ScriptProcedureHook, false}, {HOOK_STDPROCEDURE_END, Inject_ScriptProcedureHook2, false}, {HOOK_TARGETOBJECT, Inject_TargetObjectHook, false}, + {HOOK_ENCOUNTER, Inject_EncounterHook, false}, }; bool HookScripts::injectAllHooks; diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h index be0a9bc9..4168d27f 100644 --- a/sfall/Modules/HookScripts.h +++ b/sfall/Modules/HookScripts.h @@ -69,6 +69,7 @@ enum HookType HOOK_STDPROCEDURE = 40, HOOK_STDPROCEDURE_END = 41, HOOK_TARGETOBJECT = 42, + HOOK_ENCOUNTER = 43, HOOK_COUNT }; diff --git a/sfall/Modules/HookScripts/MiscHs.cpp b/sfall/Modules/HookScripts/MiscHs.cpp index f0b46e64..15e5b862 100644 --- a/sfall/Modules/HookScripts/MiscHs.cpp +++ b/sfall/Modules/HookScripts/MiscHs.cpp @@ -525,6 +525,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 (not verified) + + 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 + *(DWORD*)FO_VAR_old_world_xpos = fo::var::world_xpos; + *(DWORD*)FO_VAR_old_world_ypos = fo::var::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 + je break; + jmp fo::funcoffs::wmInterfaceRefresh_; +break: + mov eax, hkEncounterMapID; + cmp ds:[FO_VAR_Move_on_Car], 1; + jne noCar; + mov edx, FO_VAR_carCurrentArea; + call fo::funcoffs::wmMatchAreaContainingMapIdx_; + mov eax, hkEncounterMapID; +noCar: + call fo::funcoffs::map_load_idx_; + xor eax, eax; + //mov ds:[0x672E48], eax; // _wmUnkVar00 + mov hkEncounterMapID, -1; +cancelEnc: + inc eax; // 0 - continue movement, 1 - interrupt + add esp, 4; + mov ebx, 0x4C0BC7; + jmp ebx; + } +} + void Inject_BarterPriceHook() { HookCalls(BarterPriceHook, { 0x474D4C, // barter_attempt_transaction_ (offers button) @@ -587,6 +679,11 @@ void Inject_UseSkillOnHook() { SafeWriteBatch((DWORD)&sourceSkillOn, {0x4AB0EF, 0x4AB5C0, 0x4ABAF2}); // fix for time } +void Inject_EncounterHook() { + HookCall(0x4C02AF, wmWorldMap_hook); + HookCall(0x4C095C, wmRndEncounterOccurred_hook); +} + void InitMiscHookScripts() { LoadHookScript("hs_barterprice", HOOK_BARTERPRICE); LoadHookScript("hs_useskillon", HOOK_USESKILLON); @@ -598,6 +695,7 @@ void InitMiscHookScripts() { LoadHookScript("hs_setglobalvar", HOOK_SETGLOBALVAR); LoadHookScript("hs_resttimer", HOOK_RESTTIMER); LoadHookScript("hs_explosivetimer", HOOK_EXPLOSIVETIMER); + LoadHookScript("hs_encounter", HOOK_ENCOUNTER); } } diff --git a/sfall/Modules/HookScripts/MiscHs.h b/sfall/Modules/HookScripts/MiscHs.h index 5e549fd1..b3f5983c 100644 --- a/sfall/Modules/HookScripts/MiscHs.h +++ b/sfall/Modules/HookScripts/MiscHs.h @@ -15,4 +15,5 @@ namespace sfall void Inject_SetGlobalVarHook(); void Inject_RestTimerHook(); void Inject_ExplosiveTimerHook(); + void Inject_EncounterHook(); }