Added support for the newline character '\n' in pro_*.msg files (#401)

This commit is contained in:
NovaRain
2021-08-25 09:52:05 +08:00
parent bb55b97d12
commit 110facfada
6 changed files with 219 additions and 0 deletions
+181
View File
@@ -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>((DWORD)printFunc, {0x46ED87, 0x49AD7A}); // setup_inventory_, obj_examine_
sf::SafeWrite32(0x472F9A, (DWORD)&sf_inven_display_msg); // inven_obj_examine_func_
}
}
}
+20
View File
@@ -0,0 +1,20 @@
/*
* sfall
* Copyright (C) 2008-2021 The sfall team
*
*/
#pragma once
namespace game
{
namespace gui
{
class Text {
public:
static void init();
};
}
}
+2
View File
@@ -6,6 +6,8 @@
#pragma once #pragma once
#include "Game\GUI\Text.h"
#include "Game\combatAI.h" #include "Game\combatAI.h"
#include "Game\inventory.h" #include "Game\inventory.h"
#include "Game\items.h" #include "Game\items.h"
+2
View File
@@ -293,6 +293,7 @@
<ClInclude Include="FalloutEngine\Functions.h" /> <ClInclude Include="FalloutEngine\Functions.h" />
<ClInclude Include="FalloutEngine\Functions_def.h" /> <ClInclude Include="FalloutEngine\Functions_def.h" />
<ClInclude Include="Game\combatAI.h" /> <ClInclude Include="Game\combatAI.h" />
<ClInclude Include="Game\GUI\Text.h" />
<ClInclude Include="Game\inventory.h" /> <ClInclude Include="Game\inventory.h" />
<ClInclude Include="Game\items.h" /> <ClInclude Include="Game\items.h" />
<ClInclude Include="Game\objects.h" /> <ClInclude Include="Game\objects.h" />
@@ -406,6 +407,7 @@
<ClCompile Include="FalloutEngine\Variables.cpp" /> <ClCompile Include="FalloutEngine\Variables.cpp" />
<ClCompile Include="FalloutEngine\Functions.cpp" /> <ClCompile Include="FalloutEngine\Functions.cpp" />
<ClCompile Include="Game\combatAI.cpp" /> <ClCompile Include="Game\combatAI.cpp" />
<ClCompile Include="Game\GUI\Text.cpp" />
<ClCompile Include="Game\inventory.cpp" /> <ClCompile Include="Game\inventory.cpp" />
<ClCompile Include="Game\items.cpp" /> <ClCompile Include="Game\items.cpp" />
<ClCompile Include="Game\objects.cpp" /> <ClCompile Include="Game\objects.cpp" />
+12
View File
@@ -22,6 +22,9 @@
<Filter Include="Game"> <Filter Include="Game">
<UniqueIdentifier>{eb2be45d-bfdf-4a70-920d-4d616202a597}</UniqueIdentifier> <UniqueIdentifier>{eb2be45d-bfdf-4a70-920d-4d616202a597}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Game\GUI">
<UniqueIdentifier>{e19ca1b6-f754-47a9-9a6d-5ce44592d8ab}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="main.h" /> <ClInclude Include="main.h" />
@@ -349,6 +352,12 @@
<ClInclude Include="Modules\EngineTweaks.h"> <ClInclude Include="Modules\EngineTweaks.h">
<Filter>Modules</Filter> <Filter>Modules</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="FalloutEngine\VariableOffsets_HRP.h">
<Filter>FalloutEngine</Filter>
</ClInclude>
<ClInclude Include="Game\GUI\Text.h">
<Filter>Game\GUI</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="main.cpp" /> <ClCompile Include="main.cpp" />
@@ -648,6 +657,9 @@
<ClCompile Include="Modules\EngineTweaks.cpp"> <ClCompile Include="Modules\EngineTweaks.cpp">
<Filter>Modules</Filter> <Filter>Modules</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Game\GUI\Text.cpp">
<Filter>Game\GUI</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="version.rc" /> <ResourceCompile Include="version.rc" />
+2
View File
@@ -103,6 +103,8 @@ DWORD HRPAddress(DWORD addr) {
char falloutConfigName[65] = {0}; char falloutConfigName[65] = {0};
void InitReplacementHacks() { void InitReplacementHacks() {
game::gui::Text::init();
game::CombatAI::init(); game::CombatAI::init();
game::Inventory::init(); game::Inventory::init();
game::Items::init(); game::Items::init();