diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 986f63be..5b67e90f 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -123,11 +123,11 @@ ActionPointsBar=0 WorldTravelMarkers=0 ;Uncomment these lines to change the appearance of the markers ;The color index in Fallout default palette (valid range: 1..255; default is 133) -;MarkerColor=133 -;The lengh in pixels of the dots (valid range: 1..10) -;MarkerLength=1 -;The spacing in pixels between the dots (valid range: 1..10) -;MarkerSpaces=2 +;TravelMarkerColor=133 +;The lengh of the dots in pixels (valid range: 1..10) +;TravelMarkerLength=1 +;The spacing between the dots in pixels (valid range: 1..10) +;TravelMarkerSpaces=2 ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Input] diff --git a/sfall/FalloutEngine/EngineUtils.cpp b/sfall/FalloutEngine/EngineUtils.cpp index c27a79c8..f50dade9 100644 --- a/sfall/FalloutEngine/EngineUtils.cpp +++ b/sfall/FalloutEngine/EngineUtils.cpp @@ -212,23 +212,77 @@ void GetObjectsTileRadius(std::vector &objs, long sourceTile, l } } +// Returns the name of the terrain type in the position of the player's marker on the world map +const char* wmGetCurrentTerrainName() { + long* terrainId = *(long**)FO_VAR_world_subtile; + if (terrainId == nullptr) { + __asm { + lea ebx, terrainId; + mov edx, dword ptr ds:[FO_VAR_world_ypos]; + mov eax, dword ptr ds:[FO_VAR_world_xpos]; + call fo::funcoffs::wmFindCurSubTileFromPos_; + } + } + return GetMessageStr(&fo::var::wmMsgFile, 1000 + *terrainId); +} + //--------------------------------------------------------- -//print text to surface -void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWORD TxtWidth, DWORD ToWidth, BYTE *ToSurface) { - DWORD posOffset = Ypos * ToWidth + Xpos; +// copy the area from the interface buffer to the data array +void RectCopyToMemory(long fromX, long fromY, long width, long height, long fromWidth, BYTE* fromBuff, BYTE* toMem) { + fromBuff += fromY * fromWidth + fromX; + long i = 0; + for (long h = 0; h < height; h++) { + for (long w = 0; w < width; w++) { + toMem[i++] = fromBuff[w]; + } + fromBuff += fromWidth; + } +} + +// copy data from memory to the area of the interface buffer +void MemCopyToWinBuffer(long toX, long toY, long width, long height, long toWidth, BYTE* toBuff, BYTE* fromMem) { + toBuff += toY * toWidth + toX; + long i = 0; + for (long h = 0; h < height; h++) { + for (long w = 0; w < width; w++) { + toBuff[w] = fromMem[i++]; + } + toBuff += toWidth; + } +} + +//--------------------------------------------------------- +// print text to surface +void PrintText(char* displayText, BYTE colourIndex, DWORD xPos, DWORD yPos, DWORD txtWidth, DWORD toWidth, BYTE* toSurface) { + DWORD posOffset = yPos * toWidth + xPos; __asm { xor eax, eax; - mov al, ColourIndex; + mov al, colourIndex; + mov edx, displayText; push eax; - mov edx, DisplayText; - mov ebx, TxtWidth; - mov ecx, ToWidth; - mov eax, ToSurface; + mov ebx, txtWidth; + mov eax, toSurface; + mov ecx, toWidth; add eax, posOffset; call dword ptr ds:[FO_VAR_text_to_buf]; } } +void PrintTextFM(char* displayText, BYTE colorIndex, DWORD xPos, DWORD yPos, DWORD txtWidth, DWORD toWidth, BYTE* toSurface) { + DWORD posOffset = yPos * toWidth + xPos; + __asm { + xor eax, eax; + mov al, colorIndex; + mov edx, displayText; + push eax; + mov ebx, txtWidth; + mov eax, toSurface; + mov ecx, toWidth; + add eax, posOffset; + call fo::funcoffs::FMtext_to_buf_; + } +} + //--------------------------------------------------------- //gets the height of the currently selected font DWORD GetTextHeight() { @@ -249,6 +303,13 @@ DWORD GetTextWidth(const char *TextMsg) { } } +DWORD GetTextWidthFM(const char *TextMsg) { + __asm { + mov eax, TextMsg; + call fo::funcoffs::FMtext_width_; //get text width + } +} + //--------------------------------------------------------- //get width of Char for current font DWORD GetCharWidth(char CharVal) { diff --git a/sfall/FalloutEngine/EngineUtils.h b/sfall/FalloutEngine/EngineUtils.h index f9a3c812..f68d055e 100644 --- a/sfall/FalloutEngine/EngineUtils.h +++ b/sfall/FalloutEngine/EngineUtils.h @@ -80,12 +80,20 @@ long __fastcall GetTopWindowID(long xPos, long yPos); void GetObjectsTileRadius(std::vector &objs, long sourceTile, long radius, long elev, long type); +const char* wmGetCurrentTerrainName(); + +void RectCopyToMemory(long fromX, long fromY, long width, long height, long fromWidth, BYTE* fromBuff, BYTE* toMem); + +void MemCopyToWinBuffer(long toX, long toY, long width, long height, long toWidth, BYTE* toBuff, BYTE* fromMem); + // Print text to surface -void PrintText(char *displayText, BYTE colorIndex, DWORD x, DWORD y, DWORD textWidth, DWORD destWidth, BYTE *surface); +void PrintText(char* displayText, BYTE colorIndex, DWORD x, DWORD y, DWORD textWidth, DWORD destWidth, BYTE* surface); +void PrintTextFM(char* displayText, BYTE colorIndex, DWORD x, DWORD y, DWORD textWidth, DWORD destWidth, BYTE* surface); // gets the height of the currently selected font DWORD GetTextHeight(); // gets the length of a string using the currently selected font DWORD GetTextWidth(const char *textMsg); +DWORD GetTextWidthFM(const char *textMsg); // get width of Char for current font DWORD GetCharWidth(char charVal); // get maximum string length for current font - if all characters were maximum width diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h index 81391292..664abab5 100644 --- a/sfall/FalloutEngine/Functions_def.h +++ b/sfall/FalloutEngine/Functions_def.h @@ -25,6 +25,7 @@ WRAP_WATCOM_FFUNC3(FrmFrameData*, frame_ptr, FrmHeaderData*, frm, long, frame, l WRAP_WATCOM_FFUNC3(void, intface_update_items, long, animate, long, modeLeft,long, modeRight) WRAP_WATCOM_FFUNC3(long, item_add_force, GameObject*, critter, GameObject*, item, long, count) WRAP_WATCOM_FFUNC7(void, make_straight_path_func, GameObject*, objFrom, DWORD, tileFrom, DWORD, tileTo, void*, rotationPtr, DWORD*, result, long, flags, void*, func) +WRAP_WATCOM_FFUNC3(long, message_find, DWORD*, msgFile, long, msgNumber, DWORD*, outBuf) WRAP_WATCOM_FFUNC4(long, mouse_click_in, long, x, long, y, long, x_end, long, y_end) WRAP_WATCOM_FFUNC3(GameObject*, obj_blocking_at, GameObject*, object, long, tile, long, elevation) WRAP_WATCOM_FFUNC4(long, obj_move_to_tile, GameObject*, object, long, tile, long, elevation, RECT*, rect) @@ -172,6 +173,7 @@ WRAP_WATCOM_FUNC1(void, win_show, DWORD, winRef) WRAP_WATCOM_FUNC1(void, win_hide, DWORD, winRef) WRAP_WATCOM_FUNC1(BYTE*, win_get_buf, DWORD, winRef) WRAP_WATCOM_FUNC1(void, win_draw, DWORD, winRef) +WRAP_WATCOM_FUNC2(void, win_draw_rect, DWORD, winRef, RECT*, rect) WRAP_WATCOM_FUNC1(void, win_delete, DWORD, winRef) WRAP_WATCOM_FUNC0(long, windowWidth) WRAP_WATCOM_FUNC1(void, wmCarUseGas, long, gasAmount) diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index c327a38e..e0e9d7d3 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -239,16 +239,19 @@ #define FO_VAR_wd_obj 0x59E98C #define FO_VAR_window 0x6ADE58 #define FO_VAR_wmAreaInfoList 0x51DDF8 +#define FO_VAR_wmBkWin 0x51DE14 #define FO_VAR_wmBkWinBuf 0x51DE24 #define FO_VAR_wmLastRndTime 0x51DEA0 #define FO_VAR_wmMaxMapNum 0x51DE10 #define FO_VAR_wmMaxTileNum 0x51DDF0 +#define FO_VAR_wmMsgFile 0x672FB0 #define FO_VAR_wmNumHorizontalTiles 0x51DDF4 #define FO_VAR_wmViewportRightScrlLimit 0x672EFC #define FO_VAR_wmViewportBottomtScrlLimit 0x672F00 #define FO_VAR_wmWorldOffsetX 0x51DE2C #define FO_VAR_wmWorldOffsetY 0x51DE30 #define FO_VAR_wmYesNoStrs 0x51DD90 +#define FO_VAR_world_subtile 0x672E14 #define FO_VAR_world_xpos 0x672E0C #define FO_VAR_world_ypos 0x672E10 #define FO_VAR_WorldMapCurrArea 0x672E08 diff --git a/sfall/FalloutEngine/Variables_def.h b/sfall/FalloutEngine/Variables_def.h index e8a6b1e2..16a40389 100644 --- a/sfall/FalloutEngine/Variables_def.h +++ b/sfall/FalloutEngine/Variables_def.h @@ -79,7 +79,7 @@ VAR_(i_rhand, GameObject*) VAR_(i_wid, DWORD) VAR_(i_worn, GameObject*) VAR_(idle_func, DWORD) -VAR_(In_WorldMap, DWORD) +VAR_(In_WorldMap, DWORD) // moving on WorldMap VAR_(info_line, DWORD) VAR_(interfaceWindow, DWORD) VAR_(intfaceEnabled, DWORD) @@ -209,6 +209,7 @@ VAR_(WhiteColor, BYTE) VAR_(wmAreaInfoList, DWORD) VAR_(wmLastRndTime, DWORD) VAR_(wmMaxTileNum, DWORD) +VAR_(wmMsgFile, MessageList) VAR_(wmNumHorizontalTiles, DWORD) VAR_(wmViewportRightScrlLimit, DWORD) VAR_(wmViewportBottomtScrlLimit, DWORD) diff --git a/sfall/Modules/Interface.cpp b/sfall/Modules/Interface.cpp index 33563044..a20555a7 100644 --- a/sfall/Modules/Interface.cpp +++ b/sfall/Modules/Interface.cpp @@ -16,10 +16,14 @@ * along with this program. If not, see . */ +#include + #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" +#include "..\FalloutEngine\EngineUtils.h" #include "Graphics.h" #include "LoadGameHook.h" +//#include "Worldmap.h" #include "Interface.h" @@ -459,6 +463,8 @@ static void WorldmapViewportPatch() { dlogr(" Done", DL_INIT); } +////////////////////////////// FALLOUT 1 FEATURES ////////////////////////////// + struct DotPosition { long x; long y; @@ -468,7 +474,7 @@ static std::vector dots; static long optionLenDot = 1; static long optionSpaceDot = 2; -static unsigned char colorDot = 133; // color index in palette: R = 252, G = 0, B = 0 +static unsigned char colorDot = 0; // color index in palette: R = 252, G = 0, B = 0 static long spaceLen = 2; static long dotLen = 1; static long dot_xpos = 0; @@ -529,8 +535,31 @@ static void __declspec(naked) DrawingDots() { } } +static std::array wmTmpBuffer; +static bool hoveringHotspot = false; +static bool backImageIsCopy = false; + +static void PrintTerrainType(long x, long y) { + char* terrainText = (char*)fo::wmGetCurrentTerrainName(); + long txtWidth = fo::GetTextWidthFM(terrainText); + if (txtWidth > 100) txtWidth = 100; + + // offset position + y += 4; + x += 25 - (txtWidth / 2); + + fo::PrintTextFM(terrainText, 228, x, y, txtWidth, wmapWinWidth, *(BYTE**)FO_VAR_wmBkWinBuf); // text shadow + fo::PrintTextFM(terrainText, 215, x - 1, y - 1, txtWidth, wmapWinWidth, *(BYTE**)FO_VAR_wmBkWinBuf); +} + +static void PrintTerrainType() { + long y = fo::var::world_ypos - fo::var::wmWorldOffsetY; + long x = fo::var::world_xpos - fo::var::wmWorldOffsetX; + PrintTerrainType(x, y); +} + static void __declspec(naked) wmInterfaceRefresh_hook() { - if (fo::var::target_xpos != -1) { + if (colorDot && fo::var::target_xpos != -1) { if (fo::var::In_WorldMap) { DrawingDots(); } else if (!fo::var::target_xpos && !fo::var::target_ypos) { @@ -540,9 +569,63 @@ static void __declspec(naked) wmInterfaceRefresh_hook() { spaceLen = optionSpaceDot; } } + if (hoveringHotspot && !fo::var::In_WorldMap) { + PrintTerrainType(); + hoveringHotspot = backImageIsCopy = false; + } __asm jmp fo::funcoffs::wmDrawCursorStopped_; } +static long __fastcall wmDetectHotspotHover(long wmMouseX, long wmMouseY) { + long deltaX = abs((long)fo::var::world_xpos - (wmMouseX - 20 + fo::var::wmWorldOffsetX)); + long deltaY = abs((long)fo::var::world_ypos - (wmMouseY - 20 + fo::var::wmWorldOffsetY)); + + bool hovered = hoveringHotspot; + hoveringHotspot = deltaX < 6 && deltaY < 6; + if (hoveringHotspot != hovered) { // if value has changed + // upper left corner + long y = fo::var::world_ypos - fo::var::wmWorldOffsetY; + long x = fo::var::world_xpos - fo::var::wmWorldOffsetX; + long x_offset = x - 25; + if (!backImageIsCopy) { + backImageIsCopy = true; + // copy image to memory (size 100 x 15) + fo::RectCopyToMemory(x_offset, y, 100, 15, wmapWinWidth, *(BYTE**)FO_VAR_wmBkWinBuf, wmTmpBuffer.data()); + PrintTerrainType(x, y); + } else { + // restore saved image + fo::MemCopyToWinBuffer(x_offset, y, 100, 15, wmapWinWidth, *(BYTE**)FO_VAR_wmBkWinBuf, wmTmpBuffer.data()); + backImageIsCopy = false; + } + // redraw worldmap interface rectangle + RECT rect; + rect.top = y; + rect.left = x_offset; + rect.right = x + 100; + rect.bottom = y + 15; + fo::func::win_draw_rect(*(long*)FO_VAR_wmBkWin, &rect); + } + return fo::var::wmWorldOffsetY; // overwritten code +} + +static void __declspec(naked) wmWorldMap_hack() { + __asm { + cmp ds:[FO_VAR_In_WorldMap], 1; // player is moving + jne checkHover; + mov eax, dword ptr ds:[FO_VAR_wmWorldOffsetY]; + retn; +checkHover: + push ecx; + mov ecx, [esp + 0x38 - 0x30 + 8]; // x + mov edx, [esp + 0x38 - 0x34 + 8]; // y + call wmDetectHotspotHover; + pop ecx; + retn; + } +} + +//////////////////////////////////////////////////////////////////////////////// + static void __declspec(naked) wmInterfaceRefreshCarFuel_hack_empty() { __asm { mov byte ptr [eax - 1], 13; @@ -606,21 +689,23 @@ static void WorldMapInterfacePatch() { } if (GetConfigInt("Interface", "WorldTravelMarkers", 0)) { - optionLenDot = GetConfigInt("Interface", "MarkerLength", optionLenDot); - optionSpaceDot = GetConfigInt("Interface", "MarkerSpaces", optionSpaceDot); - int color = GetConfigInt("Interface", "MarkerColor", colorDot); + optionLenDot = GetConfigInt("Interface", "TravelMarkerLength", optionLenDot); + optionSpaceDot = GetConfigInt("Interface", "TravelMarkerSpaces", optionSpaceDot); + int color = GetConfigInt("Interface", "TravelMarkerColor", 133); if (color > 255) color = 255; else if (color < 1) color = 1; colorDot = color; if (optionLenDot > 10) optionLenDot = 10; else if (optionLenDot < 1) optionLenDot = 1; if (optionSpaceDot > 10) optionSpaceDot = 10; else if (optionSpaceDot < 1) optionSpaceDot = 1; - HookCall(0x4C3C7E, wmInterfaceRefresh_hook); // when calling wmDrawCursorStopped_ dots.reserve(512); LoadGameHook::OnGameReset() += []() { dots.clear(); }; } + // Fallout 1 features, travel markers and displaying terrain type + HookCall(0x4C3C7E, wmInterfaceRefresh_hook); // when calling wmDrawCursorStopped_ + MakeCall(0x4BFE84, wmWorldMap_hack); // Car fuel gauge graphics patch MakeCall(0x4C528A, wmInterfaceRefreshCarFuel_hack_empty); diff --git a/sfall/Modules/Worldmap.cpp b/sfall/Modules/Worldmap.cpp index fac7ab3e..94ca2345 100644 --- a/sfall/Modules/Worldmap.cpp +++ b/sfall/Modules/Worldmap.cpp @@ -58,6 +58,10 @@ static float scriptMapMulti = 1.0; static bool addYear = false; // used as additional years indicator static DWORD addedYears = 0; +static void __stdcall WorldmapLoopHook() { + onWorldmapLoop.invoke(); +} + static __declspec(naked) void TimeDateFix() { __asm { test edi, edi; // year buf @@ -120,10 +124,6 @@ end: } } -static void __stdcall WorldmapLoopHook() { - onWorldmapLoop.invoke(); -} - static void __declspec(naked) WorldMapFpsPatch() { __asm { push dword ptr ds:[FO_VAR_last_buttons]; @@ -177,9 +177,9 @@ subLoop: // Only used if the world map speed patch is disabled, so that world map scripts are still run static void __declspec(naked) wmWorldMap_hook() { __asm { - pushadc; + //pushadc; call WorldmapLoopHook; - popadc; + //popadc; jmp fo::funcoffs::get_input_; } } @@ -223,7 +223,7 @@ static void __declspec(naked) wmTownMapFunc_hack() { mov [esi], edx retn; end: - add esp, 4; // destroy the return address + add esp, 4; // destroy the return address mov eax, 0x4C4976; jmp eax; } @@ -315,8 +315,8 @@ end: static void RestRestore() { if (!restMode) return; - restMode = false; + SafeWrite8(0x49952C, 0x85); SafeWrite8(0x497557, 0x85); SafeWrite8(0x42E587, 0xC7);