diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 5458b6a8..0c099968 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -105,6 +105,10 @@ FadeMultiplier=100 ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Interface] +;Set to 1 to expand the number of action points displayed on the interface bar +;Requires a new IFACE_APBAR_E.frm in art\intrface\ (included in sfall.dat) to display correctly +ActionPointsBar=0 + ;Set to 1 to use the expanded world map interface ;Set to 2 to skip correcting the position of entrance markers on town maps ;You can use resized FRMs in 700x682 for town maps in the expanded world map interface diff --git a/artifacts/sfall_dat/dat_en/art/intrface/iface_apbar_e.frm b/artifacts/sfall_dat/dat_en/art/intrface/iface_apbar_e.frm new file mode 100644 index 00000000..43da42de Binary files /dev/null and b/artifacts/sfall_dat/dat_en/art/intrface/iface_apbar_e.frm differ diff --git a/artifacts/sfall_dat/sfall.dat b/artifacts/sfall_dat/sfall.dat index ec6c320d..45c64f9a 100644 Binary files a/artifacts/sfall_dat/sfall.dat and b/artifacts/sfall_dat/sfall.dat differ diff --git a/artifacts/sfall_dat/sfall_de.dat b/artifacts/sfall_dat/sfall_de.dat index 3848e5f7..7bed80e0 100644 Binary files a/artifacts/sfall_dat/sfall_de.dat and b/artifacts/sfall_dat/sfall_de.dat differ diff --git a/artifacts/sfall_dat/sfall_fr.dat b/artifacts/sfall_dat/sfall_fr.dat index 27af4adf..958acad3 100644 Binary files a/artifacts/sfall_dat/sfall_fr.dat and b/artifacts/sfall_dat/sfall_fr.dat differ diff --git a/artifacts/sfall_dat/sfall_ru.dat b/artifacts/sfall_dat/sfall_ru.dat index 26a831f9..82550292 100644 Binary files a/artifacts/sfall_dat/sfall_ru.dat and b/artifacts/sfall_dat/sfall_ru.dat differ diff --git a/artifacts/sfall_dat/sfall_zh.dat b/artifacts/sfall_dat/sfall_zh.dat index 1bbf4df4..075718fe 100644 Binary files a/artifacts/sfall_dat/sfall_zh.dat and b/artifacts/sfall_dat/sfall_zh.dat differ diff --git a/sfall/FalloutEngine/VarPointers_def.h b/sfall/FalloutEngine/VarPointers_def.h index 535d9583..77309830 100644 --- a/sfall/FalloutEngine/VarPointers_def.h +++ b/sfall/FalloutEngine/VarPointers_def.h @@ -92,6 +92,7 @@ PTR_(i_worn, fo::GameObject*) PTR_(idle_func, void*) PTR_(In_WorldMap, DWORD) // moving on WorldMap PTR_(info_line, DWORD) +PTR_(interfaceBuffer, BYTE*) PTRC(interfaceWindow, DWORD) PTR_(intfaceEnabled, DWORD) PTR_(intotal, DWORD) @@ -136,6 +137,7 @@ PTR_(mouse_is_hidden, DWORD) PTR_(mouse_x_, DWORD) PTR_(mouse_y, DWORD) PTR_(mouse_y_, DWORD) +PTR_(movePointRect, fo::BoundRect) PTR_(movie_list, const char*) // array of 17 char* PTR_(Mutate_, DWORD) PTR_(name_color, DWORD) diff --git a/sfall/FalloutEngine/VariableOffsets.h b/sfall/FalloutEngine/VariableOffsets.h index 591ab80d..d6b5810c 100644 --- a/sfall/FalloutEngine/VariableOffsets.h +++ b/sfall/FalloutEngine/VariableOffsets.h @@ -151,6 +151,7 @@ #define FO_VAR_In_WorldMap 0x672E1C #define FO_VAR_im_value 0x59E93C #define FO_VAR_info_line 0x5707D0 +#define FO_VAR_interfaceBuffer 0x59D3F4 #define FO_VAR_interfaceWindow 0x519024 #define FO_VAR_intfaceEnabled 0x518F10 #define FO_VAR_intotal 0x43E95C @@ -209,6 +210,7 @@ #define FO_VAR_mouse_x_ 0x6AC7A8 #define FO_VAR_mouse_y 0x664450 #define FO_VAR_mouse_y_ 0x6AC7A4 +#define FO_VAR_movePointRect 0x518FD4 #define FO_VAR_movie_list 0x518DA0 #define FO_VAR_Mutate_ 0x5708B4 #define FO_VAR_name_color 0x56D744 diff --git a/sfall/Modules/ExtraArt.cpp b/sfall/Modules/ExtraArt.cpp index ba26c6ac..9206ded5 100644 --- a/sfall/Modules/ExtraArt.cpp +++ b/sfall/Modules/ExtraArt.cpp @@ -123,6 +123,28 @@ static void ClearInterfaceArtCache() { frmFileCache.clear(); } +TempFrmHandle::TempFrmHandle(fo::FrmFile* frm) : _frm(frm) { +} + +TempFrmHandle::TempFrmHandle(TempFrmHandle&& other) : _frm(other._frm) { + other._frm = nullptr; +} + +TempFrmHandle::~TempFrmHandle() { + if (_frm == nullptr) return; + UnloadFrmFile(_frm); + _frm = nullptr; +} + +bool TempFrmHandle::IsValid() { + return _frm != nullptr; +} + +const fo::FrmFile& TempFrmHandle::Frm() const { + assert(_frm != nullptr); + return *_frm; +} + void ExtraArt::OnGameReset() { ClearInterfaceArtCache(); } diff --git a/sfall/Modules/ExtraArt.h b/sfall/Modules/ExtraArt.h index 14a28d58..95e7fd79 100644 --- a/sfall/Modules/ExtraArt.h +++ b/sfall/Modules/ExtraArt.h @@ -31,6 +31,22 @@ struct PcxFile { PcxFile() : pixelData(nullptr), width(0), height(0) {} }; +class TempFrmHandle { +public: + TempFrmHandle(fo::FrmFile* frm); + TempFrmHandle(TempFrmHandle&&); + ~TempFrmHandle(); + + bool IsValid(); + const fo::FrmFile& Frm() const; +private: + // disallow copy constructor and copy assignment + TempFrmHandle(const TempFrmHandle&); + TempFrmHandle& operator=(TempFrmHandle); + + fo::FrmFile* _frm; +}; + class ExtraArt : public Module { public: const char* name() { return "ExtraArt"; } diff --git a/sfall/Modules/HeroAppearance.cpp b/sfall/Modules/HeroAppearance.cpp index 3f1c5e25..f48dfe6c 100644 --- a/sfall/Modules/HeroAppearance.cpp +++ b/sfall/Modules/HeroAppearance.cpp @@ -586,10 +586,12 @@ static void DrawCharNote(bool style, int winRef, DWORD xPosWin, DWORD yPosWin, B BYTE *PadSurface = new BYTE [280 * 168]; surface_draw(280, 168, widthBG, xPosBG, yPosBG, BGSurface, 280, 0, 0, PadSurface); - fo::FrmFile* frm = LoadUnlistedFrm((style) ? "AppStyle.frm" : "AppRace.frm", fo::OBJ_TYPE_SKILLDEX); - if (frm != nullptr) { - fo::util::DrawToSurface(frm->frameData[0].width, frm->frameData[0].height, 0, 0, frm->frameData[0].width, frm->frameData[0].dataPtr(), 136, 37, 280, 168, PadSurface, 0); // cover buttons pics bottom - UnloadFrmFile(frm); + { + TempFrmHandle frmHandle( LoadUnlistedFrm((style) ? "AppStyle.frm" : "AppRace.frm", fo::ArtType::OBJ_TYPE_SKILLDEX) ); + if (frmHandle.IsValid()) { + const fo::FrmFile& frm = frmHandle.Frm(); + fo::util::DrawToSurface(frm.frameData[0].width, frm.frameData[0].height, 0, 0, frm.frameData[0].width, frm.frameData[0].dataPtr(), 136, 37, 280, 168, PadSurface, 0); // cover buttons pics bottom + } } int oldFont = GetFont(); // store current font diff --git a/sfall/Modules/Interface.cpp b/sfall/Modules/Interface.cpp index 3c0476c1..d19c74a5 100644 --- a/sfall/Modules/Interface.cpp +++ b/sfall/Modules/Interface.cpp @@ -22,6 +22,7 @@ #include "..\FalloutEngine\Fallout2.h" #include "..\SimplePatch.h" #include "..\Utils.h" +#include "ExtraArt.h" #include "Graphics.h" #include "LoadGameHook.h" #include "Worldmap.h" @@ -89,6 +90,82 @@ fo::Window* Interface::GetWindow(long winType) { return (winID > 0) ? fo::func::GNW_find(winID) : nullptr; } +static BYTE movePointBackground[16 * 9 * 5]; +static bool hrpAPBarRectSetup = false; + +static void DrawExtendedApBar() { + const char* const ifaceApBarFrm = "iface_apbar_e.frm"; // 183x13 at 266,10 (x = width - 374) + TempFrmHandle frmHandle( LoadUnlistedFrm(ifaceApBarFrm, fo::ArtType::OBJ_TYPE_INTRFACE) ); + if (!frmHandle.IsValid()) return; + + DWORD ifaceWin = *fo::ptr::interfaceWindow; + fo::Window* win = fo::func::GNW_find(ifaceWin); + if (win == nullptr) return; + + const int destOffsetRight = 374; + const int destOffsetTop = 10; + const fo::FrmFrameData& srcFrame = frmHandle.Frm().frameData[0]; + BYTE* dest = *fo::ptr::interfaceBuffer + (win->width * (destOffsetTop + 1)) - destOffsetRight; + fo::func::buf_to_buf(srcFrame.dataPtr(), srcFrame.width, srcFrame.height, srcFrame.width, dest, win->width); +} + +static __declspec(naked) void intface_init_hook_unlock_iface_frm() { + __asm { + pushadc; + call DrawExtendedApBar; + popadc; + jmp fo::funcoffs::art_ptr_unlock_; + } +} + +static __declspec(naked) void intface_init_hack() { + __asm { + add eax, 9276 - (54 / 2); // x offset + mov edx, 144 - 90; // width + add [esp + 4], edx; + add [esp + 0x10 + 4], edx; + retn; + } +} + +static __declspec(naked) void intface_update_move_points_hack() { + static const DWORD intface_update_move_points_Ret = 0x45EE3E; + __asm { + mov eax, 16 * 9 + push eax; + push 5; + push eax; + jmp intface_update_move_points_Ret; + } +} + +static void APBarRectPatch() { + fo::ptr::movePointRect->x -= (54 / 2); // 54 = 144(new width) - 90(old width) + fo::ptr::movePointRect->offx += (54 / 2); +} + +static void ActionPointsBarPatch() { + dlogr("Applying expanded action points bar patch.", DL_INIT); + if (hrpIsEnabled) { + hrpAPBarRectSetup = true; + } else { + APBarRectPatch(); + } + + // intface_init_ + const DWORD movePointBgAddr[] = {0x45E343, 0x45EE3F}; + SafeWriteBatch((DWORD)&movePointBackground, movePointBgAddr); + // intface_update_move_points_ + const DWORD movePointMaxAddr[] = {0x45EE55, 0x45EE7B, 0x45EE82, 0x45EE9C, 0x45EEA0}; + SafeWriteBatch(16, movePointMaxAddr); + const DWORD ifaceBufDestAddr[] = {0x45EE33, 0x45EEC8, 0x45EF16}; + SafeWriteBatch(9276 - (54 / 2), ifaceBufDestAddr); + + HookCall(0x45D962, intface_init_hook_unlock_iface_frm); + MakeCall(0x45E356, intface_init_hack); + MakeJump(0x45EE38, intface_update_move_points_hack, 1); +} + static long costAP = -1; static __declspec(naked) void intface_redraw_items_hack0() { __asm { @@ -391,7 +468,7 @@ static void WorldmapViewportPatch() { HookCall(0x4C4B8F, wmTownMapInit_hook); } // up/down buttons of the location list (wmInterfaceInit_) - const DWORD wmIfaceUpDnBtnXAddr[] = {0x4C2D3C, 0x4C2D7A}; // offset by X (480) + const DWORD wmIfaceUpDnBtnXAddr[] = {0x4C2D3C, 0x4C2D7A}; SafeWriteBatch(WMAP_WIN_WIDTH - (640 - 480), wmIfaceUpDnBtnXAddr); // offset by X (480) // town/world button (wmInterfaceInit_) @@ -1089,6 +1166,9 @@ static void UIAnimationSpeedPatch() { } void Interface::OnAfterGameInit() { + if (hrpAPBarRectSetup) { + APBarRectPatch(); + } if (worldmapInterface) { WorldmapViewportPatch(); // Note: must be applied after WorldMapSlots patch } @@ -1109,6 +1189,9 @@ void Interface::init() { SafeWriteBatch(CodeType::JumpShort, windowRoundingAddr); //} + if (IniReader::GetConfigInt("Interface", "ActionPointsBar", 0)) { + ActionPointsBarPatch(); + } DrawActionPointsNumber(); WorldMapInterfacePatch(); SpeedInterfaceCounterAnimsPatch();