diff --git a/sfall/Game/GUI/Text.cpp b/sfall/Game/GUI/Text.cpp new file mode 100644 index 00000000..f3c75370 --- /dev/null +++ b/sfall/Game/GUI/Text.cpp @@ -0,0 +1,181 @@ +/* + * sfall + * Copyright (C) 2008-2021 The sfall team + * + */ + +#include "..\..\main.h" +#include "..\..\FalloutEngine\Fallout2.h" + +#include "Text.h" + +namespace game +{ +namespace gui +{ + +namespace sf = sfall; + +// Returns the position of the newline character, or the position of the character within the specified width (implementation from HRP) +static long GetPositionWidth(const char* text, long width) { + long gapWidth; + __asm { + call dword ptr ds:[FO_VAR_text_spacing]; + mov gapWidth, eax; + } + + long wordCharCount = 0; + long position = 0; + long w = 0; + + char c = text[position]; + while (c) { + if (c == '\\' && text[position + 1] == 'n') return position; + + if (c != ' ') wordCharCount++; else wordCharCount = 0; + + w += gapWidth + fo::util::GetCharWidth(c); + if (w > width) { + // set the position to the beginning of the current word + if (wordCharCount > 1 && ((wordCharCount - 1) != position)) { + position -= wordCharCount; // position at the space char + } + break; + } + c = text[++position]; + } + return position; +} + +// Replaces the implementation of display_print_ function from HRP with support for the newline control character '\n' +// Works with vanilla and HRP 4.1.8 +static void __fastcall DisplayPrintLineBreak(const char* message) { + if (*message == 0 || !fo::var::GetInt(FO_VAR_disp_init)) return; + const char* text = message; + + const long max_lines = 100; // aka FO_VAR_max + long max_disp_chars = 256; // HRP value (vanilla 80) + char* display_string_buf_addr; + + long width = (sf::hrpIsEnabled) ? sf::GetIntHRPValue(HRP_VAR_disp_width) : 0; + if (width == 0) { + width = 167; // vanilla size + max_disp_chars = 80; + display_string_buf_addr = (char*)FO_VAR_display_string_buf; // array size 100x80 + } else { + display_string_buf_addr = (char*)sf::HRPAddress(HRP_VAR_display_string_buf); // array size 100x256, allocated by HRP + } + + if (!(fo::var::combat_state & 1)) { + long time = fo::var::GetInt(FO_VAR_bk_process_time); + if ((time - fo::var::GetInt(FO_VAR_lastTime)) >= 500) { + *fo::var::SetInt(FO_VAR_lastTime) = time; + fo::func::gsound_play_sfx_file((const char*)0x50163C); // "monitor" + } + } + + long font = fo::var::curr_font_num; + fo::func::text_font(101); + + unsigned char bulletChar = 149; + long wChar = fo::util::GetCharWidth(bulletChar); + width -= (wChar + fo::var::GetInt(FO_VAR_max_disp)); + + do { + char* display_string_buf = &display_string_buf_addr[max_disp_chars * fo::var::GetInt(FO_VAR_disp_start)]; + + long pos = GetPositionWidth(text, width); + + if (bulletChar) { + *display_string_buf = bulletChar; + display_string_buf++; + bulletChar = 0; + width += wChar; + } + + std::strncpy(display_string_buf, text, pos); + display_string_buf[pos] = 0; + + if (text[pos] == ' ') { + pos++; + } else if (text[pos] == '\\' && text[pos + 1] == 'n') { + pos += 2; // position after the 'n' character + } + text += pos; + + *fo::var::SetInt(FO_VAR_disp_start) = (fo::var::GetInt(FO_VAR_disp_start) + 1) % max_lines; + } while (*text); + + *fo::var::SetInt(FO_VAR_disp_curr) = fo::var::GetInt(FO_VAR_disp_start); + + fo::func::text_font(font); + __asm call fo::funcoffs::display_redraw_; +} + +static void __declspec(naked) sf_display_print() { + __asm { + push ecx; + mov ecx, eax; // message + call DisplayPrintLineBreak; + pop ecx; + retn; + } +} + +static void __stdcall SplitPrintMessage(char* message, void* printFunc) { + char* text = message; + while (*text) { + if (text[0] == '\\' && text[1] == 'n') { + *text = 0; // set null terminator + + __asm mov eax, message; + __asm call printFunc; + + *text = '\\'; + text += 2; // position after the 'n' character + message = text; + } else { + text++; + } + } + // print the last line or the all text if there was no line break + if (message != text) { + __asm mov eax, message; + __asm call printFunc; + } +} + +static void __declspec(naked) sf_display_print_alt() { + __asm { + push ecx; + push fo::funcoffs::display_print_; + push eax; // message + call SplitPrintMessage; + pop ecx; + retn; + } +} + +static void __declspec(naked) sf_inven_display_msg() { + __asm { + push ecx; + push fo::funcoffs::inven_display_msg_; + push eax; // message + call SplitPrintMessage; + pop ecx; + retn; + } +} + +void Text::init() { + // Support for the newline control character '\n' in the object description in pro_*.msg files + void* printFunc = sf_display_print; // for vanilla and HRP 4.1.8 + if (sf::hrpIsEnabled && !sf::hrpVersionValid) { + printFunc = sf_display_print_alt; + } + sf::SafeWriteBatch((DWORD)printFunc, {0x46ED87, 0x49AD7A}); // setup_inventory_, obj_examine_ + sf::SafeWrite32(0x472F9A, (DWORD)&sf_inven_display_msg); // inven_obj_examine_func_ +} + +} +} \ No newline at end of file diff --git a/sfall/Game/GUI/Text.h b/sfall/Game/GUI/Text.h new file mode 100644 index 00000000..047e1ed2 --- /dev/null +++ b/sfall/Game/GUI/Text.h @@ -0,0 +1,20 @@ +/* + * sfall + * Copyright (C) 2008-2021 The sfall team + * + */ + +#pragma once + +namespace game +{ +namespace gui +{ + +class Text { +public: + static void init(); +}; + +} +} \ No newline at end of file diff --git a/sfall/ReplacementFuncs.h b/sfall/ReplacementFuncs.h index 6d70375d..32e950c5 100644 --- a/sfall/ReplacementFuncs.h +++ b/sfall/ReplacementFuncs.h @@ -6,6 +6,8 @@ #pragma once +#include "Game\GUI\Text.h" + #include "Game\combatAI.h" #include "Game\inventory.h" #include "Game\items.h" diff --git a/sfall/ddraw.vcxproj b/sfall/ddraw.vcxproj index ef4bf313..91f57aed 100644 --- a/sfall/ddraw.vcxproj +++ b/sfall/ddraw.vcxproj @@ -293,6 +293,7 @@ + @@ -406,6 +407,7 @@ + diff --git a/sfall/ddraw.vcxproj.filters b/sfall/ddraw.vcxproj.filters index f731436f..62e0d19a 100644 --- a/sfall/ddraw.vcxproj.filters +++ b/sfall/ddraw.vcxproj.filters @@ -22,6 +22,9 @@ {eb2be45d-bfdf-4a70-920d-4d616202a597} + + {e19ca1b6-f754-47a9-9a6d-5ce44592d8ab} + @@ -349,6 +352,12 @@ Modules + + FalloutEngine + + + Game\GUI + @@ -648,6 +657,9 @@ Modules + + Game\GUI + diff --git a/sfall/main.cpp b/sfall/main.cpp index 653cc570..2b20d85d 100644 --- a/sfall/main.cpp +++ b/sfall/main.cpp @@ -103,6 +103,8 @@ DWORD HRPAddress(DWORD addr) { char falloutConfigName[65] = {0}; void InitReplacementHacks() { + game::gui::Text::init(); + game::CombatAI::init(); game::Inventory::init(); game::Items::init();