From 7f8481d03a2161684989ab9508927b41bb77f980 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Fri, 28 Sep 2018 13:59:09 +0800 Subject: [PATCH] Added "get/set_rest_on_map" script functions (from Mr.Stalin) --- artifacts/scripting/headers/sfall.h | 2 + artifacts/scripting/sfall function notes.txt | 11 +++ sfall/FalloutEngine/VariableOffsets.h | 1 + sfall/Modules/HookScripts/Common.h | 3 - sfall/Modules/LoadGameHook.cpp | 24 ++++- sfall/Modules/Scripting/Handlers/Metarule.cpp | 2 + sfall/Modules/Scripting/Handlers/Worldmap.cpp | 23 +++++ sfall/Modules/Scripting/Handlers/Worldmap.h | 4 + sfall/Modules/Worldmap.cpp | 88 +++++++++++++++++++ sfall/Modules/Worldmap.h | 5 ++ 10 files changed, 158 insertions(+), 5 deletions(-) diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index b9b6f5d4..abdfea04 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -230,6 +230,7 @@ #define display_stats sfall_func0("display_stats") #define exec_map_update_scripts sfall_func0("exec_map_update_scripts") #define floor2(value) sfall_func1("floor2", value) +#define get_can_rest_on_map(map, elev) sfall_func2("get_can_rest_on_map", map, elev) #define get_current_inven_size(obj) sfall_func1("get_current_inven_size", obj) #define get_cursor_mode sfall_func0("get_cursor_mode") #define get_flags(obj) sfall_func1("get_flags", obj) @@ -247,6 +248,7 @@ #define lock_is_jammed(obj) sfall_func1("lock_is_jammed", obj) #define outlined_object sfall_func0("outlined_object") #define real_dude_obj sfall_func0("real_dude_obj") +#define set_can_rest_on_map(map, elev, value) sfall_func3("set_can_rest_on_map", map, elev, value) #define set_car_intface_art(artIndex) sfall_func1("set_car_intface_art", artIndex) #define set_cursor_mode(mode) sfall_func1("set_cursor_mode", mode) #define set_dude_obj(critter) sfall_func1("set_dude_obj", critter) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index b803c925..aa0e532b 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -505,6 +505,17 @@ Some utility/math functions are available: - works just like vanilla CreateWin function, but creates a window with MoveOnTop flag if flags argument is not specified, and allows to set additional flags for the created window - MoveOnTop flag allows the created window to be placed on top of the game interface +> void sfall_func3("set_can_rest_on_map", int mapNum, int elev, bool value) +- allows/disallows to rest on the map for the specified level, overrides the can_rest_here values in maps.txt +- mapNum is the map index from maps.txt +- passing -1 to the elev argument will set the rest value for all map elevations +- the set rest value will be stored in sfalldb.sav file (in savegame) + +> int sfall_func2("get_can_rest_on_map", int mapNum, int elev) +- returns the set rest value of the map after using the set_can_rest_on_map function +- returns -1 if the rest value of the map was not previously set (i.e. no data for the map in sfalldb.sav) +- the can_rest_here values in maps.txt are ignored + ------------------------ ------ MORE INFO ------- ------------------------ diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index 62f1eec3..66183f5d 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -109,6 +109,7 @@ #define FO_VAR_main_window 0x5194F0 #define FO_VAR_map_elevation 0x519578 #define FO_VAR_map_global_vars 0x51956C +#define FO_VAR_map_number 0x631D88 #define FO_VAR_map_state 0x631D28 #define FO_VAR_master_db_handle 0x58E948 #define FO_VAR_max 0x56FB50 diff --git a/sfall/Modules/HookScripts/Common.h b/sfall/Modules/HookScripts/Common.h index a81dcfc2..7ad0c449 100644 --- a/sfall/Modules/HookScripts/Common.h +++ b/sfall/Modules/HookScripts/Common.h @@ -44,7 +44,4 @@ void _stdcall EndHook(); #define HookBegin __asm pushad __asm call BeginHook __asm popad -#define hookbegin(a) __asm pushad __asm call BeginHook __asm popad __asm mov argCount, a -#define hookend __asm pushad __asm call EndHook __asm popad - } diff --git a/sfall/Modules/LoadGameHook.cpp b/sfall/Modules/LoadGameHook.cpp index 48a30408..20f670f9 100644 --- a/sfall/Modules/LoadGameHook.cpp +++ b/sfall/Modules/LoadGameHook.cpp @@ -33,6 +33,7 @@ #include "ScriptExtender.h" #include "Scripting\Arrays.h" #include "ExtraSaveSlots.h" +#include "Worldmap.h" #include "LoadGameHook.h" @@ -131,6 +132,14 @@ static void _stdcall SaveGame2() { fo::DisplayPrint(saveSfallDataFailMsg); fo::func::gsound_play_sfx_file("IISXXXX1"); } + + GetSavePath(buf, "db"); + h = CreateFileA(buf, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); + if (h != INVALID_HANDLE_VALUE) { + Worldmap::SaveData(h); + } + CloseHandle(h); + GetSavePath(buf, "fs"); h = CreateFileA(buf, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); if (h != INVALID_HANDLE_VALUE) { @@ -207,6 +216,17 @@ static void _stdcall LoadGame_Before() { } else { dlogr("Cannot read sfallgv.sav - assuming non-sfall save.", DL_MAIN); } + + GetSavePath(buf, "db"); + h = CreateFileA(buf, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if (h != INVALID_HANDLE_VALUE) { + dlog("Loading data from sfalldb.sav...", DL_INIT); + Worldmap::LoadData(h); + CloseHandle(h); + dlogr(" Done", DL_INIT); + } else { + dlogr("Cannot read sfalldb.sav.", DL_INIT); + } } // called whenever game is being reset (prior to loading a save or when returning to main menu) @@ -278,9 +298,9 @@ static void __stdcall NewGame_After() { static void __declspec(naked) main_load_new_hook() { __asm { pushad; - push eax; + mov esi, eax; // keep call NewGame_Before; - pop eax; + mov eax, esi; // restore call fo::funcoffs::main_load_new_; call NewGame_After; popad; diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index 0e4a0b15..73bc4b94 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -85,6 +85,7 @@ static const SfallMetarule metarules[] = { {"display_stats", sf_display_stats, 0, 0}, {"exec_map_update_scripts", sf_exec_map_update_scripts, 0, 0}, {"floor2", sf_floor2, 1, 1, {ARG_NUMBER}}, + {"get_can_rest_on_map", sf_get_rest_on_map, 2, 2, {ARG_INT, ARG_INT}}, {"get_current_inven_size", sf_get_current_inven_size, 1, 1, {ARG_OBJECT}}, {"get_cursor_mode", sf_get_cursor_mode, 0, 0}, {"get_flags", sf_get_flags, 1, 1, {ARG_OBJECT}}, @@ -103,6 +104,7 @@ static const SfallMetarule metarules[] = { {"lock_is_jammed", sf_lock_is_jammed, 1, 1, {ARG_OBJECT}}, {"outlined_object", sf_outlined_object, 0, 0}, {"real_dude_obj", sf_real_dude_obj, 0, 0}, + {"set_can_rest_on_map", sf_set_rest_on_map, 3, 3, {ARG_INT, ARG_INT, ARG_INT}}, {"set_car_intface_art", sf_set_car_intface_art, 1, 1, {ARG_INT}}, {"set_cursor_mode", sf_set_cursor_mode, 1, 1, {ARG_INT}}, {"set_dude_obj", sf_set_dude_obj, 1, 1, {ARG_OBJECT}}, diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.cpp b/sfall/Modules/Scripting/Handlers/Worldmap.cpp index 8dca5b2e..8a0e4616 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.cpp +++ b/sfall/Modules/Scripting/Handlers/Worldmap.cpp @@ -263,5 +263,28 @@ void sf_set_rest_mode(OpcodeContext& ctx) { Worldmap::SetRestMode(ctx.arg(0).asInt()); } +void sf_set_rest_on_map(OpcodeContext& ctx) { + long mapId = ctx.arg(0).asInt(); + if (mapId < 0) { + ctx.printOpcodeError("set_rest_on_map() - invalid map number argument."); + return; + } + long elev = ctx.arg(1).asInt(); + if (elev < -1 && elev > 2) { + ctx.printOpcodeError("set_rest_on_map() - invalid map elevation argument."); + } else { + Worldmap::SetRestMapLevel(mapId, elev, ctx.arg(2).asBool()); + } +} + +void sf_get_rest_on_map(OpcodeContext& ctx) { + long elev = ctx.arg(1).asInt(); + if (elev < 0 && elev > 2) { + ctx.printOpcodeError("get_rest_on_map() - invalid map elevation argument."); + } else { + ctx.setReturn(Worldmap::GetRestMapLevel(elev, ctx.arg(0).asInt())); + } +} + } } diff --git a/sfall/Modules/Scripting/Handlers/Worldmap.h b/sfall/Modules/Scripting/Handlers/Worldmap.h index ffcd0f25..2a1cce7f 100644 --- a/sfall/Modules/Scripting/Handlers/Worldmap.h +++ b/sfall/Modules/Scripting/Handlers/Worldmap.h @@ -50,5 +50,9 @@ void sf_set_rest_heal_time(OpcodeContext&); void sf_set_rest_mode(OpcodeContext&); +void sf_set_rest_on_map(OpcodeContext&); + +void sf_get_rest_on_map(OpcodeContext&); + } } diff --git a/sfall/Modules/Worldmap.cpp b/sfall/Modules/Worldmap.cpp index d769d52c..9cb24c27 100644 --- a/sfall/Modules/Worldmap.cpp +++ b/sfall/Modules/Worldmap.cpp @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include #include #include "..\main.h" @@ -32,6 +33,12 @@ static Delegate<> onWorldmapLoop; static DWORD ViewportX; static DWORD ViewportY; +struct levelRest { + char level[4]; +}; +std::map mapRestInfo; + +static bool restMap; static bool restMode; static bool restTime; @@ -209,6 +216,24 @@ static void __declspec(naked) wmInterfaceInit_text_font_hook() { } } +static void __declspec(naked) critter_can_obj_dude_rest_hook() { + using namespace fo; + __asm { + push eax; + mov ecx, eax; // elevation + mov edx, dword ptr ds:[FO_VAR_map_number]; + call Worldmap::GetRestMapLevel; + xor edx, edx; + cmp eax, edx; + jge skip; + pop eax; + jmp fo::funcoffs::wmMapCanRestHere_; +skip: + add esp, 4; + retn; // overrides + } +} + static void RestRestore() { if (!restMode) return; @@ -413,6 +438,31 @@ void WorldMapFontPatch() { } } +void Worldmap::SaveData(HANDLE file) { + DWORD sizeWrite, count = mapRestInfo.size(); + WriteFile(file, &count, 4, &sizeWrite, 0); + std::map::iterator it; + for (it = mapRestInfo.begin(); it != mapRestInfo.end(); it++) { + WriteFile(file, &it->first, 4, &sizeWrite, 0); + WriteFile(file, &it->second, sizeof(levelRest), &sizeWrite, 0); + } +} + +void Worldmap::LoadData(HANDLE file) { + DWORD count, sizeRead; + ReadFile(file, &count, 4, &sizeRead, 0); + if (sizeRead != 4) return; + if (!restMap) HookCall(0x42E57A, critter_can_obj_dude_rest_hook); + for (DWORD i = 0; i < count; i++) { + DWORD mID; + levelRest elevData; + ReadFile(file, &mID, 4, &sizeRead, 0); + ReadFile(file, &elevData, sizeof(levelRest), &sizeRead, 0); + mapRestInfo.insert(std::make_pair(mID, elevData)); + } + restMap = true; +} + void Worldmap::init() { PathfinderFixInit(); StartingStatePatches(); @@ -426,6 +476,7 @@ void Worldmap::init() { SetCarInterfaceArt(0x1B1); if (restTime) SetRestHealTime(180); RestRestore(); + mapRestInfo.clear(); }; } @@ -465,4 +516,41 @@ void Worldmap::SetRestMode(DWORD mode) { } } +void Worldmap::SetRestMapLevel(int mapId, long elev, bool canRest) { + auto keyIt = mapRestInfo.find(mapId); + if (keyIt != mapRestInfo.end()) { + if (elev == -1) { + keyIt->second.level[++elev] = canRest; + keyIt->second.level[++elev] = canRest; + elev++; + } + keyIt->second.level[elev] = canRest; + } else { + if (!restMap) { + HookCall(0x42E57A, critter_can_obj_dude_rest_hook); + restMap = true; + } + + levelRest elevData = {-1, -1, -1, -1}; + std::pair info (mapId, elevData); + if (elev == -1) { + info.second.level[++elev] = canRest; + info.second.level[++elev] = canRest; + elev++; + } + info.second.level[elev] = canRest; + mapRestInfo.insert(info); + } +} + +long __fastcall Worldmap::GetRestMapLevel(long elev, int mapId) { + if (mapRestInfo.empty()) return -1; + + auto keyIt = mapRestInfo.find(mapId); + if (keyIt != mapRestInfo.end()) { + return keyIt->second.level[elev]; + } + return -1; +} + } diff --git a/sfall/Modules/Worldmap.h b/sfall/Modules/Worldmap.h index ecf22c3f..1e8be5a2 100644 --- a/sfall/Modules/Worldmap.h +++ b/sfall/Modules/Worldmap.h @@ -31,9 +31,14 @@ public: static Delegate<>& OnWorldmapLoop(); + static void Worldmap::SaveData(HANDLE); + static void Worldmap::LoadData(HANDLE); + static void SetCarInterfaceArt(DWORD artIndex); static void SetRestHealTime(DWORD minutes); static void SetRestMode(DWORD mode); + static void SetRestMapLevel(int mapId, long elev, bool canRest); + static long __fastcall GetRestMapLevel(long elev, int mapId); }; }