diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index ac26eb20..b0bf8087 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -29,6 +29,7 @@ WRAP_WATCOM_FFUNC3(void, check_for_death, fo::GameObject*, critter, long, amount WRAP_WATCOM_FFUNC6(long, combat_safety_invalidate_weapon_func, fo::GameObject*, source, fo::GameObject*, weapon, long, hitMode, fo::GameObject*, targetA, DWORD*, outSafeRange, fo::GameObject*, targetB) WRAP_WATCOM_FFUNC3(void, correctFidForRemovedItem, fo::GameObject*, critter, fo::GameObject*, item, long, slotFlag) WRAP_WATCOM_FFUNC7(long, createWindow, const char*, winName, DWORD, x, DWORD, y, DWORD, width, DWORD, height, long, color, long, flags) +WRAP_WATCOM_FFUNC8(void, cscale, BYTE*, src, long, sWidth, long, sHeight, long, sStride, BYTE*, dst, long, width, long, height, long, stride) WRAP_WATCOM_FFUNC4(long, determine_to_hit, fo::GameObject*, source, fo::GameObject*, target, long, bodyPart, long, hitMode) WRAP_WATCOM_FFUNC3(void, display_inventory, long, inventoryOffset, long, visibleOffset, long, mode) WRAP_WATCOM_FFUNC4(void, display_target_inventory, long, inventoryOffset, long, visibleOffset, DWORD*, targetInventory, long, mode) @@ -73,7 +74,6 @@ WRAP_WATCOM_FFUNC4(void, wmInterfaceDrawSubTileRectFogged, BYTE*, surface, long, WRAP_WATCOM_FFUNC3(const char*, interpretGetString, fo::Program*, scriptPtr, DWORD, dataType, DWORD, strId) //WRAP_WATCOM_FFUNC6(void, drawScaledBuf, BYTE*, dst, long, dstW, long, dstH, const BYTE*, src, long, w, long, h) -WRAP_WATCOM_FFUNC8(void, cscale, BYTE*, src, long, sWidth, long, sHeight, long, sStride, BYTE*, dst, long, width, long, height, long, stride) /* stdcall */ WRAP_WATCOM_FUNC1(fo::AIcap*, ai_cap, fo::GameObject*, critter) diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index d724000c..1df63d49 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -328,6 +328,7 @@ #define FO_VAR_text_width 0x51E3C0 #define FO_VAR_tile 0x631D30 #define FO_VAR_tile_center_tile 0x66BE34 +#define FO_VAR_tile_mask 0x66B9C4 #define FO_VAR_tile_offx 0x66BDFC #define FO_VAR_tile_offy 0x66BDF8 #define FO_VAR_tile_x 0x66BE18 diff --git a/sfall/FalloutEngine/Variables_def.h b/sfall/FalloutEngine/Variables_def.h index 1f635cc0..29f788c2 100644 --- a/sfall/FalloutEngine/Variables_def.h +++ b/sfall/FalloutEngine/Variables_def.h @@ -21,7 +21,7 @@ VAR_(btncnt, DWORD) VARD(cap, fo::AIcap) // dynamic array VAR_(carCurrentArea, DWORD) VAR_(carGasAmount, long) // from 0 to 80000 -VAR_(cmap, DWORD) +VARA(cmap, fo::PALETTE, 256) VAR_(colorTable, DWORD) VAR_(combat_end_due_to_load, DWORD) VAR_(combat_free_move, DWORD) @@ -143,6 +143,7 @@ VAR_(mouse_y_, DWORD) VAR_(movePointRect, fo::BoundRect) VARA(movie_list, const char*, 17) VAR_(Mutate_, DWORD) +//VAR_(mve_win_rect, fo::BoundRect) VAR_(name_color, DWORD) VAR_(name_font, DWORD) VAR_(name_sort_list, DWORD) @@ -235,6 +236,7 @@ VAR_(text_spacing, DWORD) VAR_(text_to_buf, DWORD) VAR_(text_width, DWORD) VAR_(tile, DWORD) +VARA(tile_mask, BYTE, 512) VAR_(title_color, DWORD) VAR_(title_font, DWORD) VARA(trait_data, fo::TraitInfo, fo::TRAIT_count) diff --git a/sfall/HRP/Init.cpp b/sfall/HRP/Init.cpp index b78e0ca2..3176d71a 100644 --- a/sfall/HRP/Init.cpp +++ b/sfall/HRP/Init.cpp @@ -229,7 +229,7 @@ void Setting::init(const char* exeFileName, std::string &cmdline) { if (Setting::ExternalEnabled()) { char infoMsg[512]; sf::Translate::Get("sfall", "HiResInfo", - "This version of sfall has its own integrated High Resolution mode patch, which is compatible with the High Resolution Patch by Mash.\n\n" + "This version of sfall has its own integrated High Resolution mode patch, which is compatible with the settings of the High Resolution Patch by Mash.\n\n" "If you want to continue using the High Resolution Patch by Mash without seeing this message, disable the 'HiResMode' option in the ddraw.ini file.\n" "Or you can disable the external HRP to get new graphic improvements from sfall.\n\n" "Do you want to disable the High Resolution Patch by Mash?", infoMsg, 512); @@ -256,18 +256,6 @@ void Setting::init(const char* exeFileName, std::string &cmdline) { // Read High Resolution config - /* - if (IniReader::GetInt("Main", "SCALE_2X", 0, f2ResIni)) { - SCR_WIDTH /= 2; - SCR_HEIGHT /= 2; - - if (SCR_WIDTH < 640) SCR_WIDTH = 640; - if (SCR_HEIGHT < 480) SCR_HEIGHT = 480; - - SCALE_2X = true; - }; - */ - int windowed = (sf::IniReader::GetInt("Main", "WINDOWED", 0, f2ResIni) != 0) ? 1 : 0; if (windowed && sf::IniReader::GetInt("Main", "WINDOWED_FULLSCREEN", 0, f2ResIni)) { windowed += 1; @@ -285,9 +273,14 @@ void Setting::init(const char* exeFileName, std::string &cmdline) { if (COLOUR_BITS != 32 && COLOUR_BITS != 24 && COLOUR_BITS != 16) COLOUR_BITS = 32; } - if (SCR_HEIGHT >= 960 && SCR_WIDTH >= 1280 && sf::IniReader::GetInt("Main", "SCALE_2X", 0, f2ResIni)) { - SCR_WIDTH /= 2; - SCR_HEIGHT /= 2; + if (sf::IniReader::GetInt("Main", "SCALE_2X", 0, f2ResIni)) { + if (SCR_HEIGHT < 960 && SCR_WIDTH < 1280) { + SCR_WIDTH = 640; + SCR_HEIGHT = 480; + } else { + SCR_WIDTH /= 2; + SCR_HEIGHT /= 2; + } SCALE_2X = 1; } diff --git a/sfall/HRP/InterfaceBar.cpp b/sfall/HRP/InterfaceBar.cpp index 74fb3ca0..ce0adcc0 100644 --- a/sfall/HRP/InterfaceBar.cpp +++ b/sfall/HRP/InterfaceBar.cpp @@ -565,13 +565,13 @@ void IFaceBar::init() { if (IFACE_BAR_MODE > 0) { // Set view map height to game resolution // replace subtract 99 with add 1 - sf::SafeWrite8(0x481CDF, 1); - sf::SafeWrite8(0x481E2E, 1); + sf::SafeWrite8(0x481CDF, 1); // iso_init_ + sf::SafeWrite8(0x481E2E, 1); // iso_init_ // replace subtract 99 with add 1 - sf::SafeWrite8(0x481DC3, -1); - sf::SafeWrite8(0x4827A9, -1); + sf::SafeWrite8(0x481DC3, -1); // iso_init_ + sf::SafeWrite8(0x4827A9, -1); // map_scroll_ // remove subtract 100 - sf::SafeWrite8(0x48284F, 0); + sf::SafeWrite8(0x48284F, 0); // map_scroll_ } sf::LoadGameHook::OnBeforeGameClose() += []() { diff --git a/sfall/HRP/ViewMap/EdgeBorder.cpp b/sfall/HRP/ViewMap/EdgeBorder.cpp index 8bdb1fbb..b2f06319 100644 --- a/sfall/HRP/ViewMap/EdgeBorder.cpp +++ b/sfall/HRP/ViewMap/EdgeBorder.cpp @@ -17,12 +17,11 @@ namespace HRP namespace sf = sfall; -static EdgeBorder::Edge* MapEdgeData; +static EdgeBorder::Edge* MapEdgeData = nullptr; static EdgeBorder::Edge* currentMapEdge; // reference static long edgeVersion; // 0 - version 1 (obsolete), 1 - version 2 (current) -static bool isDefaultSetEdge; -//bool isLoadingMapEdge; +static bool isSetDefaultEdge; long EdgeBorder::EdgeVersion() { return edgeVersion; } @@ -120,7 +119,7 @@ static void SetDefaultEdgeData() { edge->nextEdgeData = nullptr; } edgeVersion = 0; - isDefaultSetEdge = true; + isSetDefaultEdge = true; } // Implementation from HRP by Mash @@ -212,8 +211,6 @@ static fo::DbFile* LoadMapEdgeFileSub(char* mapName) { } static void __fastcall LoadMapEdgeFile() { - //isLoadingMapEdge = 0; - fo::DbFile* file = LoadMapEdgeFileSub(sf::LoadGameHook::mapLoadingName); if (file) { // load error fo::func::db_fclose(file); @@ -223,7 +220,7 @@ static void __fastcall LoadMapEdgeFile() { // Implementation from HRP by Mash long EdgeBorder::GetCenterTile(long tile, long mapLevel) { - if (!isDefaultSetEdge) SetDefaultEdgeData(); // needed at game initialization + if (!isSetDefaultEdge) SetDefaultEdgeData(); // needed at game initialization long tX, tY; ViewMap::GetTileCoordOffset(tile, tX, tY); @@ -333,6 +330,8 @@ long EdgeBorder::CheckBorder(long tile) { void EdgeBorder::init() { sf::LoadGameHook::OnBeforeMapLoad() += LoadMapEdgeFile; + + sf::LoadGameHook::OnBeforeGameClose() += []() { delete[] MapEdgeData; }; } } diff --git a/sfall/HRP/ViewMap/EdgeBorder.h b/sfall/HRP/ViewMap/EdgeBorder.h index 9ac4e0e6..1d861251 100644 --- a/sfall/HRP/ViewMap/EdgeBorder.h +++ b/sfall/HRP/ViewMap/EdgeBorder.h @@ -31,7 +31,6 @@ public: }; } - private: ~Edge() { Release(); } diff --git a/sfall/HRP/ViewMap/EdgeClipping.cpp b/sfall/HRP/ViewMap/EdgeClipping.cpp index 47835013..a1adc3c1 100644 --- a/sfall/HRP/ViewMap/EdgeClipping.cpp +++ b/sfall/HRP/ViewMap/EdgeClipping.cpp @@ -7,6 +7,7 @@ #include "..\..\main.h" #include "..\..\FalloutEngine\Fallout2.h" +#include "..\InterfaceBar.h" #include "EdgeBorder.h" #include "ViewMap.h" @@ -218,13 +219,69 @@ static void __declspec(naked) obj_render_post_roof_hook_rect_inside_bound() { } } +static long rightLimit = -1; + +static long __fastcall gmouse_scr_offy_map_limit() { + rightLimit = -1; + + long bottom = fo::var::scr_size.offy; + if (bottom > mapVisibleArea.bottom) { + return mapVisibleArea.bottom + 1 + 99; // +99 takes into account the subtraction in the engine + } + + if (IFaceBar::IFACE_BAR_MODE) { + long x, y; + fo::func::mouse_get_position(&x, &y); + fo::Window* win = fo::func::GNW_find(fo::var::interfaceWindow); + + if ((x + 25) > win->rect.offx || (x + 68) < win->rect.x) { + bottom += 100; // does not enter the interface area + } else if ((x + 25) < win->rect.x) { + bottom += 100; + rightLimit = win->rect.x; + } + } + return bottom; +} + +static void __declspec(naked) gmouse_scr_offx_map_limit() { + __asm { + mov eax, ds:[FO_VAR_scr_size + 8]; // scr_size.offx + cmp eax, mapVisibleArea.right; + jle skip; + mov eax, mapVisibleArea.right; + //inc eax; + retn; +skip: + cmp rightLimit, eax; + cmovb eax, rightLimit; + retn; + } +} + +long EdgeClipping::CheckMapClipping() { + if (!ViewMap::EDGE_CLIPPING_ON) return 1; + return fo::func::mouse_click_in(mapVisibleArea.left, mapVisibleArea.top, mapVisibleArea.right, mapVisibleArea.bottom); +} + void EdgeClipping::init() { sf::HookCall(0x4B15F6, refresh_game_hook_rect_inside_bound); + sf::HookCall(0x483EF0, map_scroll_refresh_game_hook_rect_inside_bound); sf::MakeCall(0x44E481, gmouse_check_scrolling_hack); // from HRP 3.06 (TODO: redo the implementation so that the scrolling of the map works) - // Prevents rendering of "post roof" objects (fixes the red pixels from the hex cursor remaining at the edges of the cropped part of the map) + // Prevents the rendering of "post roof" objects (fixes the red pixels from the hex cursor remaining at the edges of the cropped part of the map) sf::HookCall(0x489802, obj_render_post_roof_hook_rect_inside_bound); + + // Limits the creation of the list of action icons beyond the visible map area or the bottom panel of the interface bar + // (this should be removed after changing the implementation of the list of action icons) + // gmouse_handle_event_ + sf::MakeCall(0x44C597, gmouse_scr_offy_map_limit); + sf::MakeCall(0x44C5AC, gmouse_scr_offx_map_limit); + // the same thing, only when hovering the cursor over objects + // gmouse_bk_process_ + sf::MakeCall(0x44BA40, gmouse_scr_offy_map_limit); + sf::MakeCall(0x44BA55, gmouse_scr_offx_map_limit); } } diff --git a/sfall/HRP/ViewMap/EdgeClipping.h b/sfall/HRP/ViewMap/EdgeClipping.h index 3ce71752..a54e6be3 100644 --- a/sfall/HRP/ViewMap/EdgeClipping.h +++ b/sfall/HRP/ViewMap/EdgeClipping.h @@ -13,6 +13,8 @@ class EdgeClipping { public: static void init(); + + static long CheckMapClipping(); }; } diff --git a/sfall/HRP/ViewMap/ViewMap.cpp b/sfall/HRP/ViewMap/ViewMap.cpp index 608ffdd3..0aa61d69 100644 --- a/sfall/HRP/ViewMap/ViewMap.cpp +++ b/sfall/HRP/ViewMap/ViewMap.cpp @@ -78,9 +78,7 @@ void ViewMap::GetWinMapHalfSize(long &outW, long &outH) { static long __fastcall tile_set_center(long tile, long modeFlags) { if (tile < 0 || tile >= 40000) return -1; // _grid_size: 40000 - long mapElevation = fo::var::map_elevation; - - if (modeFlags) tile = EdgeBorder::GetCenterTile(tile, mapElevation); + if (modeFlags) tile = EdgeBorder::GetCenterTile(tile, fo::var::map_elevation); // get the center tile in what cases? if (!(modeFlags & 2) && fo::var::getInt(FO_VAR_scroll_limiting_on)) { long x, y; @@ -144,7 +142,7 @@ static long __fastcall tile_set_center(long tile, long modeFlags) { if (modeFlags & 1 && fo::var::getInt(FO_VAR_refresh_enabled)) { fo::func::tile_refresh_display(); - return -1; // this fixes a map rendering bug, but why? + return -1; // necessary for the correct rendering of the map } return 0; } @@ -321,21 +319,114 @@ static void __declspec(naked) obj_render_pre_roof_hack_1() { static void __declspec(naked) obj_render_pre_roof_hack() { __asm { mov ebx, [eax]; - lea eax, [edx + ebx]; - cmp eax, 40000; // _grid_size - jge capMax; - cmp eax, 0; - jl capMin; + lea eax, [edx + ebx]; // edx - tilenum + cmp eax, 40000; // _grid_size + jge tileMax; + test eax, eax; + js tileMin; retn; -capMax: +tileMax: mov eax, 39999; retn; -capMin: +tileMin: xor eax, eax; retn; } } +static void __declspec(naked) obj_create_intersect_list_hack() { + __asm { + mov ecx, [ebx + eax]; + lea eax, [edx + ecx]; // edx - tilenum + cmp eax, 40000; // _grid_size + jge tileMax; + test eax, eax; + js tileMin; + retn; +tileMax: + mov eax, 39999; + retn; +tileMin: + xor eax, eax; + retn; + } +} + +// Implementation of tile_num_ engine function without checking the grid boundary (from HRP 3.0.6 by Mash) +static long __fastcall tile_num_HRP(long x, long y) { + long X, Y; + + long yTileOff = y - fo::var::getInt(FO_VAR_tile_offy); + if (yTileOff >= 0) { + Y = yTileOff / 12; + } else { + Y = (yTileOff + 1) / 12 - 1; + } + + long xTileOff = x - fo::var::getInt(FO_VAR_tile_offx) - 16 * Y; + if (xTileOff >= 0) { + X = xTileOff / 64; + } else { + //__asm { + // mov eax, xTileOff; + // inc eax; + // mov edx, eax; + // sar edx, 31; // always -1 + // shl edx, 6; // always -64 + // sbb eax, edx; + // sar eax, 6; + // dec eax; + // mov X, eax; + //} + X = ((xTileOff + 1) / 64) - 1; + } + + long xOffset = xTileOff - (X * 64); + long yOffset = yTileOff - 12 * Y; + long tY = X + Y; + long tX = X * 2; + + if (xOffset >= 32) { + xOffset -= 32; + tX++; + } + + long xTile = fo::var::getInt(FO_VAR_tile_x) + tX; + long yTile = fo::var::getInt(FO_VAR_tile_y) + tY; + + switch (fo::var::tile_mask[(32 * yOffset) + xOffset]) { + case 1: + yTile--; + break; + case 2: + if (++xTile & 1) yTile--; + break; + case 3: + if (!(--xTile & 1)) yTile++; + break; + case 4: + yTile++; + break; + } + return (grid_width * yTile) + (grid_width - 1 - xTile); +} + +static void __declspec(naked) obj_render_pre_roof_hack_tile_num() { + __asm { + push ecx; + call tile_num_HRP; + pop ecx; + retn; + } +} + +static void __declspec(naked) obj_create_intersect_list_hack_tile_num() { + __asm { + mov ecx, eax; + jmp tile_num_HRP; + } +} + void ViewMap::init() { if (SCROLL_DIST_X <= 0) SCROLL_DIST_X = 480; else if (SCROLL_DIST_X < 320) SCROLL_DIST_X = 320; @@ -352,11 +443,14 @@ void ViewMap::init() { sf::MakeCall(0x489730, obj_render_pre_roof_hack_0, 1); sf::MakeCall(0x4897E3, obj_render_pre_roof_hack_1); - // Fix - sf::MakeCall(0x489665, obj_render_pre_roof_hack); // find the bug + // Fixes from 3.06 (fix crashes when going beyond the limits of the vanilla grid size of the map) + sf::MakeCall(0x489665, obj_render_pre_roof_hack); + sf::HookCall(0x4895B7, obj_render_pre_roof_hack_tile_num); // replace the tile_num_ function + sf::MakeCall(0x48C66B, obj_create_intersect_list_hack, 1); + sf::HookCall(0x48C5F3, obj_create_intersect_list_hack_tile_num); // replace the tile_num_ function obj_on_screen_rect.width = Setting::ScreenWidth(); - obj_on_screen_rect.height = Setting::ScreenHeight(); // maybe take into account the interface bar and be: height - 100? + obj_on_screen_rect.height = Setting::ScreenHeight(); // (maybe it should take into account the interface bar) sf::SafeWrite32(0x45C886, (DWORD)&obj_on_screen_rect); // replace rectangle in op_obj_on_screen_ EdgeBorder::init(); diff --git a/sfall/Modules/Interface.cpp b/sfall/Modules/Interface.cpp index e7618f76..bbd6aa87 100644 --- a/sfall/Modules/Interface.cpp +++ b/sfall/Modules/Interface.cpp @@ -27,6 +27,7 @@ #include "Worldmap.h" #include "..\HRP\InterfaceBar.h" +#include "..\HRP\viewmap\EdgeClipping.h" #include "Interface.h" @@ -932,6 +933,9 @@ static void SpeedInterfaceCounterAnimsPatch() { static bool IFACE_BAR_MODE = false; static long gmouse_handle_event_hook() { + // check whether the player clicks on the clipping area of the map + if (!HRP::EdgeClipping::CheckMapClipping()) return 0; // block + long countWin = fo::var::num_windows; long ifaceWin = fo::var::interfaceWindow; fo::Window* win = nullptr; @@ -944,7 +948,9 @@ static long gmouse_handle_event_hook() { if (fo::func::mouse_click_in(rect->left, rect->top, rect->right, rect->bottom)) return 0; // 0 - block clicking in the window area } } + if (IFACE_BAR_MODE) return 1; + // if IFACE_BAR_MODE is not enabled, check the display_win window area win = fo::func::GNW_find(fo::var::getInt(FO_VAR_display_win)); RECT *rect = &win->wRect; @@ -1035,6 +1041,10 @@ static void UIAnimationSpeedPatch() { SimplePatch(&addrs[4], 2, "Misc", "PipboyTimeAnimDelay", 50, 0, 127); } +static void HackMouseEvent() { + HookCall(0x44C018, gmouse_handle_event_hook); +} + void Interface::init() { InterfaceWindowPatch(); InventoryCharacterRotationSpeedPatch(); @@ -1055,10 +1065,15 @@ void Interface::init() { // Transparent/Hidden - will not toggle the mouse cursor when the cursor hovers over a transparent/hidden window // ScriptWindow - prevents the player from moving when clicking on the window if the 'Transparent' flag is not set HookCall(0x44B737, gmouse_bk_process_hook); - LoadGameHook::OnBeforeGameInit() += []() { - if (HRP::Setting::VersionIsValid) IFACE_BAR_MODE = (GetIntHRPValue(HRP_VAR_IFACE_BAR_MODE) != 0); - HookCall(0x44C018, gmouse_handle_event_hook); // replaces hack function from HRP - }; + if (HRP::Setting::IsEnabled()) { + IFACE_BAR_MODE = (HRP::IFaceBar::IFACE_BAR_MODE != 0); + HackMouseEvent(); + } else { + LoadGameHook::OnBeforeGameInit() += []() { + if (HRP::Setting::VersionIsValid) IFACE_BAR_MODE = (GetIntHRPValue(HRP_VAR_IFACE_BAR_MODE) != 0); + HackMouseEvent(); // replaces hack function from HRP by Mash + }; + } } void Interface::exit() { diff --git a/sfall/WinProc.cpp b/sfall/WinProc.cpp index 04b5482c..6e5d79ce 100644 --- a/sfall/WinProc.cpp +++ b/sfall/WinProc.cpp @@ -70,12 +70,12 @@ static int __stdcall WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPara break; case WM_PAINT: - if (GetUpdateRect(hWnd, &rect, 0) != 0) { + if (window && GetUpdateRect(hWnd, &rect, 0) != 0) { // it's unclear under what conditions the redrawing is required rect.right -= 1; rect.bottom -= 1; __asm { - //lea eax, rect; - //call fo::funcoffs::win_refresh_all_; + lea eax, rect; + call fo::funcoffs::win_refresh_all_; } } break; @@ -221,9 +221,7 @@ void WinProc::SetMoveKeys() { } void WinProc::Moving() { - if ((moveWindowKey[0] != 0 && KeyDown(moveWindowKey[0])) || - (moveWindowKey[1] != 0 && KeyDown(moveWindowKey[1]))) - { + if (moveWindowKey[0] != 0 && (KeyDown(moveWindowKey[0]) || (moveWindowKey[1] != 0 && KeyDown(moveWindowKey[1])))) { int mx, my; GetMouse(&mx, &my); win.x += mx;