diff --git a/Engine/ac/dialog.cpp b/Engine/ac/dialog.cpp index 73c63dbd772..e9851655a72 100644 --- a/Engine/ac/dialog.cpp +++ b/Engine/ac/dialog.cpp @@ -1462,23 +1462,25 @@ bool is_in_dialog() } // NOTE: this is ugly, but I could not come to a better solution at the time... -void set_dialog_option_result(int dlgopt_result) +void set_dialog_result_goto(int dlgnum) { assert(dialogExec && dialogScriptsInst); - if (!dialogExec || !dialogScriptsInst) - return; + if (dialogScriptsInst) + dialogScriptsInst->returnValue = dlgnum; +} - dialogScriptsInst->returnValue = dlgopt_result; +void set_dialog_result_stop() +{ + assert(dialogExec && dialogScriptsInst); + if (dialogScriptsInst) + dialogScriptsInst->returnValue = RUN_DIALOG_STOP_DIALOG; } -bool handle_state_change_in_dialog_request(const char *apiname, int dlgreq_retval, bool expect_dialog_request) +bool handle_state_change_in_dialog_request(const char *apiname, int dlgreq_retval) { // Test if we are inside a dialog state AND dialog_request callback if ((dialogExec == nullptr) || (play.stop_dialog_at_end == DIALOG_NONE)) { - // Some command may only work inside dialog_request (?) - if (expect_dialog_request) - debug_script_warn("%s: not in a dialog_request(), ignored", apiname); return false; // not handled, process command as normal } diff --git a/Engine/ac/dialog.h b/Engine/ac/dialog.h index 7e48ca0d3f1..84f7f9f5208 100644 --- a/Engine/ac/dialog.h +++ b/Engine/ac/dialog.h @@ -32,8 +32,10 @@ void Dialog_Start(ScriptDialog *sd); void do_conversation(int dlgnum); // Tells if the game is currently running a dialog bool is_in_dialog(); -// Assigns a return value to pass to the dialog state after current option's script have finished executing -void set_dialog_option_result(int dlgopt_result); +// Commands dialog executor to goto a different dialog topic after current option's script have finished executing +void set_dialog_result_goto(int dlgnum); +// Commands dialog executor to stop a dialog after current option's script have finished executing +void set_dialog_result_stop(); // Displays dialog options, and returns the chosen number, or CHOSE_TEXTPARSER if parser input was activated int show_dialog_options(int dlgnum, bool runGameLoopsInBackground); // Handles a dialog option, optionally "sais" its text, optionally run corresponding dialog script's entry @@ -41,7 +43,7 @@ int run_dialog_option(int dlgnum, int dialog_choice, int sayChosenOption, bool r // Handles a game-state changing command (such as StartDialog) inside "dialog_request" callback. // Returns whether the change was handled in "dialog's way", and further processing is not necessary. // Otherwise should process the command as normal. -bool handle_state_change_in_dialog_request(const char *apiname, int dlgreq_retval, bool expect_dialog_request = false); +bool handle_state_change_in_dialog_request(const char *apiname, int dlgreq_retval); extern std::vector scrDialog; extern std::vector dialog; diff --git a/Engine/ac/global_dialog.cpp b/Engine/ac/global_dialog.cpp index 9d28c9d8098..095bebcf190 100644 --- a/Engine/ac/global_dialog.cpp +++ b/Engine/ac/global_dialog.cpp @@ -45,7 +45,11 @@ void RunDialog(int tum) void StopDialog() { - handle_state_change_in_dialog_request("StopDialog", DIALOG_STOP, true); + if (handle_state_change_in_dialog_request("StopDialog", DIALOG_STOP)) + return; // handled + + if (inside_script) + get_executingscript()->QueueAction(PostScriptAction(ePSAStopDialog, 0, "StopDialog")); } void SetDialogOption(int dlg, int opt, int onoroff, bool dlg_script) diff --git a/Engine/script/executingscript.cpp b/Engine/script/executingscript.cpp index ba3d4f3def6..b8a482b9d30 100644 --- a/Engine/script/executingscript.cpp +++ b/Engine/script/executingscript.cpp @@ -38,15 +38,26 @@ void ExecutingScript::QueueAction(PostScriptAction &&act) // been queued, don't allow a second thing to be queued switch (prev_act.Type) { + // A number of scheduled commands prevent ANY other scheduled command to be added case ePSANewRoom: case ePSARestoreGame: case ePSARestoreGameDialog: case ePSARunAGSGame: case ePSARestartGame: - quitprintf("!%s: Cannot run this command, since there was a %s command already queued to run in \"%s\", line %d", + debug_script_warn("!%s: Cannot run this command, since there was a %s command already queued to run in \"%s\", line %d", act.Name.GetCStr(), prev_act.Name.GetCStr(), prev_act.Position.Section.GetCStr(), prev_act.Position.Line); - break; + return; + // Dialog-state changing commands are mutually exclusive + case ePSARunDialog: + case ePSAStopDialog: + if (act.Type == ePSARunDialog || act.Type == ePSAStopDialog) + { + debug_script_warn("!%s: Cannot run this command, since there was a %s command already queued to run in \"%s\", line %d", + act.Name.GetCStr(), prev_act.Name.GetCStr(), + prev_act.Position.Section.GetCStr(), prev_act.Position.Line); + return; + } default: break; } diff --git a/Engine/script/executingscript.h b/Engine/script/executingscript.h index e9618568d24..fd6ccec80f6 100644 --- a/Engine/script/executingscript.h +++ b/Engine/script/executingscript.h @@ -62,6 +62,7 @@ struct QueuedScript QueuedScript() = default; }; +// Actions that can be scheduled for until the current script completes enum PostScriptActionType { ePSAUndefined, @@ -73,7 +74,8 @@ enum PostScriptActionType ePSARunDialog, ePSARestartGame, ePSASaveGame, - ePSASaveGameDialog + ePSASaveGameDialog, + ePSAStopDialog }; struct PostScriptAction diff --git a/Engine/script/script.cpp b/Engine/script/script.cpp index 64a362d7a25..48d3c6087da 100644 --- a/Engine/script/script.cpp +++ b/Engine/script/script.cpp @@ -689,13 +689,16 @@ void post_script_cleanup() case ePSARunDialog: if (is_in_dialog()) { - set_dialog_option_result(thisData); + set_dialog_result_goto(thisData); } else { do_conversation(thisData); } break; + case ePSAStopDialog: + set_dialog_result_stop(); + break; case ePSARestartGame: cancel_all_scripts(); restart_game();