diff --git a/sfall/FalloutEngine/Functions.cpp b/sfall/FalloutEngine/Functions.cpp index ac38c512..7b09e28b 100644 --- a/sfall/FalloutEngine/Functions.cpp +++ b/sfall/FalloutEngine/Functions.cpp @@ -1,20 +1,20 @@ /* -* sfall -* Copyright (C) 2008-2016 The sfall team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * sfall + * Copyright (C) 2008-2016 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #include "FunctionOffsets.h" #include "VariableOffsets.h" @@ -155,9 +155,18 @@ void __declspec(naked) interpretError(const char* fmt, ...) { } long __fastcall tile_num(long x, long y) { - __asm push ebx; // don't delete (bug in tile_num_) + __asm xor ebx, ebx; // don't delete (bug in tile_num_) WRAP_WATCOM_FCALL2(tile_num_, x, y); - __asm pop ebx; +} + +void __fastcall square_xy(long x, long y, long* outSX, long* outSY) { + __asm { + xor ebx, ebx; // don't delete (bug in square_xy_) + mov eax, ecx; + push outSY; + mov ecx, outSX; + call fo::funcoffs::square_xy_; + } } GameObject* __fastcall obj_blocking_at_wrapper(GameObject* obj, DWORD tile, DWORD elevation, void* func) { diff --git a/sfall/FalloutEngine/Functions.h b/sfall/FalloutEngine/Functions.h index af529a98..db550d4e 100644 --- a/sfall/FalloutEngine/Functions.h +++ b/sfall/FalloutEngine/Functions.h @@ -1,20 +1,20 @@ /* -* sfall -* Copyright (C) 2008-2017 The sfall team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ + * sfall + * Copyright (C) 2008-2017 The sfall team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ #pragma once @@ -54,6 +54,8 @@ void __declspec() interpretError(const char* fmt, ...); long __fastcall tile_num(long x, long y); +void __fastcall square_xy(long x, long y, long* outSX, long* outSY); + GameObject* __fastcall obj_blocking_at_wrapper(GameObject* obj, DWORD tile, DWORD elevation, void* func); // Creates a button on a given window diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index e494b982..1334ba55 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -60,6 +60,7 @@ WRAP_WATCOM_FFUNC4(long, register_object_run_to_object, fo::GameObject*, source, WRAP_WATCOM_FFUNC3(long, register_object_play_sfx, fo::GameObject*, object, const char*, sfxName, long, delay) WRAP_WATCOM_FFUNC3(long, scr_get_local_var, long, sid, long, varId, long*, value) WRAP_WATCOM_FFUNC3(long, scr_set_local_var, long, sid, long, varId, long, value) +WRAP_WATCOM_FFUNC3(long, square_coord, long, square, long*, outX, long*, outY) WRAP_WATCOM_FFUNC6(long, text_object_create, fo::GameObject*, object, const char*, text, long, font, long, colorText, long, colorOutline, fo::BoundRect*, rect) WRAP_WATCOM_FFUNC3(long, tile_coord, long, tile, long*, outX, long*, outY) // the fourth argument of the function is not used WRAP_WATCOM_FFUNC3(long, tile_num_in_direction, long, tile, long, rotation, long, distance) diff --git a/sfall/HRP/Init.cpp b/sfall/HRP/Init.cpp index 08a8a825..81c369a2 100644 --- a/sfall/HRP/Init.cpp +++ b/sfall/HRP/Init.cpp @@ -57,6 +57,8 @@ bool HRP::CheckExternalPatch() { } void HRP::init() { + //SafeWrite16(0x4B2EA8, 0x9090); // _show_grid + if (!hrpIsEnabled && IniReader::GetIntDefaultConfig("Main", "HiResMode", 1) == 0) return; // vanilla game mode SCR_WIDTH = IniReader::GetInt("Main", "SCR_WIDTH", 640, f2ResIni); @@ -93,8 +95,8 @@ void HRP::init() { std::string x = trim(IniReader::GetString("MAPS", "SCROLL_DIST_X", "480", 16, f2ResIni)); std::string y = trim(IniReader::GetString("MAPS", "SCROLL_DIST_Y", "400", 16, f2ResIni)); - ViewMap::SCROLL_DIST_X = (x == "HALF_SCRN") ? (SCR_WIDTH / 2) + 32 : std::atol(x.c_str()); - ViewMap::SCROLL_DIST_Y = (y == "HALF_SCRN") ? (SCR_HEIGHT / 2) + 24 : std::atol(y.c_str()); + ViewMap::SCROLL_DIST_X = (!x.compare(0, 9, "HALF_SCRN")) ? (SCR_WIDTH / 2) + 32 : std::atol(x.c_str()); + ViewMap::SCROLL_DIST_Y = (!y.compare(0, 9, "HALF_SCRN")) ? (SCR_HEIGHT / 2) + 24 : std::atol(y.c_str()); ViewMap::IGNORE_PLAYER_SCROLL_LIMITS = (IniReader::GetInt("MAPS", "IGNORE_PLAYER_SCROLL_LIMITS", 0, f2ResIni) != 0); ViewMap::IGNORE_MAP_EDGES = (IniReader::GetInt("MAPS", "IGNORE_MAP_EDGES", 0, f2ResIni) != 0); @@ -106,20 +108,27 @@ void HRP::init() { IniReader::GetString("Main", "f2_res_patches", "data", MAX_PATH, f2ResIni) ); - // Inject hacks + /* Inject hacks */ SafeWrite32(0x482E30, FO_VAR_mapEntranceTileNum); // map_load_file_ (_tile_center_tile to _mapEntranceTileNum) - // Set the resolution for GNW95_init_mode_ex_ - SafeWrite32(0x4CAD6B, SCR_WIDTH); // 640 - SafeWrite32(0x4CAD66, SCR_HEIGHT); // 480 + if (SCR_WIDTH != 640 || SCR_HEIGHT != 480) { + // Set the resolution for GNW95_init_mode_ex_ + SafeWrite32(0x4CAD6B, SCR_WIDTH); // 640 + SafeWrite32(0x4CAD66, SCR_HEIGHT); // 480 - // Set the resolution for the overlapping temporary black window when loading/starting the game - // main_load_new_ - SafeWrite32(0x480D6C, SCR_HEIGHT); - SafeWrite32(0x480D84, SCR_WIDTH); - // gnw_main_ - SafeWrite32(0x480AFA, SCR_HEIGHT); // -100 for ifacebar? - SafeWrite32(0x480B04, SCR_WIDTH); + // Set the resolution for the overlapping temporary black window when loading/starting the game + // main_load_new_ + SafeWrite32(0x480D6C, SCR_HEIGHT); + SafeWrite32(0x480D84, SCR_WIDTH); + // gnw_main_ + SafeWrite32(0x480AFA, SCR_HEIGHT); // -100 for ifacebar? + SafeWrite32(0x480B04, SCR_WIDTH); + // LoadGame_ + SafeWrite32(0x47C6E0, SCR_HEIGHT); + SafeWrite32(0x47C6E5, SCR_WIDTH); + SafeWrite32(0x47C703, SCR_WIDTH); + SafeWrite32(0x47C70D, SCR_HEIGHT); + } // Inits SplashScreen::init(); diff --git a/sfall/HRP/ViewMap/EdgeBorder.cpp b/sfall/HRP/ViewMap/EdgeBorder.cpp index d0ed9987..c4c6a8f3 100644 --- a/sfall/HRP/ViewMap/EdgeBorder.cpp +++ b/sfall/HRP/ViewMap/EdgeBorder.cpp @@ -16,15 +16,15 @@ namespace sfall { static EdgeBorder::Edge* MapEdgeData; - -// reference -EdgeBorder::Edge* currentMapEdge; - -EdgeBorder::Edge* EdgeBorder::CurrentMapEdge() { return currentMapEdge; } +static EdgeBorder::Edge* currentMapEdge; // reference static long edgeVersion; // 0 - version 1 (obsolete), 1 - version 2 (current) -bool isLoadingMapEdge; -bool isDefaultSetEdge; +static bool isDefaultSetEdge; +//bool isLoadingMapEdge; + +long EdgeBorder::EdgeVersion() { return edgeVersion; } + +EdgeBorder::Edge* EdgeBorder::CurrentMapEdge() { return currentMapEdge; } // Implementation from HRP by Mash static void CalcEdgeData(EdgeBorder::Edge* edgeData, long w, long h) { @@ -67,7 +67,7 @@ static void CalcEdgeData(EdgeBorder::Edge* edgeData, long w, long h) { long rectH = (edgeData->borderRect.bottom - edgeData->borderRect.top) / 2; long _rectH = rectH; if (rectH % 24) { - rectH -= rectH % 24; // truncate + rectH -= rectH % 24; _rectH = rectH + 24; } if (rectH < h) { @@ -78,7 +78,6 @@ static void CalcEdgeData(EdgeBorder::Edge* edgeData, long w, long h) { edgeData->borderRect.bottom -= h; } - // borderRect: right is less than left if ((edgeData->borderRect.left < edgeData->borderRect.right) || (edgeData->borderRect.left - edgeData->borderRect.right) == 32) { edgeData->borderRect.left = edgeData->borderRect.right; } @@ -197,6 +196,7 @@ static fo::DbFile* LoadMapEdgeFileSub(char* mapName) { if (getValue != mapLevel) break; // next level EdgeBorder::Edge *edge = new EdgeBorder::Edge; + edge->prevEdgeData = edgeData; edge->nextEdgeData = nullptr; edge->squareRect = edgeData->squareRect; // rect copy edgeData->nextEdgeData = edge; @@ -303,8 +303,8 @@ long EdgeBorder::CheckBorder(long tile) { long x, y; ViewMap::GetTileCoordOffset(tile, x, y); - if (x > currentMapEdge->borderRect.left || x < currentMapEdge->borderRect.right || - y > currentMapEdge->borderRect.bottom || y < currentMapEdge->borderRect.top) + if (x > currentMapEdge->borderRect.left || x < currentMapEdge->borderRect.right || + y < currentMapEdge->borderRect.top || y > currentMapEdge->borderRect.bottom) { return 0; // block } diff --git a/sfall/HRP/ViewMap/EdgeBorder.h b/sfall/HRP/ViewMap/EdgeBorder.h index ee151802..c05f1b20 100644 --- a/sfall/HRP/ViewMap/EdgeBorder.h +++ b/sfall/HRP/ViewMap/EdgeBorder.h @@ -12,8 +12,8 @@ namespace sfall class EdgeBorder { public: struct Edge { - POINT center; // x/y center of current map screen? - RECT borderRect; + POINT center; // x/y center of current map screen? + RECT borderRect; // right is less than left RECT rect_2; RECT tileRect; RECT squareRect; @@ -36,7 +36,10 @@ public: }; static void init(); + static Edge* CurrentMapEdge(); + static long EdgeVersion(); + static long GetCenterTile(long tile, long mapLevel); static long CheckBorder(long tile); }; diff --git a/sfall/HRP/ViewMap/EdgeClipping.cpp b/sfall/HRP/ViewMap/EdgeClipping.cpp index e9b5c2a6..77cc4001 100644 --- a/sfall/HRP/ViewMap/EdgeClipping.cpp +++ b/sfall/HRP/ViewMap/EdgeClipping.cpp @@ -19,7 +19,7 @@ static RECT mapVisibleArea; // Returns -1 if the scrRect is not in the rectangle of inRect (same as rect_inside_bound_) static __inline long rect_inside_bound(RECT* srcRect, RECT* inRect, RECT* outRect) { - *outRect = *srcRect; // srcRect copy to outRect + if (srcRect != outRect) *outRect = *srcRect; // srcRect copy to outRect if (inRect->right < srcRect->left || inRect->left > srcRect->right || @@ -111,7 +111,7 @@ static long __fastcall rect_inside_bound_clip(RECT* srcRect, RECT* inRect, RECT* if (ViewMap::EDGE_CLIPPING_ON) { if (CheckRect(outRect)) { // when do you need the fill below? - long height = outRect->bottom - outRect->top + 1; + long height = (outRect->bottom - outRect->top) + 1; if (height > 0) { long width = (outRect->right - outRect->left) + 1; if (width > 0) { @@ -139,7 +139,7 @@ static void __declspec(naked) refresh_game_hook_rect_inside_bound() { static long __fastcall rect_inside_bound_scroll_clip(RECT* srcRect, RECT* inRect, RECT* outRect) { long height = (srcRect->bottom - srcRect->top) + 1; if (height > 0) { - long width = srcRect->right - srcRect->left + 1; + long width = (srcRect->right - srcRect->left) + 1; if (width > 0) { ClearRect(width, height, srcRect); } @@ -159,9 +159,10 @@ static void __declspec(naked) map_scroll_refresh_game_hook_rect_inside_bound() { } static long __fastcall MouseCheckArea(long x, long y) { - if (x < (mapVisibleArea.left) || x > (mapVisibleArea.right) || y < mapVisibleArea.top || y >= mapVisibleArea.bottom) { + if (!ViewMap::EDGE_CLIPPING_ON) return 0; + if (x < mapVisibleArea.left || x > mapVisibleArea.right || y < mapVisibleArea.top || y >= mapVisibleArea.bottom) { if (fo::func::win_get_top_win(x, y) == fo::var::getInt(FO_VAR_display_win)) { - return -7; + return 40000; } } return 0; @@ -186,7 +187,8 @@ check: jnz set; retn; set: - mov ebx, eax; + mov ebx, -7; + //mov ecx, 3; retn; } } diff --git a/sfall/HRP/ViewMap/ViewMap.cpp b/sfall/HRP/ViewMap/ViewMap.cpp index 767a2a70..075338d4 100644 --- a/sfall/HRP/ViewMap/ViewMap.cpp +++ b/sfall/HRP/ViewMap/ViewMap.cpp @@ -17,6 +17,10 @@ namespace sfall { +const long grid_width = 200; // _grid_width +const long square_width = 100; +const long square_length = 100; + static Rectangle obj_on_screen_rect; long ViewMap::SCROLL_DIST_X; @@ -39,15 +43,15 @@ void ViewMap::GetCoordFromOffset(long &inOutX, long &inOutY) { } void ViewMap::GetTileCoord(long tile, long &outX, long &outY) { - int x = tile % 200; // tile % _grid_width // 20100%200 = 100 - int y = (tile / 200) + (x / 2); // tile / _grid_width + x / 2 // 20100/200 = 100+(100/2) = y:150 ??? + int x = tile % grid_width; // 20100%200 = 100 + int y = (tile / grid_width) + (x / 2); // 20100/200 = 100+(100/2) = y:150 ??? outY = y; outX = (2 * x) - y; // 2*100 = 200-150 = x:50 ??? } void ViewMap::GetTileCoordOffset(long tile, long &outX, long &outY) { - int x = tile % 200; // tile % _grid_width // 20100%200 = 100 - int y = (tile / 200) + (x / 2); // tile / _grid_width + x / 2 // 20100/200 = 100+(100/2) = y:150 ??? + int x = tile % grid_width; // 20100%200 = 100 + int y = (tile / grid_width) + (x / 2); // 20100/200 = 100+(100/2) = y:150 ??? y &= 0xFFFFFFFE; // even to down x = (2 * x) + 200 - y; // 2*100 = 200+200-150 = x:250 ??? @@ -112,8 +116,8 @@ static long __fastcall tile_set_center(long tile, long modeFlags) { fo::var::setInt(FO_VAR_tile_center_tile) = tile; - long tile_y = tile / 200; // _grid_width - long tile_x = 200 - (tile % 200) - 1; + long tile_y = tile / grid_width; + long tile_x = 200 - (tile % grid_width) - 1; if (tile_x & 1) { tile_x--; @@ -155,11 +159,178 @@ static void __declspec(naked) tile_set_center_hack_replacement() { static void __declspec(naked) tile_scroll_to_hack_replacement() { __asm { - //mov edx, 3; // modeFlags is always 2 + mov edx, 3; // modeFlags is always 2 jmp tile_set_center_hack_replacement; } } +static long __fastcall square_rect_render_floor(long y, long id, long x) { + if (x < 0 || x >= square_width || y < 0 || y >= square_length) return -1; + + auto edge = EdgeBorder::CurrentMapEdge(); + if (EdgeBorder::EdgeVersion() && (x > edge->squareRect.left || x < edge->squareRect.right || y > edge->squareRect.bottom || y < edge->squareRect.top)) { + return 1; + } + return id; +} + +static void __declspec(naked) square_render_floor_hook_art_id() { + __asm { + mov ecx, [esp + 0x24 + 4]; + push esi; + call square_rect_render_floor; + test eax, eax; + js skipDraw; // -1 + mov edx, eax; + xor ecx, ecx; + mov eax, 4; + jmp fo::funcoffs::art_id_; + +skipDraw: + mov [esp], 0x4B2AD4; + retn 4; + } +} + +static long __fastcall square_rect_render_roof(long y, long id, long x) { + if (x < 0 || x >= square_width || y < 0 || y >= square_length) return -1; + + auto edge = EdgeBorder::CurrentMapEdge(); + if (EdgeBorder::EdgeVersion() && (x > (edge->squareRect.left + 2) || x < (edge->squareRect.right + 2) || + y > (edge->squareRect.bottom + 3) || y < (edge->squareRect.top + 3))) + { + return -1; + } + return id; +} + +static void __declspec(naked) square_render_roof_hook_art_id() { + using namespace fo; + __asm { + mov ecx, [esp + 0x20 + 4]; + push edi; + call square_rect_render_roof; + test eax, eax; + js skipDraw; // -1 + mov edx, eax; + xor ecx, ecx; + mov eax, OBJ_TYPE_TILE; + jmp fo::funcoffs::art_id_; + +skipDraw: + mov eax, esi; + retn 4; + } +} + +static void __declspec(naked) square_roof_intersect_hook_art_id() { + using namespace fo; + __asm { + mov ecx, [esp + 0x8 + 4]; + push [esp + 0x4 + 4]; + call square_rect_render_roof; + test eax, eax; + js skipDraw; // -1 + mov edx, eax; + xor ecx, ecx; + mov eax, OBJ_TYPE_TILE; + jmp fo::funcoffs::art_id_; + +skipDraw: + xor eax, eax; + mov [esp], 0x4B2C05; + retn 4; + } +} + +static void __fastcall square_obj_render(fo::BoundRect* rect, long tag) { + if (EdgeBorder::EdgeVersion() == 0) return; + + long x0, y0, x1, y1, x2, y2, x3, y3; + + fo::func::square_xy(rect->x, rect->offy, &x0, &y0); + fo::func::square_xy(rect->x, rect->y, &x1, &y1); + fo::func::square_xy(rect->offx, rect->y, &x2, &y2); + fo::func::square_xy(rect->offx, rect->offy, &x3, &y3); + + if (++x0 > square_width) x0 = square_width - 1; + if (--x2 < 0) x2 = 0; + if (--y1 < 0) y1 = 0; + if (++y3 > square_length) y3 = square_length - 1; + + if (y1 >= y3 || x2 >= x0) return; // top >= bottom / left >= right + + long Y = y1; + long sY = square_width * Y; + + auto edge = EdgeBorder::CurrentMapEdge(); + do { + long X = x2; + do { + if (X > edge->squareRect.left && ((edge->field_48 >> 24) & 1) == tag || + Y < edge->squareRect.top && ((edge->field_48 >> 16) & 1) == tag || + X < edge->squareRect.right && ((edge->field_48 >> 8) & 1) == tag || + Y > edge->squareRect.bottom && (edge->field_48 & 1) == tag) + { + long s_x, s_y; + fo::func::square_coord(X + sY, &s_x, &s_y); + __asm { + mov ecx, rect; + mov ebx, s_y; + mov edx, s_x; + mov eax, 0x4000001; // tile fid + call fo::funcoffs::floor_draw_; + } + } + } while (++X < x0); + + sY += square_width; + } while (++Y < y3); // y < bottom +} + +static void __declspec(naked) obj_render_pre_roof_hack_0() { + __asm { + lea ecx, [esp + 4]; + mov edx, 0; + call square_obj_render; + mov edi, [esp + 0x2C + 4]; + test edi, edi; + retn; + } +} + +static void __declspec(naked) obj_render_pre_roof_hack_1() { + __asm { + lea ecx, [esp + 4]; + mov edx, 1; + call square_obj_render; + pop eax; // ret addr + add esp, 0x44; + pop ebp; + pop edi; + jmp eax; + } +} + +// An odd solution to the problem from HRP 3.06 (4.1.8 uses a different solution) +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; + retn; +capMax: + mov eax, 39999; + retn; +capMin: + xor eax, eax; + retn; + } +} + void ViewMap::init() { if (SCROLL_DIST_X <= 0) SCROLL_DIST_X = 480; else if (SCROLL_DIST_X < 320) SCROLL_DIST_X = 320; @@ -170,6 +341,15 @@ void ViewMap::init() { MakeJump(fo::funcoffs::tile_set_center_, tile_set_center_hack_replacement); // 0x4B12F8 MakeJump(fo::funcoffs::tile_scroll_to_, tile_scroll_to_hack_replacement); // 0x4B3924 + HookCall(0x4B2AC0, square_render_floor_hook_art_id); + HookCall(0x4B2261, square_render_roof_hook_art_id); + HookCall(0x4B2BF7, square_roof_intersect_hook_art_id); // need to test object_under_mouse_ + MakeCall(0x489730, obj_render_pre_roof_hack_0, 1); + MakeCall(0x4897E3, obj_render_pre_roof_hack_1); + + // Fix + MakeCall(0x489665, obj_render_pre_roof_hack); // find the bug + obj_on_screen_rect.width = HRP::ScreenWidth(); obj_on_screen_rect.height = HRP::ScreenHeight(); // maybe take into account the interface bar and be: height - 100? SafeWrite32(0x45C886, (DWORD)&obj_on_screen_rect); // replace rectangle in op_obj_on_screen_