diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 4d393bdd..2185f717 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -244,7 +244,6 @@ #define create_win(winName, x, y, w, h) sfall_func5("create_win", winName, x, y, w, h) #define create_win_flag(winName, x, y, w, h, flag) sfall_func6("create_win", winName, x, y, w, h, flag) #define critter_inven_obj2(obj, type) sfall_func2("critter_inven_obj2", obj, type) -#define dialog_box(text) sfall_func1("dialog_box", text) #define dialog_obj sfall_func0("dialog_obj") #define display_stats sfall_func0("display_stats") #define draw_image(pathFile, frame, x, y, noTrans) sfall_func5("draw_image", pathFile, frame, x, y, noTrans) @@ -273,6 +272,7 @@ #define item_weight(obj) sfall_func1("item_weight", obj) #define lock_is_jammed(obj) sfall_func1("lock_is_jammed", obj) #define loot_obj sfall_func0("loot_obj") +#define message_box(text) sfall_func1("message_box", text) #define metarule_exist(metaruleName) sfall_func1("metarule_exist", metaruleName) #define npc_engine_level_up(toggle) sfall_func1("npc_engine_level_up", toggle) #define obj_under_cursor(onlyCritter, includeDude) sfall_func2("obj_under_cursor", onlyCritter, includeDude) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 18de2260..51976a5c 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -623,12 +623,13 @@ optional arguments: - sets a floating text for a town on the world map when hovering the cursor over the player's marker - areaID: the ID number of the town from city.txt -> int sfall_func1("dialog_box", string text1) -> int sfall_func2("dialog_box", string text1, string text2) -> int sfall_func3("dialog_box", string text1, string text2, string text3) -- creates a dialog box with text and returns the result of pressing the button: 0 - No, 1 - Yes -- text1/text2/text3: corresponding text in the dialog box for the first, second, and third lines +> int sfall_func4("message_box", string message, int flags, int color1, int color2) +- creates a dialog box with text and returns the result of pressing the button: 0 - No, 1 - Yes/Done - returns -1 if for some reason the dialog box cannot be created +- message: the text in the dialog box. Use the \n control character to move text to a new line (example: "Hello\nWorld!") +optional arguments: +- flags: mode flags (see DIALOGOUT_* constants in define_extra.h). Pass -1 to skip setting the flags (default flags are DIALOGOUT_NORMAL|DIALOGOUT_YESNO) +- color1/2: the color index in Fallout palette. color1 sets the text color for the first line, and color2 for all subsequent lines of text (default color is 145) ------------------------ ------ MORE INFO ------- diff --git a/sfall/BugFixes.cpp b/sfall/BugFixes.cpp index 4fccf24c..015581b2 100644 --- a/sfall/BugFixes.cpp +++ b/sfall/BugFixes.cpp @@ -2474,6 +2474,14 @@ endLoop: } } +static void __declspec(naked) main_death_scene_hook() { + __asm { + mov eax, 100; + call block_for_tocks_; + jmp get_time_; + } +} + void BugFixesInit() { #ifndef NDEBUG @@ -3115,4 +3123,7 @@ void BugFixesInit() // Fix for party member's equipped weapon being placed in the incorrect item slot after leveling up MakeCall(0x495FD9, partyMemberCopyLevelInfo_hack, 1); + + // Fix the playback of the speech sound file for the death screen + HookCall(0x481409, main_death_scene_hook); } diff --git a/sfall/FalloutEngine.cpp b/sfall/FalloutEngine.cpp index 786c7811..7889156d 100644 --- a/sfall/FalloutEngine.cpp +++ b/sfall/FalloutEngine.cpp @@ -1083,35 +1083,37 @@ long __stdcall WinRegisterButton(DWORD winRef, long xPos, long yPos, long width, } } -void __stdcall DialogOut(const char* text) { +void __fastcall DialogOut(const char* text, const char** textEx, long lines) { __asm { push 1; // DIALOGOUT_NORMAL flag xor eax, eax; - push eax; // ColorMsg - push eax; // DisplayMsg (unknown) mov al, byte ptr ds:[0x6AB718]; + push eax; // ColorMsg + push 0; // DisplayMsg (unknown) push eax; // ColorIndex + mov eax, ecx; // DisplayText push 116; // y mov ecx, 192; // x - mov eax, text; // DisplayText - xor ebx, ebx; - xor edx, edx; + mov ebx, lines; // count text lines 0-5 call dialog_out_; } } -long __fastcall DialogOutEx(const char* text, const char** textEx, long count, long flags) { +long __fastcall DialogOutEx(const char* text, const char** textEx, long lines, long flags, long colors) { __asm { - mov eax, 145; // Color index + xor eax, eax; + mov ebx, colors;// Color index + mov al, bh; push flags; // flag + and ebx, 0xFF push eax; // ColorMsg2 push 0; // DisplayMsg (unknown) - push eax; // ColorMsg1 mov eax, ecx; // DisplayText first line - push 116; // y + push ebx; // ColorMsg1 mov ecx, 192; // x - mov ebx, count; // count text lines 0-2 - call dialog_out_; // edx - DisplayText second/third line + push 116; // y + mov ebx, lines; // count text lines 0-5 + call dialog_out_; // edx - DisplayText second and later lines } } diff --git a/sfall/FalloutEngine.h b/sfall/FalloutEngine.h index 0dde5ad8..f813eef4 100644 --- a/sfall/FalloutEngine.h +++ b/sfall/FalloutEngine.h @@ -1186,9 +1186,9 @@ long __fastcall CreateWindowFunc(const char* winName, DWORD x, DWORD y, DWORD wi // creates a button long __stdcall WinRegisterButton(DWORD winRef, long xPos, long yPos, long width, long height, long hoverOn, long hoverOff, long buttonDown, long buttonUp, BYTE* pictureUp, BYTE* pictureDown, long arg12, long buttonType); -void __stdcall DialogOut(const char* text); +void __fastcall DialogOut(const char* text, const char** textEx, long lines); -long __fastcall DialogOutEx(const char* text, const char** textEx, long count, long flags); +long __fastcall DialogOutEx(const char* text, const char** textEx, long lines, long flags, long colors); void __fastcall WindowDisplayBuf(long x, long width, long y, long height, void* data, long noTrans); diff --git a/sfall/ScriptExtender.cpp b/sfall/ScriptExtender.cpp index 8faab9d8..84521e26 100644 --- a/sfall/ScriptExtender.cpp +++ b/sfall/ScriptExtender.cpp @@ -417,11 +417,12 @@ static const SfallOpcodeMetadata opcodeMetaArray[] = { {sf_add_g_timer_event, "add_g_timer_event", {DATATYPE_MASK_INT, DATATYPE_MASK_INT}}, {sf_add_trait, "add_trait", {DATATYPE_MASK_INT}}, {sf_create_win, "create_win", {DATATYPE_MASK_STR, DATATYPE_MASK_INT, DATATYPE_MASK_INT, DATATYPE_MASK_INT, DATATYPE_MASK_INT, DATATYPE_MASK_INT}}, - {sf_dialog_box, "dialog_box", {DATATYPE_MASK_STR, DATATYPE_MASK_STR, DATATYPE_MASK_STR}}, {sf_draw_image, "draw_image", {DATATYPE_MASK_INT | DATATYPE_MASK_STR, DATATYPE_MASK_INT, DATATYPE_MASK_INT, DATATYPE_MASK_INT, DATATYPE_MASK_INT}}, {sf_draw_image_scaled, "draw_image_scaled", {DATATYPE_MASK_INT | DATATYPE_MASK_STR, DATATYPE_MASK_INT, DATATYPE_MASK_INT, DATATYPE_MASK_INT, DATATYPE_MASK_INT, DATATYPE_MASK_INT}}, + {sf_get_window_attribute, "get_window_attribute", {DATATYPE_MASK_INT, DATATYPE_MASK_INT}}, {sf_hide_window, "hide_window", {DATATYPE_MASK_STR}}, {sf_inventory_redraw, "inventory_redraw", {DATATYPE_MASK_INT}}, + {sf_message_box, "message_box", {DATATYPE_MASK_STR, DATATYPE_MASK_INT, DATATYPE_MASK_INT, DATATYPE_MASK_INT}}, {sf_remove_timer_event, "remove_timer_event", {DATATYPE_MASK_INT}}, {sf_set_cursor_mode, "set_cursor_mode", {DATATYPE_MASK_INT}}, {sf_set_flags, "set_flags", {DATATYPE_MASK_VALID_OBJ, DATATYPE_MASK_INT}}, diff --git a/sfall/ScriptOps/InterfaceOp.hpp b/sfall/ScriptOps/InterfaceOp.hpp index a2760183..56cb47ee 100644 --- a/sfall/ScriptOps/InterfaceOp.hpp +++ b/sfall/ScriptOps/InterfaceOp.hpp @@ -168,6 +168,20 @@ static void __declspec(naked) resume_game() { } } +// copy and split +static void _stdcall SplitToBuffer(const char* str, const char** str_ptr, long &lines) { + size_t i = 0; + do { + if (str[i] == '\n' && lines < 4) { + gTextBuffer[i] = '\0'; + str_ptr[lines++] = &gTextBuffer[++i]; + } else { + gTextBuffer[i] = str[i++]; + } + } while (str[i]); + gTextBuffer[i] = '\0'; +} + static void _stdcall create_message_window2() { const ScriptValue &strArg = opHandler.arg(0); if (strArg.isString()) { @@ -177,8 +191,12 @@ static void _stdcall create_message_window2() { const char* str = strArg.strValue(); if (!str || str[0] == 0) return; + long lines = 0; + const char* str_ptr[4]; + SplitToBuffer(str, str_ptr, lines); + dialogShow = true; - DialogOut(str); + DialogOut(gTextBuffer, str_ptr, lines); dialogShow = false; } else { OpcodeInvalidArgs("create_message_window"); @@ -189,23 +207,28 @@ static void __declspec(naked) create_message_window() { _WRAP_OPCODE(create_message_window2, 1, 0) } -static void sf_dialog_box() { - const char* str = opHandler.arg(0).strValue(); - if (!str || str[0] == 0) return; +static void sf_message_box() { + static u_short dialogShowCount = 0; - const char* str2[2]; long lines = 0; - if (opHandler.numArgs() > 1) { - ++lines; - str2[0] = opHandler.arg(1).strValue(); - } + const char* str_ptr[4]; + SplitToBuffer(opHandler.arg(0).asString(), str_ptr, lines); + + long colors = 0x9191, flags = DIALOGOUT_NORMAL | DIALOGOUT_YESNO; + if (opHandler.numArgs() > 1 && opHandler.arg(1).rawValue() != -1) flags = opHandler.arg(1).rawValue(); if (opHandler.numArgs() > 2) { - ++lines; - str2[1] = opHandler.arg(2).strValue(); + colors &= 0xFF00; + colors |= (opHandler.arg(2).rawValue() & 0xFF); } + if (opHandler.numArgs() > 3) { + colors &= 0xFF; + colors |= (opHandler.arg(3).rawValue() & 0xFF) << 8; + } + dialogShowCount++; *(DWORD*)_script_engine_running = 0; - long result = DialogOutEx(str, str2, lines, DIALOGOUT_NORMAL | DIALOGOUT_YESNO); - *(DWORD*)_script_engine_running = 1; + long result = DialogOutEx(gTextBuffer, str_ptr, lines, flags, colors); + if (--dialogShowCount == 0) *(DWORD*)_script_engine_running = 1; + opHandler.setReturn(result); } @@ -715,43 +738,33 @@ static void sf_unwield_slot() { } void sf_get_window_attribute() { - const ScriptValue &winArg = opHandler.arg(0); - if (winArg.isInt()) { - long attr = 0; - if (opHandler.numArgs() > 1) { - const ScriptValue &attrArg = opHandler.arg(1); - if (!attrArg.isInt()) goto invalidArgs; - attr = attrArg.rawValue(); - } - WINinfo* win = GetUIWindow(winArg.rawValue()); - if (win == nullptr) { - if (attr != 0) { - opHandler.printOpcodeError("get_window_attribute() - failed to get the interface window."); - opHandler.setReturn(-1); - } - return; - } - if ((long)win == -1) { - opHandler.printOpcodeError("get_window_attribute() - invalid window type number."); + long attr = 0; + if (opHandler.numArgs() > 1) attr = opHandler.arg(1).rawValue(); + + WINinfo* win = GetUIWindow(opHandler.arg(0).rawValue()); + if (win == nullptr) { + if (attr != 0) { + opHandler.printOpcodeError("get_window_attribute() - failed to get the interface window."); opHandler.setReturn(-1); - return; } - long result = 0; - switch (attr) { - case 0: // check if window exists - result = 1; - break; - case 1: // x - result = win->wRect.left; - break; - case 2: // y - result = win->wRect.top; - break; - } - opHandler.setReturn(result); - } else { -invalidArgs: - OpcodeInvalidArgs("get_window_attribute"); - opHandler.setReturn(0); + return; } + if ((long)win == -1) { + opHandler.printOpcodeError("get_window_attribute() - invalid window type number."); + opHandler.setReturn(-1); + return; + } + long result = 0; + switch (attr) { + case 0: // check if window exists + result = 1; + break; + case 1: // x + result = win->wRect.left; + break; + case 2: // y + result = win->wRect.top; + break; + } + opHandler.setReturn(result); } diff --git a/sfall/ScriptOps/MetaruleOp.hpp b/sfall/ScriptOps/MetaruleOp.hpp index a216bbcb..09dc3e3e 100644 --- a/sfall/ScriptOps/MetaruleOp.hpp +++ b/sfall/ScriptOps/MetaruleOp.hpp @@ -126,7 +126,6 @@ static const SfallMetarule metaruleArray[] = { {"create_win", sf_create_win, 5, 6}, {"critter_inven_obj2", sf_critter_inven_obj2, 2, 2}, {"dialog_obj", sf_get_dialog_object, 0, 0}, - {"dialog_box", sf_dialog_box, 1, 3}, {"display_stats", sf_display_stats, 0, 0}, // refresh {"draw_image", sf_draw_image, 1, 5}, {"draw_image_scaled", sf_draw_image_scaled, 1, 6}, @@ -152,6 +151,7 @@ static const SfallMetarule metaruleArray[] = { {"item_weight", sf_item_weight, 1, 1}, {"lock_is_jammed", sf_lock_is_jammed, 1, 1}, {"loot_obj", sf_get_loot_object, 0, 0}, + {"message_box", sf_message_box, 1, 4}, {"metarule_exist", sf_metarule_exist, 1, 1}, {"npc_engine_level_up", sf_npc_engine_level_up, 1, 1}, {"obj_under_cursor", sf_obj_under_cursor, 2, 2},