From c0fcbecd63fa6d8648a2b583a48a5fbd8f30ac1b Mon Sep 17 00:00:00 2001 From: Sokomine Date: Wed, 1 Jan 2025 22:37:59 +0100 Subject: [PATCH] added functions for updating and importing dialogs and options for future ink import --- functions_dialogs.lua | 243 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) diff --git a/functions_dialogs.lua b/functions_dialogs.lua index c408aec..ff3919f 100644 --- a/functions_dialogs.lua +++ b/functions_dialogs.lua @@ -11,6 +11,10 @@ -- Helpers --### +yl_speak_up.string_starts_with = function(str, starts_with) + return (string.sub(str, 1, string.len(starts_with)) == starts_with) +end + yl_speak_up.get_number_from_id = function(any_id) if(not(any_id) or any_id == "d_got_item" or any_id == "d_end" or any_id == "d_dynamic") then return "0" @@ -97,6 +101,50 @@ yl_speak_up.add_new_dialog = function(dialog, pname, next_id, dialog_text) return future_d_id end + +-- update existing or create a new dialog named d_name with d_text +-- (useful for import from ink and likewise functionality) +yl_speak_up.update_dialog = function(log, dialog, dialog_name, dialog_text) + -- does a dialog with name d_name already exist? + local d_id = yl_speak_up.d_name_to_d_id(dialog, dialog_name) + -- name the thing for logging purposes + local log_str = "Dialog "..tostring(d_id) + if(dialog_name and dialog_name ~= d_id) then + log_str = " ["..tostring(dialog_name).."]: " + end + if(not(d_id)) then + -- pname is nil - thus no logging and no adding of a back to start option + -- next_id is also nil - so just add a new dialog + d_id = yl_speak_up.add_new_dialog(dialog, nil, nil, dialog_text) + if(not(d_id)) then + -- the creation may have failed (i.e. dialog not beeing a dialog, + -- or too many dialogs in dialog already) + table.insert(log, log_str.."FAILED to create new dialog.") + return nil + end + table.insert(log, log_str.."Successfully created dialog.") + + elseif(dialog.n_dialogs[d_id].d_text ~= dialog_text) then + -- else update the text + table.insert(log, log_str.."Changed dialog text from \"".. + tostring(dialog.n_dialogs[d_id].d_text).."\" to \""..tostring(dialog_text).."\".") + -- actually change the dialog text + dialog.n_dialogs[d_id].d_text = dialog_text + end + + -- set d_name if it differs from d_id + if(d_id ~= dialog_name + and (not(dialog.n_dialogs[d_id].d_name) + or(dialog.n_dialogs[d_id].d_name ~= dialog_name))) then + table.insert(log, log_str.."Changed dialog name from \"".. + tostring(dialog.n_dialogs[d_id].d_name).."\" to \""..tostring(dialog_name).."\".") + -- actually change the dialog name + dialog.n_dialogs[d_id].d_name = dialog_name + end + return d_id +end + + -- add a new option/answer to dialog d_id with option_text (or default "") -- option_text (optional) the text that shall be shown as option/answer -- target_dialog (optional) the target dialog where the player will end up when choosing @@ -227,6 +275,198 @@ yl_speak_up.add_new_option = function(dialog, pname, next_id, d_id, option_text, end +-- update existing or create a new option named option_name for dialog dialog_name +-- If option_name starts with.. +-- new_ create a new option (discard the rest of option_name) +-- automaticly_ set o_autoanswer +-- randomly_ set o_random +-- grey_out_ set o_text_when_prerequisites_not_met +-- ..and take what remains as option_name. +-- (useful for import from ink and likewise functionality) +-- +-- TODO: these notes need to be taken care of in the calling function +-- Note: The calling function may need to adjust o_sort according to its needs. +-- Note: The calling function also needs to take care to set *all* dialog options to +-- *randomly selected* if at least *one* is set to this value. This cannot be done +-- here as the other dialog options may not even be definied here yet. +-- Note: Preconditions, actions and effects are not handled here (apart from the "dialog" +-- effect/result for the redirection to the target dialog) +yl_speak_up.update_dialog_option = function(log, dialog, dialog_name, option_name, option_text, target_dialog, + alternate_text, visit_only_once) + -- does the dialog we want to add to exist? + local d_id = yl_speak_up.d_name_to_d_id(dialog, dialog_name) + if(not(d_id)) then + -- the dialog does not exist - we cannot add an option to a nonexistant dialog + return nil + end + -- name the thing for logging purposes + local log_str = "Dialog "..tostring(d_id) + if(dialog_name and dialog_name ~= d_id) then + log_str = " ["..tostring(dialog_name).."], option <"..tostring(option_name)..">: " + end + + -- translate the name of the target_dialog if needed + if(target_dialog and not(yl_speak_up.is_special_dialog(target_dialog))) then + target_dialog = yl_speak_up.d_name_to_d_id(dialog, target_dialog) + end + -- TODO: dialogs d_got_item and d_trade are special + + local o_id = option_name + local mode = 0 + local text_when_prerequisites_not_met = "" + if( o_id and syl_speak_up.string_starts_with(o_id, "new_")) then + -- we are asked to create a *new* option + o_id = nil + elseif(o_id and yl_speak_up.string_starts_with(o_id, "automaticly_")) then + -- this option will be automaticly selected if its preconditions are true + mode = 1 + option_name = string.sub(o_id, string.find(o_id, "automaticly_") + 1) + o_id = option_name + elseif(o_id and yl_speak_up.string_starts_with(o_id, "randomly_")) then + -- this option will be randomly selected if its preconditions are true; + -- (that means all other options of this dialog will have to be randomly as well; + -- something which cannot be done here as there is no guarantee that all options + -- *exist* at this point) + mode = 2 + option_name = string.sub(o_id, string.find(o_id, "randomly_") + 1) + o_id = option_name + elseif(o_id and yl_speak_up.string_starts_with(o_id, "grey_out_")) then + -- this sets o_text_when_prerequisites_not_met + mode = 3 + option_name = string.sub(o_id, string.find(o_id, "grey_out_") + 1) + o_id = option_name + -- in this case we don't want to change the old text - just the greyed out one + -- (we keep option_text in case the option needs to be created) + text_when_prerequisites_not_met = option_text + end + + + -- if the option does not exist: create it + if( not(dialog.n_dialogs[d_id].d_options) + or not(o_id) or o_id == "" + or not(dialog.n_dialogs[d_id].d_options[o_id])) then + local next_id = nil + -- get the id part (number) from o_id - because we may be creating a new option here - + -- but said option may have a diffrent *name* than what a new option would get by + -- default + if(o_id) then + next_id = string.sub(o_id, 3) + if(next_id == "" or not(tonumber(next_id))) then + next_id = nil + table.insert(log, log_str.."FAILED to create new option.") + end + end + -- pname is nil - thus no logging here + o_id = yl_speak_up.add_new_option(dialog, nil, next_id, d_id, option_text, target_dialog) + if(not(o_id)) then + return nil + end + table.insert(log, log_str.."Successfully created new option \""..tostring(o_id).."\".") + + -- do not change the option_text when we want to change the text of the grey_out_ option text + elseif(dialog.n_dialogs[d_id].d_options[o_id].o_text_when_prerequisites_met ~= option_text + and mode ~= 3) then + -- else update the text + table.insert(log, log_str.."Changed option text from \"".. + tostring(dialog.n_dialogs[d_id].d_text.d_options[o_id]).. + "\" to \""..tostring(option_text).."\" for option \""..tostring(o_id).."\".") + -- actually update the text + dialog.n_dialogs[d_id].d_options[o_id].o_text_when_prerequisites_met = option_text + end + + -- abbreviate that + local o_data = dialog.n_dialogs[d_id].d_options[o_id] + + local r_found = false + -- the target_dialog may have been changed + for r_id, r in pairs(o_data.o_results or {}) do + -- we found the right result/effect that holds the (current) target_dialog + if(r and r.r_type and r.r_type == "dialog") then + if(not(r.r_value) or r.r_value ~= target_dialog) then + table.insert(log, log_str.."Changed target dialog from \"".. + tostring(r.r_value).."\" to \""..tostring(target_dialog).. + "\" for option \""..tostring(o_id).."\".") + -- actually change the target dialog + r_found = true + r.r_value = target_dialog + end + -- the alternate_text may have been changed + if(r.alternate_text ~= alternate_text) then + table.insert(log, log_str.."Changed alternate text from \"".. + tostring(r.r_alternate_text).."\" to \""..tostring(alternate_text).. + "\" for option \""..tostring(o_id).."\".") + r.alternate_text = alternate_text + end + end + end + -- for some reason the effect pointing to the target dialog got lost! + if(not(r_found)) then + -- create the result/effect that points to the target_dialog + local r_id = yl_speak_up.add_new_result(dialog, d_id, o_id) + if(r_id) then + o_data.o_results[r_id].r_type = "dialog" + o_data.o_results[r_id].r_value = target_dialog + o_data.o_results[r_id].alternate_text = alternate_text + table.insert(log, log_str.."Set target dialog to "..tostring(target_dialog).. + " for option \""..tostring(o_id).."\".") + end + end + + -- is this option selected automaticly if all preconditions are met? + if( mode == 1 and not(o_data.o_autoanswer)) then + o_data.o_autoanswer = 1 + table.insert(log, log_str.."Changed option \""..tostring(o_id).."\" to AUTOMATICLY SELECTED.") + -- actually update the text + -- is this option selected randomly? + elseif(mode == 2 and not(o_data.o_random)) then + o_data.o_random = 1 + table.insert(log, log_str.."Changed option \""..tostring(o_id).."\" to RANDOMLY SELECTED.") + -- grey out the given text and show that as answer when preconditions not met? + elseif(mode == 3) then + if((not(o_dat.o_text_when_prerequisites_not_met + or o_data.o_text_when_prerequisites_not_met ~= text_when_prerequisites_not_met))) then + + table.insert(log, log_str.."Changed option text WHEN PREREQUISITES NOT MET from \"".. + tostring(o_data.o_text_when_prerequisites_not_met).. + "\" to \""..tostring(text_when_prerequisites_not_met).. + "\" for option \""..tostring(o_id).."\".") + -- actually change it + o_data.o_text_when_prerequisites_not_met = text_when_prerequisites_not_met + end + -- make sure this text is really shown - and greyed out + -- (resetting this can only happen through editing the NPC directly; not through import) + o_data.o_hide_when_prerequisites_not_met = "false" + o_data.o_grey_when_prerequisites_not_met = "true" + else + -- mode is 0 - that means everything is normal for this option + if(o_data.o_autoanswer) then + o_data.o_autoanswer = nil + table.insert(log, log_str.."Removed AUTOMATICLY SELECTED from option \"".. + tostring(o_id).."\".") + end + if(o_data.o_random) then + o_data.o_random = nil + table.insert(log, log_str.."Removed RANDOMLY SELECTED from option \"".. + tostring(o_id).."\".") + end + -- set visibility back to default + o_data.o_hide_when_prerequisites_not_met = "false" + o_data.o_grey_when_prerequisites_not_met = "false" + end + + -- the visit_only_once option is handled without logging as it might create too many + -- entries in the log without adding any helpful information + if(visit_only_once + and (not(o_data.sb_v.o_visit_only_once) or o_data.o_visit_only_once ~= 1)) then + o_data.sb_v.o_visit_only_once = 1 + elseif(not(visit_only_once) + and o_data.sb_v.o_visit_only_once and o_data.o_visit_only_once == 1) then + o_data.sb_v.o_visit_only_once = nil + end + return o_id +end + + -- add a new result to option o_id of dialog d_id yl_speak_up.add_new_result = function(dialog, d_id, o_id) if(not(dialog) or not(dialog.n_dialogs) or not(dialog.n_dialogs[d_id]) @@ -239,8 +479,10 @@ yl_speak_up.add_new_result = function(dialog, d_id, o_id) dialog.n_dialogs[d_id].d_options[o_id].o_results = {} end dialog.n_dialogs[d_id].d_options[o_id].o_results[future_r_id] = {} + dialog.n_dialogs[d_id].d_options[o_id].o_results[future_r_id].r_id = future_r_id return future_r_id end +-- TODO: we need yl_speak_up.update_dialog_option_result as well -- this is useful for result types that can exist only once per option @@ -366,6 +608,7 @@ yl_speak_up.d_name_to_d_id = function(dialog, d_name) return k end end + return nil end