From 279f91274ecc67d515f24dc70e7513642970bc32 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Fri, 25 Dec 2020 14:46:47 +0800 Subject: [PATCH] Modified some of the mechanics of "interface_overlay" function Changed "intface_redraw" with one argument to redraw the specified interface window. --- artifacts/scripting/headers/sfall.h | 4 +- artifacts/scripting/sfall function notes.txt | 12 +++- artifacts/scripting/sfall opcode list.txt | 2 - sfall/FalloutEngine/EngineUtils.cpp | 7 +- sfall/FalloutEngine/EngineUtils.h | 4 +- sfall/FalloutEngine/Enums.h | 20 +++--- sfall/FalloutEngine/Structs.h | 28 ++++++++ sfall/Modules/MiscPatches.cpp | 37 ++++++---- .../Modules/Scripting/Handlers/Interface.cpp | 25 ++++--- sfall/Modules/SubModules/GameRender.cpp | 69 +++++++++++-------- sfall/Modules/SubModules/GameRender.h | 2 +- sfall/main.h | 7 -- 12 files changed, 137 insertions(+), 80 deletions(-) diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 3378c682..1ee667d9 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -338,7 +338,8 @@ #define interface_art_draw_ex(winID, artID, x, y, frame, param) sfall_func6("interface_art_draw", winID, artID, x, y, frame, param) #define interface_print(text, winType, x, y, color) sfall_func5("interface_print", text, winType, x, y, color) #define interface_print_width(text, winType, x, y, color, w) sfall_func6("interface_print", text, winType, x, y, color, w) -#define interface_redraw_all sfall_func1("intface_redraw", 1) +#define interface_redraw_all sfall_func1("intface_redraw", -1) +#define interface_redraw_win(winType) sfall_func1("intface_redraw", winType) #define intface_hide sfall_func0("intface_hide") #define intface_is_hidden sfall_func0("intface_is_hidden") #define intface_is_shown(winType) sfall_func1("get_window_attribute", winType) @@ -358,6 +359,7 @@ #define overlay_create(winType) sfall_func2("interface_overlay", winType, 1) #define overlay_clear(winType) sfall_func2("interface_overlay", winType, 2) #define overlay_clear_rectangle(winType, x, y, w, h) sfall_func6("interface_overlay", winType, 2, x, y, w, h) +#define overlay_destroy(winType) sfall_func2("interface_overlay", winType, 0) #define real_dude_obj sfall_func0("real_dude_obj") #define remove_all_timer_events sfall_func0("remove_timer_event") #define remove_timer_event(fixedParam) sfall_func1("remove_timer_event", fixedParam) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index b456951b..2a14fd31 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -402,9 +402,9 @@ Some utility/math functions are available: - works just like vanilla critter_inven_obj, but correctly reports item in player's inactive hand slot > void sfall_func0("intface_redraw") -> void sfall_func1("intface_redraw", bool eachWin) +> void sfall_func1("intface_redraw", int winType) - redraws main game interface, useful to reflect changes after directly changing current player weapons or stats -- eachWin: pass True to redraw all interface windows +- winType: the type number of the interface window (see WINTYPE_* constants in sfall.h). Pass -1 to redraw all interface windows > void sfall_func0("intface_hide") - hides main interface @@ -782,6 +782,14 @@ optional arguments: - fills the rectangle area of the currently selected script window with the specified color, or clears the window with transparent (index 0) color (call the function without arguments) - color: the color index in the game palette (from 0 to 255) +> int sfall_func2("interface_overlay", int winType, int mode) +> int sfall_func6("interface_overlay", int winType, 2, int x, int y, int width, int height) +- creates an additional drawing surface above the graphic layer of the specified interface window. All subsequent calls of interface_art_draw and interface_print functions will draw on it +- winType: the type number of the interface window (see WINTYPE_* constants in sfall.h) +- mode: 1 - creates a new overlay surface + 2 - clears the overlay area or the specified rectangle defined by the x, y, width, height arguments + 0 - destroys the created overlay surface (frees up the memory allocated to the surface) + ------------------------ ------ MORE INFO ------- ------------------------ diff --git a/artifacts/scripting/sfall opcode list.txt b/artifacts/scripting/sfall opcode list.txt index 60e3cf00..7bdaac29 100644 --- a/artifacts/scripting/sfall opcode list.txt +++ b/artifacts/scripting/sfall opcode list.txt @@ -365,6 +365,4 @@ 0x8280 - any sfall_func7(string funcName, arg1, arg2, arg3, arg4, arg5, arg6, arg7) 0x8281 - any sfall_func8(string funcName, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) - * These functions require AllowUnsafeScripting to be enabled in ddraw.ini - diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index 2db7fb05..41b3db47 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -499,10 +499,11 @@ void RedrawObject(GameObject* obj) { func::tile_refresh_rect(&rect, obj->elevation); } -// Redraws all interface windows -void RefreshGNW(size_t from) { +// Redraws all windows +void RefreshGNW(bool skipOwner) { *(DWORD*)FO_VAR_doing_refresh_all = 1; - for (size_t i = from; i < fo::var::num_windows; i++) { + for (size_t i = 0; i < fo::var::num_windows; i++) { + if (skipOwner && fo::var::window[i]->flags & fo::WinFlags::OwnerFlag) continue; fo::func::GNW_win_refresh(fo::var::window[i], &fo::var::scr_size, 0); } *(DWORD*)FO_VAR_doing_refresh_all = 0; diff --git a/sfall/FalloutEngine/EngineUtils.h b/sfall/FalloutEngine/EngineUtils.h index 8035be38..263b88b1 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -148,8 +148,8 @@ DWORD GetMaxCharWidth(); // Redraw the given object on screen (does not always redraws the whole object) void RedrawObject(GameObject* obj); -// Redraws all interface windows -void RefreshGNW(size_t from = 0); +// Redraws all windows +void RefreshGNW(bool skipOwner = false); UnlistedFrm *LoadUnlistedFrm(char *frmName, unsigned int folderRef); diff --git a/sfall/FalloutEngine/Enums.h b/sfall/FalloutEngine/Enums.h index fb051bd7..19d92a3f 100644 --- a/sfall/FalloutEngine/Enums.h +++ b/sfall/FalloutEngine/Enums.h @@ -789,16 +789,16 @@ namespace Fields { namespace WinFlags { enum WinButtonFlags : long { - OwnerFlag = 0x1, - DontMoveTop = 0x2, - MoveOnTop = 0x4, - Hidden = 0x8, - Exclusive = 0x10, - Transparent = 0x20, - UnknownFlag40 = 0x40, - UnknownFlag80 = 0x80, - ScriptWindow = 0x100, - itsButton = 0x10000, + OwnerFlag = 0x000001, // sfall Render flag, indicates that the window surface is used for rendering the game scene + DontMoveTop = 0x000002, // does not move the window to top when the mouse is clicked in the window area or when it is showing + MoveOnTop = 0x000004, // places the window on top when it is created + Hidden = 0x000008, + Exclusive = 0x000010, + Transparent = 0x000020, + UnknownFlag40 = 0x000040, + UnknownFlag80 = 0x000080, + ScriptWindow = 0x000100, + itsButton = 0x010000, }; } diff --git a/sfall/FalloutEngine/Structs.h b/sfall/FalloutEngine/Structs.h index 3dc39a29..f112724f 100644 --- a/sfall/FalloutEngine/Structs.h +++ b/sfall/FalloutEngine/Structs.h @@ -22,6 +22,18 @@ #include "Enums.h" +namespace sfall +{ + +struct Rectangle { + long x, y, width, height; + + long right() { return x + (width - 1); } + long bottom() { return y + (height - 1); } +}; + +} + namespace fo { @@ -74,6 +86,22 @@ struct BoundRect { long y; long offx; // right long offy; // bottom + + BoundRect() {}; + + BoundRect(sfall::Rectangle rect) { + x = rect.x; + y = rect.y; + offx = rect.right(); + offy = rect.bottom(); + } + + BoundRect(RECT* rect) { + x = rect->left; + y = rect->top; + offx = rect->right; + offy = rect->bottom; + } }; struct RectList { diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp index 7f99dbb8..609dd076 100644 --- a/sfall/Modules/MiscPatches.cpp +++ b/sfall/Modules/MiscPatches.cpp @@ -572,11 +572,32 @@ static void AlwaysReloadMsgs() { } } -static void RemoveWindowRoundingPatch() { +static void InterfaceWindowPatch() { if (GetConfigInt("Misc", "RemoveWindowRounding", 1)) { SafeWriteBatch(CodeType::JumpShort, {0x4D6EDD, 0x4D6F12}); - //SafeWrite16(0x4B8090, 0x04EB); // jmps 0x4B8096 (old) } + + dlog("Applying flags patch for interface windows.", DL_INIT); + + // Remove MoveOnTop flag for interfaces + SafeWrite8(0x46ECE9, (*(BYTE*)0x46ECE9) ^ fo::WinFlags::MoveOnTop); // Player Inventory/Loot/UseOn + SafeWrite8(0x41B966, (*(BYTE*)0x41B966) ^ fo::WinFlags::MoveOnTop); // Automap + + // Set OwnerFlag flag + SafeWrite8(0x481CEC, (*(BYTE*)0x481CEC) | fo::WinFlags::OwnerFlag); // _display_win (map win) + SafeWrite8(0x44E7D2, (*(BYTE*)0x44E7D2) | fo::WinFlags::OwnerFlag); // gmovie_play_ (movie win) + + // Remove OwnerFlag flag + SafeWrite8(0x4B801B, (*(BYTE*)0x4B801B) ^ fo::WinFlags::OwnerFlag); // createWindow_ + // Remove OwnerFlag and Transparent flags + SafeWrite8(0x42F869, (*(BYTE*)0x42F869) ^ (fo::WinFlags::Transparent | fo::WinFlags::OwnerFlag)); // addWindow_ + + dlogr(" Done", DL_INIT); + + // Disable unused code for the RandX and RandY window structure fields (these fields can now be used for other purposes) + SafeWrite32(0x4D630C, 0x9090C031); // xor eax, eax + SafeWrite8(0x4D6310, 0x90); + BlockCall(0x4D6319); } static void InventoryCharacterRotationSpeedPatch() { @@ -685,15 +706,6 @@ static void SkipLoadingGameSettingsPatch() { } } -static void InterfaceDontMoveOnTopPatch() { - //if (GetConfigInt("Misc", "InterfaceDontMoveOnTop", 0)) { - dlog("Applying no MoveOnTop flag for interface patch.", DL_INIT); - SafeWrite8(0x46ECE9, fo::WinFlags::Exclusive); // Player Inventory/Loot/UseOn - SafeWrite8(0x41B966, fo::WinFlags::Exclusive); // Automap - dlogr(" Done", DL_INIT); - //} -} - static void UseWalkDistancePatch() { int distance = GetConfigInt("Misc", "UseWalkDistance", 3) + 2; if (distance > 1 && distance < 5) { @@ -834,7 +846,7 @@ void MiscPatches::init() { PlayIdleAnimOnReloadPatch(); SkilldexImagesPatch(); - RemoveWindowRoundingPatch(); + InterfaceWindowPatch(); ScienceOnCrittersPatch(); InventoryCharacterRotationSpeedPatch(); @@ -862,7 +874,6 @@ void MiscPatches::init() { PartyMemberSkillPatch(); SkipLoadingGameSettingsPatch(); - InterfaceDontMoveOnTopPatch(); UseWalkDistancePatch(); } diff --git a/sfall/Modules/Scripting/Handlers/Interface.cpp b/sfall/Modules/Scripting/Handlers/Interface.cpp index 57584266..d5d5efc9 100644 --- a/sfall/Modules/Scripting/Handlers/Interface.cpp +++ b/sfall/Modules/Scripting/Handlers/Interface.cpp @@ -268,10 +268,17 @@ void op_is_iface_tag_active(OpcodeContext &ctx) { } void mf_intface_redraw(OpcodeContext& ctx) { - if (ctx.arg(0).rawValue() == 0) { + if (ctx.numArgs() == 0) { fo::func::intface_redraw(); } else { - fo::func::RefreshGNW(2); // fake redraw all interfaces (TODO: need a real redraw of interfaces) + // fake redraw interfaces (TODO: need a real redraw of interface?) + long winType = ctx.arg(0).rawValue(); + if (winType == -1) { + fo::func::RefreshGNW(true); + } else { + fo::Window* win = Interface::GetWindow(winType); + if (win && (int)win != -1) fo::func::GNW_win_refresh(win, &win->rect, 0); + } } } @@ -636,7 +643,7 @@ static long InterfaceDrawImage(OpcodeContext& ctx, fo::Window* ifaceWin) { surface + (y * ifaceWin->width) + x, width, height, ifaceWin->width ); - if (!(ctx.arg(0).rawValue() & 0x1000000)) { + if (!(ctx.arg(0).rawValue() & 0x1000000)) { // is set to "Don't redraw" fo::func::GNW_win_refresh(ifaceWin, &ifaceWin->rect, 0); } @@ -828,15 +835,15 @@ void mf_win_fill_color(OpcodeContext& ctx) { } void mf_interface_overlay(OpcodeContext& ctx) { - fo::Window* win = nullptr; long winType = ctx.arg(0).rawValue(); - if (ctx.arg(1).rawValue()) { - win = Interface::GetWindow(winType); - if (!win || (int)win == -1) return; - } + fo::Window* win = Interface::GetWindow(winType); + if (!win || (int)win == -1) return; switch (ctx.arg(1).rawValue()) { + case 0: + GameRender::DestroyOverlaySurface(win); + break; case 1: GameRender::CreateOverlaySurface(win, winType); break; @@ -856,8 +863,6 @@ void mf_interface_overlay(OpcodeContext& ctx) { GameRender::ClearOverlay(win); } break; - //case 0: // unused (reserved) - // GameRender::DestroyOverlaySurface(winType); } } diff --git a/sfall/Modules/SubModules/GameRender.cpp b/sfall/Modules/SubModules/GameRender.cpp index 4cb8adf4..1220a8d7 100644 --- a/sfall/Modules/SubModules/GameRender.cpp +++ b/sfall/Modules/SubModules/GameRender.cpp @@ -25,6 +25,8 @@ namespace sfall { +static void __fastcall sf_GNW_win_refresh(fo::Window* win, RECT* updateRect, BYTE* toBuffer); + class OverlaySurface { private: @@ -34,12 +36,12 @@ private: BYTE* surface = nullptr; public: - //long winType = -1; + long winType = -1; BYTE* Surface() { return surface; } void CreateSurface(fo::Window* win, long winType) { - //this->winType = winType; + this->winType = winType; this->surfWidth = win->width; this->size = win->height * win->width; @@ -66,26 +68,28 @@ public: size_t sizeD = rect.width >> 2; size_t sizeB = rect.width & 3; - size_t stride = sizeD << 2; + size_t strideD = sizeD << 2; + size_t stride = surfWidth - rect.width; long height = rect.height; while (height--) { if (sizeD) { __stosd((DWORD*)surf, 0, sizeD); - surf += stride; + surf += strideD; } if (sizeB) { __stosb(surf, 0, sizeB); surf += sizeB; } + surf += stride; }; } } - /*void DestroySurface() { + void DestroySurface() { delete[] surface; surface = nullptr; - }*/ + } ~OverlaySurface() { delete[] surface; @@ -95,32 +99,45 @@ public: static long indexPosition = 0; void GameRender::CreateOverlaySurface(fo::Window* win, long winType) { - overlaySurfaces[indexPosition].CreateSurface(win, winType); + if (win->randY) return; + if (overlaySurfaces[indexPosition].winType == winType) { + overlaySurfaces[indexPosition].ClearSurface(); + } else { + if (++indexPosition == 5) indexPosition = 0; + overlaySurfaces[indexPosition].CreateSurface(win, winType); + } win->randY = reinterpret_cast(&overlaySurfaces[indexPosition]); - if (++indexPosition == 5) indexPosition = 0; -}; +} BYTE* GameRender::GetOverlaySurface(fo::Window* win) { return reinterpret_cast(win->randY)->Surface(); -}; +} void GameRender::ClearOverlay(fo::Window* win) { if (win->randY) reinterpret_cast(win->randY)->ClearSurface(); -}; +} void GameRender::ClearOverlay(fo::Window* win, Rectangle &rect) { - if (win->randY) reinterpret_cast(win->randY)->ClearSurface(rect); -}; - -/*void GameRender::DestroyOverlaySurface(long winType) { - for (size_t i = 0; i < 5; i++) { - if (overlaySurfaces[i].winType == winType) { - overlaySurfaces[i].DestroySurface(); - overlaySurfaces[i].winType = -1; - //break; - } + if (win->randY) { + reinterpret_cast(win->randY)->ClearSurface(rect); + fo::BoundRect updateRect = rect; + updateRect.x += win->rect.x; + updateRect.y += win->rect.y; + updateRect.offx += win->rect.x; + updateRect.offy += win->rect.y; + sf_GNW_win_refresh(win, reinterpret_cast(&updateRect), 0); } -};*/ +} + +void GameRender::DestroyOverlaySurface(fo::Window* win) { + if (win->randY) { + auto overlay = reinterpret_cast(win->randY); + win->randY = nullptr; + overlay->winType = -1; + overlay->DestroySurface(); + sf_GNW_win_refresh(win, &win->wRect, 0); + } +} static BYTE* GetBuffer() { return (BYTE*)*(DWORD*)FO_VAR_screen_buffer; @@ -202,7 +219,7 @@ static void __fastcall sf_GNW_win_refresh(fo::Window* win, RECT* updateRect, BYT __asm mov rects, eax; if (!rects) return; - rects->rect = { updateRect->left, updateRect->top, updateRect->right, updateRect->bottom }; + rects->rect = updateRect; rects->nextRect = nullptr; /* @@ -312,17 +329,11 @@ void GameRender::init() { 0x4D5D46, // win_init_ (create screen_buffer) 0x4D75E6 // win_clip_ (remove _buffering checking) }); - SafeWrite8(0x42F869, fo::WinFlags::MoveOnTop | fo::WinFlags::OwnerFlag); // addWindow_ (remove Transparent flag) // Custom implementation of the GNW_win_refresh function MakeJump(0x4D6FD9, GNW_win_refresh_hack, 1); // Replace _screendump_buf with _screen_buffer for creating screenshots SafeWriteBatch(FO_VAR_screen_buffer, {0x4C8FD1, 0x4C900D}); - - // Disable unused code for the RandX and RandY window structure fields (these fields can now be used for other purposes) - SafeWrite32(0x4D630C, 0x9090C031); // xor eax, eax - SafeWrite8(0x4D6310, 0x90); - BlockCall(0x4D6319); } } diff --git a/sfall/Modules/SubModules/GameRender.h b/sfall/Modules/SubModules/GameRender.h index cd11efcd..c1535b50 100644 --- a/sfall/Modules/SubModules/GameRender.h +++ b/sfall/Modules/SubModules/GameRender.h @@ -26,7 +26,7 @@ public: static void init(); static void CreateOverlaySurface(fo::Window* win, long winType); -// static void DestroyOverlaySurface(long winType); + static void DestroyOverlaySurface(fo::Window* win); static void ClearOverlay(fo::Window* win); static void ClearOverlay(fo::Window* win, Rectangle &rect); static BYTE* GetOverlaySurface(fo::Window* win); diff --git a/sfall/main.h b/sfall/main.h index 60a2b953..8b7f377c 100644 --- a/sfall/main.h +++ b/sfall/main.h @@ -80,13 +80,6 @@ namespace sfall #define pushadc __asm push eax __asm push edx __asm push ecx #define popadc __asm pop ecx __asm pop edx __asm pop eax -struct Rectangle { - long x, y, width, height; - - long right() { return x + width; } - long bottom() { return y + height; } -}; - // Gets the integer value from given INI file. int iniGetInt(const char* section, const char* setting, int defaultValue, const char* iniFile);