diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 09f5b9a4..1009e99b 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -308,7 +308,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) @@ -327,6 +328,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 f177c321..64dd6761 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 @@ -728,6 +728,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 6f1350e5..0cd55fe9 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/Define.h b/sfall/Define.h index 2b9baa2d..8a326cea 100644 --- a/sfall/Define.h +++ b/sfall/Define.h @@ -702,16 +702,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.cpp b/sfall/FalloutEngine.cpp index 46da6fe2..22e09222 100644 --- a/sfall/FalloutEngine.cpp +++ b/sfall/FalloutEngine.cpp @@ -1707,10 +1707,11 @@ void RedrawObject(TGameObj* obj) { TileRefreshRect(&rect, obj->elevation); } -// Redraws all interface windows -void RefreshGNW(size_t from) { +// Redraws all windows +void RefreshGNW(bool skipOwner) { *(DWORD*)_doing_refresh_all = 1; - for (size_t i = from; i < *ptr_num_windows; i++) { + for (size_t i = 0; i < *ptr_num_windows; i++) { + if (skipOwner && ptr_window[i]->flags & WinFlags::OwnerFlag) continue; GNWWinRefresh(ptr_window[i], ptr_scr_size, 0); } *(DWORD*)_doing_refresh_all = 0; diff --git a/sfall/FalloutEngine.h b/sfall/FalloutEngine.h index 3b2b61ca..f86bc2f8 100644 --- a/sfall/FalloutEngine.h +++ b/sfall/FalloutEngine.h @@ -1376,7 +1376,7 @@ DWORD __stdcall GetMaxCharWidth(); // Redraw the given object on screen (does not always redraws the whole object) void RedrawObject(TGameObj* obj); -// Redraws all interface windows -void RefreshGNW(size_t from = 0); +// Redraws all windows +void RefreshGNW(bool skipOwner = false); UNLSTDfrm *LoadUnlistedFrm(char *frmName, unsigned int folderRef); diff --git a/sfall/FalloutStructs.h b/sfall/FalloutStructs.h index 019e827a..03e92f45 100644 --- a/sfall/FalloutStructs.h +++ b/sfall/FalloutStructs.h @@ -20,6 +20,13 @@ #include +struct sRectangle { + long x, y, width, height; + + long right() { return x + (width - 1); } + long bottom() { return y + (height - 1); } +}; + /******************************************************************************/ /* FALLOUT2.EXE structs should be placed here */ /******************************************************************************/ diff --git a/sfall/Graphics.cpp b/sfall/Graphics.cpp index 71a42697..17a5fc4c 100644 --- a/sfall/Graphics.cpp +++ b/sfall/Graphics.cpp @@ -1329,6 +1329,8 @@ static __forceinline void UpdateDDSurface(BYTE* surface, int width, int height, } } +static void __fastcall sf_GNW_win_refresh(WINinfo* win, RECT* updateRect, BYTE* toBuffer); + class OverlaySurface { private: @@ -1338,14 +1340,14 @@ private: BYTE* surface; public: - //long winType = -1; + long winType; - OverlaySurface() : size(0), surface(nullptr) {} + OverlaySurface() : size(0), surface(nullptr), winType(-1) {} BYTE* Surface() { return surface; } void CreateSurface(WINinfo* win, long winType) { - //this->winType = winType; + this->winType = winType; this->surfWidth = win->width; this->size = win->height * win->width; @@ -1372,26 +1374,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; @@ -1401,32 +1405,45 @@ public: static long indexPosition = 0; void GameRender_CreateOverlaySurface(WINinfo* 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(WINinfo* win) { return reinterpret_cast(win->randY)->Surface(); -}; +} void GameRender_ClearOverlay(WINinfo* win) { if (win->randY) reinterpret_cast(win->randY)->ClearSurface(); -}; +} void GameRender_ClearOverlay(WINinfo* win, sRectangle &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); + BoundRect updateRect = { rect.x, rect.y, rect.right(), rect.bottom() }; + 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(WINinfo* win) { + if (win->randY) { + OverlaySurface* 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*)_screen_buffer; @@ -1664,18 +1681,12 @@ void Graphics_Init() { 0x4D75E6 // win_clip_ (remove _buffering checking) }; SafeWriteBatch(0x9090, winBufferAddr); - SafeWrite8(0x42F869, WinFlags::MoveOnTop | 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 const DWORD scrdumpBufAddr[] = {0x4C8FD1, 0x4C900D}; SafeWriteBatch(_screen_buffer, scrdumpBufAddr); - - // 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); } void Graphics_Exit() { diff --git a/sfall/Graphics.h b/sfall/Graphics.h index 446b27b5..feb17153 100644 --- a/sfall/Graphics.h +++ b/sfall/Graphics.h @@ -52,7 +52,7 @@ void Gfx_ReleaseMovieTexture(); void Gfx_RefreshGraphics(); void GameRender_CreateOverlaySurface(WINinfo* win, long winType); -//void GameRender_DestroyOverlaySurface(long winType); +void GameRender_DestroyOverlaySurface(WINinfo* win); void GameRender_ClearOverlay(WINinfo* win); void GameRender_ClearOverlay(WINinfo* win, sRectangle &rect); BYTE* GameRender_GetOverlaySurface(WINinfo* win); diff --git a/sfall/Interface.cpp b/sfall/Interface.cpp index ef2ce066..c8c8e872 100644 --- a/sfall/Interface.cpp +++ b/sfall/Interface.cpp @@ -528,6 +528,7 @@ static void SpeedInterfaceCounterAnimsPatch() { } static bool IFACE_BAR_MODE = false; + static long gmouse_handle_event_hook() { long countWin = *ptr_num_windows; long ifaceWin = *ptr_interfaceWindow; diff --git a/sfall/MiscPatches.cpp b/sfall/MiscPatches.cpp index bd6b2344..c18c8c5c 100644 --- a/sfall/MiscPatches.cpp +++ b/sfall/MiscPatches.cpp @@ -551,12 +551,33 @@ static void AlwaysReloadMsgs() { } } -static void RemoveWindowRoundingPatch() { +static void InterfaceWindowPatch() { if (GetConfigInt("Misc", "RemoveWindowRounding", 1)) { const DWORD windowRoundingAddr[] = {0x4D6EDD, 0x4D6F12}; SafeWriteBatch(CODETYPE_JumpShort, windowRoundingAddr); - //SafeWrite16(0x4B8090, 0x04EB); // jmps 0x4B8096 (old) } + + dlog("Applying flags patch for interface windows.", DL_INIT); + + // Remove MoveOnTop flag for interfaces + SafeWrite8(0x46ECE9, (*(BYTE*)0x46ECE9) ^ WinFlags::MoveOnTop); // Player Inventory/Loot/UseOn + SafeWrite8(0x41B966, (*(BYTE*)0x41B966) ^ WinFlags::MoveOnTop); // Automap + + // Set OwnerFlag flag + SafeWrite8(0x481CEC, (*(BYTE*)0x481CEC) | WinFlags::OwnerFlag); // _display_win (map win) + SafeWrite8(0x44E7D2, (*(BYTE*)0x44E7D2) | WinFlags::OwnerFlag); // gmovie_play_ (movie win) + + // Remove OwnerFlag flag + SafeWrite8(0x4B801B, (*(BYTE*)0x4B801B) ^ WinFlags::OwnerFlag); // createWindow_ + // Remove OwnerFlag and Transparent flags + SafeWrite8(0x42F869, (*(BYTE*)0x42F869) ^ (WinFlags::Transparent | 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() { @@ -635,15 +656,6 @@ static void SkipLoadingGameSettingsPatch() { } } -static void InterfaceDontMoveOnTopPatch() { - //if (GetConfigInt("Misc", "InterfaceDontMoveOnTop", 0)) { - dlog("Applying no MoveOnTop flag for interface patch.", DL_INIT); - SafeWrite8(0x46ECE9, WinFlags::Exclusive); // Player Inventory/Loot/UseOn - SafeWrite8(0x41B966, WinFlags::Exclusive); // Automap - dlogr(" Done", DL_INIT); - //} -} - static void UseWalkDistancePatch() { int distance = GetConfigInt("Misc", "UseWalkDistance", 3) + 2; if (distance > 1 && distance < 5) { @@ -790,7 +802,7 @@ void MiscPatches_Init() { PlayIdleAnimOnReloadPatch(); SkilldexImagesPatch(); - RemoveWindowRoundingPatch(); + InterfaceWindowPatch(); ScienceOnCrittersPatch(); InventoryCharacterRotationSpeedPatch(); @@ -814,7 +826,6 @@ void MiscPatches_Init() { KeepWeaponSelectModePatch(); SkipLoadingGameSettingsPatch(); - InterfaceDontMoveOnTopPatch(); UseWalkDistancePatch(); } diff --git a/sfall/ScriptOps/InterfaceOps.hpp b/sfall/ScriptOps/InterfaceOps.hpp index d1b4c9e3..45af150e 100644 --- a/sfall/ScriptOps/InterfaceOps.hpp +++ b/sfall/ScriptOps/InterfaceOps.hpp @@ -341,10 +341,17 @@ static void __declspec(naked) op_is_iface_tag_active() { } static void mf_intface_redraw() { - if (opHandler.arg(0).rawValue() == 0) { + if (opHandler.numArgs() == 0) { IntfaceRedraw(); } else { - 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 = opHandler.arg(0).rawValue(); + if (winType == -1) { + RefreshGNW(true); + } else { + WINinfo* win = Interface_GetWindow(winType); + if (win && (int)win != -1) GNWWinRefresh(win, &win->rect, 0); + } } } @@ -701,7 +708,7 @@ static long __stdcall InterfaceDrawImage(OpcodeHandler& opHandler, WINinfo* ifac surface + (y * ifaceWin->width) + x, width, height, ifaceWin->width ); - if (!(opHandler.arg(0).rawValue() & 0x1000000)) { + if (!(opHandler.arg(0).rawValue() & 0x1000000)) { // is set to "Don't redraw" GNWWinRefresh(ifaceWin, &ifaceWin->rect, 0); } @@ -893,15 +900,15 @@ static void mf_win_fill_color() { } static void mf_interface_overlay() { - WINinfo* win = nullptr; long winType = opHandler.arg(0).rawValue(); - if (opHandler.arg(1).rawValue()) { - win = Interface_GetWindow(winType); - if (!win || (int)win == -1) return; - } + WINinfo* win = Interface_GetWindow(winType); + if (!win || (int)win == -1) return; switch (opHandler.arg(1).rawValue()) { + case 0: + GameRender_DestroyOverlaySurface(win); + break; case 1: GameRender_CreateOverlaySurface(win, winType); break; @@ -921,7 +928,5 @@ static void mf_interface_overlay() { GameRender_ClearOverlay(win); } break; - //case 0: // unused (reserved) - // GameRender_DestroyOverlaySurface(winType); } } diff --git a/sfall/main.h b/sfall/main.h index 4cd1dffc..3722b1ed 100644 --- a/sfall/main.h +++ b/sfall/main.h @@ -76,13 +76,6 @@ struct ddrawDll { #define pushadc __asm push eax __asm push edx __asm push ecx #define popadc __asm pop ecx __asm pop edx __asm pop eax -struct sRectangle { - 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);