diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 38ed754f..beefc5dc 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -73,7 +73,8 @@ #define LIST_ALL (9) //Valid flags for force_encounter_with_flags -#define ENCOUNTER_FLAG_NO_CAR (1) +#define ENCOUNTER_FLAG_NO_CAR (1) +#define ENCOUNTER_FLAG_LOCK (2) // block new forced encounter by the next function call until the current specified encounter occurs //The attack types returned by get_attack_type #define ATKTYPE_LWEP1 (0) diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index 38488da5..1653b2d8 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -132,6 +132,7 @@ #define FO_VAR_max 0x56FB50 #define FO_VAR_maxScriptNum 0x51C7CC #define FO_VAR_Meet_Frank_Horrigan 0x672E04 +#define FO_VAR_Move_on_Car 0x672E64 #define FO_VAR_mouse_hotx 0x6AC7D0 #define FO_VAR_mouse_hoty 0x6AC7CC #define FO_VAR_mouse_is_hidden 0x6AC790 diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.cpp b/sfall/Modules/Scripting/Handlers/Worldmap.cpp index 16da6813..f8eb6dd2 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.cpp +++ b/sfall/Modules/Scripting/Handlers/Worldmap.cpp @@ -31,77 +31,58 @@ namespace sfall namespace script { -static DWORD EncounteredHorrigan; -static DWORD ForceEnconterMapID; +static DWORD ForceEnconterMapID = -1; +static DWORD ForceEnconterFlags; -static void _stdcall ForceEncounterRestore() { - *(DWORD*)0x672E04 = EncounteredHorrigan; - SafeWrite32(0x4C070E, *(DWORD*)0x4C0718); // map id - SafeWrite16(0x4C06D8, 0x5175); - SafeWrite32(0x4C071D, 0xFFFC2413); - SafeWrite8(0x4C0706, 0x75); +DWORD ForceEncounterRestore() { + long long data = 0x672E043D83; // cmp ds:_Meet_Frank_Horrigan, 0 + SafeWriteBytes(0x4C06D1, (BYTE*)&data, 5); + ForceEnconterFlags = 0; + DWORD mapID = ForceEnconterMapID; + ForceEnconterMapID = -1; + return mapID; } -static void __declspec(naked) wmRndEncounterOccurred_hook() { +static void __declspec(naked) wmRndEncounterOccurred_hack() { __asm { - push ecx; - call ForceEncounterRestore; - pop ecx; + test ForceEnconterFlags, 0x1; // _NoCar flag + jnz noCar; + cmp ds:[FO_VAR_Move_on_Car], 0; + jz noCar; + mov edx, FO_VAR_carCurrentArea; mov eax, ForceEnconterMapID; - jmp fo::funcoffs::map_load_idx_; + call fo::funcoffs::wmMatchAreaContainingMapIdx_; +noCar: + //push ecx; + // TODO: implement a blinking red icon + call ForceEncounterRestore; + //pop ecx; + push 0x4C0721; // return addr + jmp fo::funcoffs::map_load_idx_; // eax - mapID } } -static void __fastcall ForceEncounter(DWORD mapID, DWORD flags) { - EncounteredHorrigan = *(DWORD*)0x672E04; +void sf_force_encounter(OpcodeContext& cxt) { + if (ForceEnconterFlags & (1 << 31)) return; // wait prev. encounter + + DWORD mapID = cxt.arg(0).rawValue(); + if (mapID < 0) { + cxt.printOpcodeError("%s() - invalid map number.", cxt.getOpcodeName()); + return; + } + if (ForceEnconterMapID == -1) MakeJump(0x4C06D1, wmRndEncounterOccurred_hack); + ForceEnconterMapID = mapID; - SafeWrite32(0x4C070E, mapID); - SafeWrite16(0x4C06D8, 0x13EB); // jmp 0x4C06ED - HookCall(0x4C071C, wmRndEncounterOccurred_hook); - - if (flags & 1) SafeWrite8(0x4C0706, 0xEB); -} - -void __declspec(naked) op_force_encounter() { - __asm { - push ecx; - push edx; - _GET_ARG_INT(end); - xor edx, edx; // flags - mov ecx, eax; // mapID - call ForceEncounter; -end: - pop edx; - pop ecx; - retn; - } -} - -void __declspec(naked) op_force_encounter_with_flags() { - __asm { - pushad; - mov ecx, eax; - call fo::funcoffs::interpretPopShort_; - mov edx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - mov ebx, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopShort_; - mov edi, eax; - mov eax, ecx; - call fo::funcoffs::interpretPopLong_; - cmp dx, VAR_TYPE_INT; - jnz end; - cmp di, VAR_TYPE_INT; - jnz end; - mov edx, ebx; // flags - mov ecx, eax; // mapID - call ForceEncounter; -end: - popad; - retn; + DWORD flags = 0; + if (cxt.numArgs() > 1) { + flags = cxt.arg(1).rawValue(); + if (flags & 2) { // _Lock flag + flags |= (1 << 31); // set bit 31 + } else { + flags = flags & ~(1 << 31); + } } + ForceEnconterFlags = flags; } // world_map_functions diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.h b/sfall/Modules/Scripting/Handlers/Worldmap.h index 1b6d4788..44a88a72 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.h +++ b/sfall/Modules/Scripting/Handlers/Worldmap.h @@ -25,9 +25,9 @@ namespace script class OpcodeContext; -void __declspec() op_force_encounter(); +void sf_force_encounter(OpcodeContext&); -void __declspec() op_force_encounter_with_flags(); +DWORD ForceEncounterRestore(); // world_map_functions void __declspec() op_in_world_map(); diff --git a/sfall/Modules/Scripting/Opcodes.cpp b/sfall/Modules/Scripting/Opcodes.cpp index 7503ff16..d9490aa7 100644 --- a/sfall/Modules/Scripting/Opcodes.cpp +++ b/sfall/Modules/Scripting/Opcodes.cpp @@ -18,6 +18,7 @@ #include "..\..\FalloutEngine\Fallout2.h" #include "..\KillCounter.h" +#include "..\LoadGameHook.h" #include "Handlers\Anims.h" #include "Handlers\Arrays.h" @@ -72,6 +73,7 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x161, "get_critter_extra_stat", sf_get_critter_extra_stat, 2, true, {ARG_OBJECT, ARG_INT}}, {0x163, "get_year", sf_get_year, 0, true}, {0x16c, "key_pressed", sf_key_pressed, 1, true, {ARG_INT}}, + {0x171, "force_encounter", sf_force_encounter, 1, false, {ARG_INT}}, {0x190, "get_perk_available", sf_get_perk_available, 1, true, {ARG_INT}}, {0x195, "set_weapon_knockback", sf_set_object_knockback, 3, false, {ARG_OBJECT, ARG_INT, ARG_NUMBER}}, @@ -143,6 +145,7 @@ static SfallOpcodeInfo opcodeInfoArray[] = { {0x224, "create_message_window", sf_create_message_window, 1, false, {ARG_STRING}}, {0x228, "get_attack_type", sf_get_attack_type, 0, true}, + {0x229, "force_encounter_with_flags", sf_force_encounter, 2, false, {ARG_INT, ARG_INT}}, {0x22d, "create_array", sf_create_array, 2, true, {ARG_INT, ARG_INT}}, {0x22e, "set_array", sf_set_array, 3, false, {ARG_OBJECT, ARG_ANY, ARG_ANY}}, {0x22f, "get_array", sf_get_array, 2, true, {ARG_ANY, ARG_ANY}}, // can also be used on strings @@ -286,7 +289,6 @@ void InitNewOpcodes() { opcodes[0x16e] = op_set_shader_float; opcodes[0x16f] = op_set_shader_vector; opcodes[0x170] = op_in_world_map; - opcodes[0x171] = op_force_encounter; opcodes[0x172] = op_set_world_map_pos; opcodes[0x173] = op_get_world_map_x_pos; opcodes[0x174] = op_get_world_map_y_pos; @@ -384,7 +386,6 @@ void InitNewOpcodes() { opcodes[0x225] = op_remove_trait; opcodes[0x226] = op_get_light_level; opcodes[0x227] = op_refresh_pc_art; - opcodes[0x229] = op_force_encounter_with_flags; opcodes[0x22a] = op_set_map_time_multi; opcodes[0x22b] = op_play_sfall_sound; opcodes[0x22c] = op_stop_sfall_sound; @@ -424,6 +425,10 @@ void InitNewOpcodes() { InitOpcodeInfoTable(); InitMetaruleTable(); + + LoadGameHook::OnGameReset() += []() { + ForceEncounterRestore(); // restore if the encounter did not happen + }; } }