diff --git a/edit_mode_apply_changes.lua b/edit_mode_apply_changes.lua new file mode 100644 index 0000000..4bd6747 --- /dev/null +++ b/edit_mode_apply_changes.lua @@ -0,0 +1,421 @@ + + +-- helper function for yl_speak_up.edit_mode_apply_changes; +-- makes sure the new dialog (and a result/effect "dialog" for each option) exist +yl_speak_up.prepare_new_dialog_for_option = function(dialog, pname, n_id, d_id, o_id,target_dialog,o_results) + -- is there a result/effect of the type "dialog" already? else use a fallback + local result = {r_value = "-default-"} + if(o_results) then + for kr, vr in pairs(o_results) do + if( vr.r_type == "dialog" ) then + result = vr + end + end + end + -- this may also point to a new dialog + if(target_dialog == yl_speak_up.text_new_dialog_id) then + -- create a new dialog and show it as new target dialog - but do not display + -- this dialog directly (the player may follow the -> button) + target_dialog = yl_speak_up.add_new_dialog(dialog, pname, nil) + elseif( result.r_value and result.r_value == target_dialog) then + -- no problem - the right dialog is set already + return target_dialog + end + -- store that a new option has been added to this dialog + table.insert(yl_speak_up.npc_was_changed[ n_id ], + "Dialog "..d_id..": The target dialog for option ".. + tostring(o_id).." was changed from ".. + tostring(result.r_value or "-default-").." to ".. + tostring(target_dialog)..".") + -- does the result/effect of type "dialog" exist already? then we're done + if(result.r_type and result.r_type == "dialog") then + -- actually change the target dialog + result.r_value = target_dialog + return target_dialog + end + -- create a new result (first the id, then the actual result) + local future_r_id = yl_speak_up.add_new_result(dialog, d_id, o_id) + -- actually store the new result + dialog.n_dialogs[d_id].d_options[o_id].o_results[future_r_id] = { + r_id = future_r_id, + r_type = "dialog", + r_value = target_dialog} + return target_dialog +end + + +-- helper function for formspec "yl_speak_up:talk" *and* formspec "yl_speak_up:edit_option_dialog" +-- when a parameter was changed in edit mode; +-- this is called when the player is in edit_mode (editing the NPC); +-- the function checks if the player has changed any parameters +-- Parameters: +-- pname player name +-- fields the fields returned from the formspec +-- Returns: +-- result table with information about what was added +-- (for now, only result.show_next_option is of intrest in the option edit menu) +yl_speak_up.edit_mode_apply_changes = function(pname, fields) + local n_id = yl_speak_up.edit_mode[pname] + local d_id = yl_speak_up.speak_to[pname].d_id + local dialog = yl_speak_up.speak_to[pname].dialog + + -- this way we can store the actual changes and present them to the player for saving + if(not(yl_speak_up.npc_was_changed[ n_id ])) then + yl_speak_up.npc_was_changed[ n_id ] = {} + end + + + -- nothing to do if that dialog does not exist + if(not(d_id) or not(dialog.n_dialogs) or not(dialog.n_dialogs[ d_id ])) then + return + end + + -- allow owner to mute/unmute npc (would be bad if players can already see what is going + -- to happen while the owner creates a long quest) + -- mute/unmute gets logged in the function and does not need extra log entries + local obj = yl_speak_up.speak_to[pname].obj + if(fields.mute_npc and obj) then + yl_speak_up.set_muted(pname, obj, true) + elseif(fields.un_mute_npc and obj) then + yl_speak_up.set_muted(pname, obj, false) + end + + + -- new options etc. may be added; store these IDs so that we can switch to the right target + local result = {} + + -- make this the first dialog shown when starting a conversation + if(fields.make_first_option) then + -- check which dialog(s) previously had the highest priority and change thsoe + for k, v in pairs(dialog.n_dialogs) do + if(v and v.d_sort and (v.d_sort=="0" or v.d_sort==0)) then + -- try to derive a sensible future sort priority from the key: + -- here we make use of the d_ pattern; but even if that fails to yield + -- a number, the sort function will later be able to deal with it anyway + local new_priority = string.sub(k, 3) + dialog.n_dialogs[ k ].d_sort = new_priority + end + end + -- actually make this the chat with the highest priority + dialog.n_dialogs[ d_id ].d_sort = "0" + -- this is not immediately saved, even though the changes to the previous dialog with + -- the highest priority cannot be automaticly undone (but as long as it is not saved, + -- it really does not matter; and when saving, the player has to take some care) + table.insert(yl_speak_up.npc_was_changed[ n_id ], + "Dialog "..d_id..": Turned into new start dialog.") + end + + -- detect changes to d_text: text of the dialog (what the npc is saying) + -- (only happens in dialog edit menu) + if(fields.d_text and dialog.n_dialogs[ d_id ].d_text ~= fields.d_text) then + -- store that there have been changes to this npc + table.insert(yl_speak_up.npc_was_changed[ n_id ], + "Dialog "..d_id..": d_text (what the NPC says) was changed from \"".. + tostring( dialog.n_dialogs[ d_id ].d_text).. + "\" to \""..tostring(fields.d_text).."\".") + -- actually change the text - but do not save to disk yet + dialog.n_dialogs[ d_id ].d_text = fields.d_text + end + + -- add a new option/answer + if(fields[ "add_option"]) then + -- count the options/answers so that the limit is not exceeded + local anz_options = 0 + local future_o_id = "o_" .. yl_speak_up.find_next_id(dialog.n_dialogs[d_id].d_options) + if dialog.n_dialogs[d_id].d_options == nil then + dialog.n_dialogs[d_id].d_options = {} + else + local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs[d_id].d_options, "o_sort") + anz_options = #sorted_list + end + -- we don't want an infinite amount of answers per dialog + if(anz_options >= yl_speak_up.max_number_of_options_per_dialog) then + -- this is already checked earlier on and the button only shown if + -- options can be added; so this can reamin a chat message + minetest.chat_send_player(pname, "Sorry. Only ".. + tostring(yl_speak_up.max_number_of_options_per_dialog).. + " options/answers are allowed per dialog.") + fields.add_option = nil + else + dialog.n_dialogs[d_id].d_options[future_o_id] = { + o_id = future_o_id, + o_hide_when_prerequisites_not_met = "false", + o_grey_when_prerequisites_not_met = "false", + o_sort = -1, + o_text_when_prerequisites_not_met = "", + o_text_when_prerequisites_met = "", + } + -- necessary in order for it to work + local s = yl_speak_up.sanitize_sort(dialog.n_dialogs[d_id].d_options, yl_speak_up.speak_to[pname].o_sort) + dialog.n_dialogs[d_id].d_options[future_o_id].o_sort = s + table.insert(yl_speak_up.npc_was_changed[ n_id ], + "Dialog "..d_id..": Added new option/answer "..future_o_id..".") + -- if this is selected in the options edit menu, we want to move straight on to the new option + result["show_next_option"] = future_o_id + end + end + + if(fields[ "del_option"] and fields.o_id and dialog.n_dialogs[d_id].d_options[fields.o_id]) then + local o_id = fields.o_id + -- which dialog to show instead of the deleted one? + local next_o_id = o_id + local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs[d_id].d_options, "o_sort") + for i, o in ipairs(sorted_list) do + if(o == o_id and sorted_list[ i+1 ]) then + next_o_id = sorted_list[ i+1 ] + elseif(o == o_id and sorted_list[ i-1 ]) then + next_o_id = sorted_list[ i-1 ] + end + end + table.insert(yl_speak_up.npc_was_changed[ n_id ], + "Dialog "..d_id..": Option "..tostring(o_id).." deleted.") + -- actually delete the dialog + dialog.n_dialogs[d_id].d_options[o_id] = nil + -- the current dialog is deleted; we need to show another one + result["show_next_option"] = next_o_id + -- after deleting the entry, all previous/further changes to it are kind of unintresting + return result + end + + -- ignore entries to o_sort if they are not a number + if(fields[ "edit_option_o_sort"] + and tonumber(fields[ "edit_option_o_sort"]) + and fields.o_id and dialog.n_dialogs[d_id].d_options[fields.o_id]) then + local o_id = fields.o_id + local new_nr = tonumber(fields[ "edit_option_o_sort"]) + local old_nr = tonumber(dialog.n_dialogs[d_id].d_options[o_id].o_sort) + -- if the nr is -1 (do not show) then we are done already: nothing to do + if(old_nr == new_nr) then + -- -1: do not list as option/answer (but still store and keep it) + elseif(new_nr == -1 and old_nr ~= -1) then + dialog.n_dialogs[d_id].d_options[o_id].o_sort = "-1" + table.insert(yl_speak_up.npc_was_changed[ n_id ], + "Dialog "..d_id..": Option "..tostring(o_id).." was set to -1 (do not list).") + else + -- get the old sorted list + local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs[d_id].d_options, "o_sort") + -- negative numbers are not shown + local entries_shown_list = {} + for i, o in ipairs(sorted_list) do + local n = tonumber(dialog.n_dialogs[d_id].d_options[o].o_sort) + if(n and n > 0 and o ~= o_id) then + table.insert(entries_shown_list, o) + end + end + -- insert the entry at the new position and let lua do the job + table.insert(entries_shown_list, new_nr, o_id) + -- take the indices from that new list as new sort values and store them; + -- this has the side effect that duplicate entries get sorted out as well + for i, o in ipairs(entries_shown_list) do + dialog.n_dialogs[d_id].d_options[o].o_sort = tostring(i) + end + -- store that there was a cahnge + table.insert(yl_speak_up.npc_was_changed[ n_id ], + "Dialog "..d_id..": Option "..tostring(o_id).." was moved to position ".. + tostring(new_nr)..".") + end + end + + -- changes to options are not possible if there are none + if(dialog.n_dialogs[ d_id ].d_options) then + + -- detect changes to text_option_: text for option + for k, v in pairs(dialog.n_dialogs[ d_id ].d_options) do + if( fields[ "text_option_"..k ] + and fields[ "text_option_"..k ] ~= v.o_text_when_prerequisites_met ) then + -- store that there have been changes to this npc + table.insert(yl_speak_up.npc_was_changed[ n_id ], + "Dialog "..d_id..": The text for option "..tostring(k).. + " was changed from \""..tostring(v.o_text_when_prerequisites_met).. + "\" to \""..tostring(fields[ "text_option_"..k]).."\".") + -- actually change the text of the option + dialog.n_dialogs[ d_id ].d_options[ k ].o_text_when_prerequisites_met = fields[ "text_option_"..k ] + end + end + + -- detect changes to d_id_: target dialog for option + for k, v in pairs(dialog.n_dialogs[ d_id ].d_options) do + if(fields[ "d_id_"..k ]) then + local new_target_dialog = yl_speak_up.prepare_new_dialog_for_option( + dialog, pname, n_id, d_id, k, fields[ "d_id_"..k ], v.o_results) + if(new_target_dialog ~= fields[ "d_id_"..k ]) then + fields[ "d_id_"..k ] = new_target_dialog + -- in options edit menu: show this update + result["show_next_option"] = k + end + end + end + end + + -- add a new dialog; either via "+" button or "New dialog" in dialog dropdown menu + -- this has to be done after all the other changes because those (text changes etc.) still + -- apply to the *old* dialog + if(fields.show_new_dialog + or(fields["d_id"] and fields["d_id"] == yl_speak_up.text_new_dialog_id)) then + -- create the new dialog and make sure it gets shown + local d_id = yl_speak_up.add_new_dialog(dialog, pname, nil) + -- actually show the new dialog + fields["d_id"] = d_id + fields["show_new_dialog"] = nil + end + + -- delete one empty dialog + if(fields.delete_this_empty_dialog) then + local anz_options = 0 + -- we need to show a new dialog after this one was deleted + local new_dialog = d_id + local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs, "d_sort") + for i, k in ipairs(sorted_list) do + -- count the options of this dialog + if(k == d_id) then + if(dialog.n_dialogs[d_id].d_options) then + for o, w in pairs(dialog.n_dialogs[d_id].d_options) do + anz_options = anz_options + 1 + end + end + if(sorted_list[i+1]) then + new_dialog = sorted_list[i+1] + elseif(sorted_list[i-1]) then + new_dialog = sorted_list[i-1] + end + end + end + -- there needs to be one dialog left after deleting this one, + if(#sorted_list > 1 + -- this dialog isn't allowed to hold any more options/answers + and anz_options == 0 + -- we really found a new dialog to show + and new_dialog ~= d_id + -- and the text needs to be empty + and dialog.n_dialogs[ d_id ].d_text == "") then + -- actually delete this dialog + dialog.n_dialogs[ d_id ] = nil + -- ..and store it to disk + yl_speak_up.delete_dialog(n_id, d_id) + yl_speak_up.log_change(pname, n_id, + "Deleted dialog "..tostring(d_id)..".") + -- switch to another dialog (this one was deleted after all) + fields["d_id"] = new_dialog + fields["show_new_dialog"] = nil + else + -- deleting is only possible from the talk menu, and there the delete + -- button is only shown if the dialog can be deleted; so this can remain + -- a chat message + minetest.chat_send_player(pname, "Sorry. This dialog cannot be deleted (yet). ".. + "It is either the only dialog left or has a non-empty text or has at ".. + "least on remaining option/answer.") + end + end + + -- not in options edit menu? + local o_id = fields.o_id + if(not(o_id)) then + return result + end + + local d_option = dialog.n_dialogs[ d_id ].d_options[ o_id ] + -- change alternate text when preconditions are not met + -- (only happens in options edit menu) + if(fields.option_text_not_met and d_option + and d_option.o_text_when_prerequisites_not_met ~= fields.option_text_not_met) then + -- add change to changelog + table.insert(yl_speak_up.npc_was_changed[ n_id ], + "Dialog "..d_id..": The alternate text for option "..tostring(o_id).. + " was changed from \"".. + tostring(d_option.o_text_when_prerequisites_not_met).."\" to \"".. + tostring(fields.option_text_not_met).."\".") + -- actually change the text of the option + d_option.o_text_when_prerequisites_not_met = fields.option_text_not_met + end + + -- toggle autoselection/autoclick of an option + if(d_option and fields.option_autoanswer and fields.option_autoanswer ~= "") then + if(d_option.o_autoanswer and d_option.o_autoanswer == 1 + and fields.option_autoanswer == "by clicking on it") then + d_option.o_autoanswer = nil + table.insert(yl_speak_up.npc_was_changed[ n_id ], + "Dialog "..d_id..": The modus for option "..tostring(o_id).. + " was changed from \"automaticly\" to \"by clicking on it\".") + elseif((not(d_option.o_autoanswer) or d_option.o_autoanswer ~= 1) + and fields.option_autoanswer == "automaticly") then + d_option.o_autoanswer = 1 + table.insert(yl_speak_up.npc_was_changed[ n_id ], + "Dialog "..d_id..": The modus for option "..tostring(o_id).. + " was changed from \"by clicking on it\" to \"automaticly\".") + end + end + + -- handle hide/grey out/show alternate answer + -- (only happens in options edit menu) + if(fields.hide_or_grey_or_alternate_answer and d_option) then + if(fields.hide_or_grey_or_alternate_answer == "..hide this answer." + and d_option.o_hide_when_prerequisites_not_met ~= "true") then + d_option.o_hide_when_prerequisites_not_met = "true" + d_option.o_grey_when_prerequisites_not_met = "false" + table.insert(yl_speak_up.npc_was_changed[ n_id ], + "Dialog "..d_id..": If precondition for option "..tostring(o_id).. + " is not met, hide option/answer.") + -- make sure we show this options update next + result["show_next_option"] = o_id + elseif(fields.hide_or_grey_or_alternate_answer == "..grey out the following answer:" + and d_option.o_grey_when_prerequisites_not_met ~= "true") then + d_option.o_hide_when_prerequisites_not_met = "false" + d_option.o_grey_when_prerequisites_not_met = "true" + table.insert(yl_speak_up.npc_was_changed[ n_id ], + "Dialog "..d_id..": If precondition for option "..tostring(o_id).. + " is not met, grey out option/answer.") + result["show_next_option"] = o_id + elseif(fields.hide_or_grey_or_alternate_answer == "..display the following alternate answer:" + and (d_option.o_hide_when_prerequisites_not_met ~= "false" + or d_option.o_grey_when_prerequisites_not_met) ~= "false") then + d_option.o_hide_when_prerequisites_not_met = "false" + d_option.o_grey_when_prerequisites_not_met = "false" + table.insert(yl_speak_up.npc_was_changed[ n_id ], + "Dialog "..d_id..": If precondition for option "..tostring(o_id).. + " is not met, show alternate option/answer.") + result["show_next_option"] = o_id + end + end + + -- how many times can the player fail to execute the action successfully? + if(fields[ "timer_max_attempts_on_failure"]) then + local field_name = "timer_max_attempts_on_failure" + local timer_name = "timer_on_failure_"..tostring(d_id).."_"..tostring(o_id) + if(not(tonumber(fields[ field_name ]))) then + fields[ field_name ] = 0 + end + -- make sure the variable exists + if(yl_speak_up.add_time_based_variable(timer_name)) then + yl_speak_up.set_variable_metadata(timer_name, nil, "parameter", "max_attempts", + fields[ field_name ]) + end + end + -- ..and how long has the player to wait in order to try again? + if(fields[ "timer_max_seconds_on_failure"]) then + local field_name = "timer_max_seconds_on_failure" + local timer_name = "timer_on_failure_"..tostring(d_id).."_"..tostring(o_id) + if(not(tonumber(fields[ field_name ]))) then + fields[ field_name ] = 0 + end + -- make sure the variable exists + if(yl_speak_up.add_time_based_variable(timer_name)) then + yl_speak_up.set_variable_metadata(timer_name, nil, "parameter", "duration", + fields[ field_name ]) + end + end + if(fields[ "timer_max_seconds_on_success"]) then + local field_name = "timer_max_seconds_on_success" + local timer_name = "timer_on_success_"..tostring(d_id).."_"..tostring(o_id) + if(not(tonumber(fields[ field_name ]))) then + fields[ field_name ] = 0 + end + -- make sure the variable exists + if(yl_speak_up.add_time_based_variable(timer_name)) then + yl_speak_up.set_variable_metadata(timer_name, nil, "parameter", "duration", + fields[ field_name ]) + end + end + -- currently only contains result["show_new_option"] (which is needed for options edit menu) + return result +end +-- end of yl_speak_up.edit_mode_apply_changes diff --git a/functions.lua b/functions.lua index d75e00d..43cd0bb 100644 --- a/functions.lua +++ b/functions.lua @@ -342,428 +342,6 @@ yl_speak_up.sort_keys = function(t, simple) end --- helper function for yl_speak_up.edit_mode_apply_changes; --- makes sure the new dialog (and a result/effect "dialog" for each option) exist -yl_speak_up.prepare_new_dialog_for_option = function(dialog, pname, n_id, d_id, o_id,target_dialog,o_results) - -- is there a result/effect of the type "dialog" already? else use a fallback - local result = {r_value = "-default-"} - if(o_results) then - for kr, vr in pairs(o_results) do - if( vr.r_type == "dialog" ) then - result = vr - end - end - end - -- this may also point to a new dialog - if(target_dialog == yl_speak_up.text_new_dialog_id) then - -- create a new dialog and show it as new target dialog - but do not display - -- this dialog directly (the player may follow the -> button) - target_dialog = yl_speak_up.add_new_dialog(dialog, pname, nil) - elseif( result.r_value and result.r_value == target_dialog) then - -- no problem - the right dialog is set already - return target_dialog - end - -- store that a new option has been added to this dialog - table.insert(yl_speak_up.npc_was_changed[ n_id ], - "Dialog "..d_id..": The target dialog for option ".. - tostring(o_id).." was changed from ".. - tostring(result.r_value or "-default-").." to ".. - tostring(target_dialog)..".") - -- does the result/effect of type "dialog" exist already? then we're done - if(result.r_type and result.r_type == "dialog") then - -- actually change the target dialog - result.r_value = target_dialog - return target_dialog - end - -- create a new result (first the id, then the actual result) - local future_r_id = yl_speak_up.add_new_result(dialog, d_id, o_id) - -- actually store the new result - dialog.n_dialogs[d_id].d_options[o_id].o_results[future_r_id] = { - r_id = future_r_id, - r_type = "dialog", - r_value = target_dialog} - return target_dialog -end - - --- helper function for formspec "yl_speak_up:talk" *and* formspec "yl_speak_up:edit_option_dialog" --- when a parameter was changed in edit mode; --- this is called when the player is in edit_mode (editing the NPC); --- the function checks if the player has changed any parameters --- Parameters: --- pname player name --- fields the fields returned from the formspec --- Returns: --- result table with information about what was added --- (for now, only result.show_next_option is of intrest in the option edit menu) -yl_speak_up.edit_mode_apply_changes = function(pname, fields) - local n_id = yl_speak_up.edit_mode[pname] - local d_id = yl_speak_up.speak_to[pname].d_id - local dialog = yl_speak_up.speak_to[pname].dialog - - -- this way we can store the actual changes and present them to the player for saving - if(not(yl_speak_up.npc_was_changed[ n_id ])) then - yl_speak_up.npc_was_changed[ n_id ] = {} - end - - - -- nothing to do if that dialog does not exist - if(not(d_id) or not(dialog.n_dialogs) or not(dialog.n_dialogs[ d_id ])) then - return - end - - -- allow owner to mute/unmute npc (would be bad if players can already see what is going - -- to happen while the owner creates a long quest) - -- mute/unmute gets logged in the function and does not need extra log entries - local obj = yl_speak_up.speak_to[pname].obj - if(fields.mute_npc and obj) then - yl_speak_up.set_muted(pname, obj, true) - elseif(fields.un_mute_npc and obj) then - yl_speak_up.set_muted(pname, obj, false) - end - - - -- new options etc. may be added; store these IDs so that we can switch to the right target - local result = {} - - -- make this the first dialog shown when starting a conversation - if(fields.make_first_option) then - -- check which dialog(s) previously had the highest priority and change thsoe - for k, v in pairs(dialog.n_dialogs) do - if(v and v.d_sort and (v.d_sort=="0" or v.d_sort==0)) then - -- try to derive a sensible future sort priority from the key: - -- here we make use of the d_ pattern; but even if that fails to yield - -- a number, the sort function will later be able to deal with it anyway - local new_priority = string.sub(k, 3) - dialog.n_dialogs[ k ].d_sort = new_priority - end - end - -- actually make this the chat with the highest priority - dialog.n_dialogs[ d_id ].d_sort = "0" - -- this is not immediately saved, even though the changes to the previous dialog with - -- the highest priority cannot be automaticly undone (but as long as it is not saved, - -- it really does not matter; and when saving, the player has to take some care) - table.insert(yl_speak_up.npc_was_changed[ n_id ], - "Dialog "..d_id..": Turned into new start dialog.") - end - - -- detect changes to d_text: text of the dialog (what the npc is saying) - -- (only happens in dialog edit menu) - if(fields.d_text and dialog.n_dialogs[ d_id ].d_text ~= fields.d_text) then - -- store that there have been changes to this npc - table.insert(yl_speak_up.npc_was_changed[ n_id ], - "Dialog "..d_id..": d_text (what the NPC says) was changed from \"".. - tostring( dialog.n_dialogs[ d_id ].d_text).. - "\" to \""..tostring(fields.d_text).."\".") - -- actually change the text - but do not save to disk yet - dialog.n_dialogs[ d_id ].d_text = fields.d_text - end - - -- add a new option/answer - if(fields[ "add_option"]) then - -- count the options/answers so that the limit is not exceeded - local anz_options = 0 - local future_o_id = "o_" .. yl_speak_up.find_next_id(dialog.n_dialogs[d_id].d_options) - if dialog.n_dialogs[d_id].d_options == nil then - dialog.n_dialogs[d_id].d_options = {} - else - local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs[d_id].d_options, "o_sort") - anz_options = #sorted_list - end - -- we don't want an infinite amount of answers per dialog - if(anz_options >= yl_speak_up.max_number_of_options_per_dialog) then - -- this is already checked earlier on and the button only shown if - -- options can be added; so this can reamin a chat message - minetest.chat_send_player(pname, "Sorry. Only ".. - tostring(yl_speak_up.max_number_of_options_per_dialog).. - " options/answers are allowed per dialog.") - fields.add_option = nil - else - dialog.n_dialogs[d_id].d_options[future_o_id] = { - o_id = future_o_id, - o_hide_when_prerequisites_not_met = "false", - o_grey_when_prerequisites_not_met = "false", - o_sort = -1, - o_text_when_prerequisites_not_met = "", - o_text_when_prerequisites_met = "", - } - -- necessary in order for it to work - local s = yl_speak_up.sanitize_sort(dialog.n_dialogs[d_id].d_options, yl_speak_up.speak_to[pname].o_sort) - dialog.n_dialogs[d_id].d_options[future_o_id].o_sort = s - table.insert(yl_speak_up.npc_was_changed[ n_id ], - "Dialog "..d_id..": Added new option/answer "..future_o_id..".") - -- if this is selected in the options edit menu, we want to move straight on to the new option - result["show_next_option"] = future_o_id - end - end - - if(fields[ "del_option"] and fields.o_id and dialog.n_dialogs[d_id].d_options[fields.o_id]) then - local o_id = fields.o_id - -- which dialog to show instead of the deleted one? - local next_o_id = o_id - local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs[d_id].d_options, "o_sort") - for i, o in ipairs(sorted_list) do - if(o == o_id and sorted_list[ i+1 ]) then - next_o_id = sorted_list[ i+1 ] - elseif(o == o_id and sorted_list[ i-1 ]) then - next_o_id = sorted_list[ i-1 ] - end - end - table.insert(yl_speak_up.npc_was_changed[ n_id ], - "Dialog "..d_id..": Option "..tostring(o_id).." deleted.") - -- actually delete the dialog - dialog.n_dialogs[d_id].d_options[o_id] = nil - -- the current dialog is deleted; we need to show another one - result["show_next_option"] = next_o_id - -- after deleting the entry, all previous/further changes to it are kind of unintresting - return result - end - - -- ignore entries to o_sort if they are not a number - if(fields[ "edit_option_o_sort"] - and tonumber(fields[ "edit_option_o_sort"]) - and fields.o_id and dialog.n_dialogs[d_id].d_options[fields.o_id]) then - local o_id = fields.o_id - local new_nr = tonumber(fields[ "edit_option_o_sort"]) - local old_nr = tonumber(dialog.n_dialogs[d_id].d_options[o_id].o_sort) - -- if the nr is -1 (do not show) then we are done already: nothing to do - if(old_nr == new_nr) then - -- -1: do not list as option/answer (but still store and keep it) - elseif(new_nr == -1 and old_nr ~= -1) then - dialog.n_dialogs[d_id].d_options[o_id].o_sort = "-1" - table.insert(yl_speak_up.npc_was_changed[ n_id ], - "Dialog "..d_id..": Option "..tostring(o_id).." was set to -1 (do not list).") - else - -- get the old sorted list - local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs[d_id].d_options, "o_sort") - -- negative numbers are not shown - local entries_shown_list = {} - for i, o in ipairs(sorted_list) do - local n = tonumber(dialog.n_dialogs[d_id].d_options[o].o_sort) - if(n and n > 0 and o ~= o_id) then - table.insert(entries_shown_list, o) - end - end - -- insert the entry at the new position and let lua do the job - table.insert(entries_shown_list, new_nr, o_id) - -- take the indices from that new list as new sort values and store them; - -- this has the side effect that duplicate entries get sorted out as well - for i, o in ipairs(entries_shown_list) do - dialog.n_dialogs[d_id].d_options[o].o_sort = tostring(i) - end - -- store that there was a cahnge - table.insert(yl_speak_up.npc_was_changed[ n_id ], - "Dialog "..d_id..": Option "..tostring(o_id).." was moved to position ".. - tostring(new_nr)..".") - end - end - - -- changes to options are not possible if there are none - if(dialog.n_dialogs[ d_id ].d_options) then - - -- detect changes to text_option_: text for option - for k, v in pairs(dialog.n_dialogs[ d_id ].d_options) do - if( fields[ "text_option_"..k ] - and fields[ "text_option_"..k ] ~= v.o_text_when_prerequisites_met ) then - -- store that there have been changes to this npc - table.insert(yl_speak_up.npc_was_changed[ n_id ], - "Dialog "..d_id..": The text for option "..tostring(k).. - " was changed from \""..tostring(v.o_text_when_prerequisites_met).. - "\" to \""..tostring(fields[ "text_option_"..k]).."\".") - -- actually change the text of the option - dialog.n_dialogs[ d_id ].d_options[ k ].o_text_when_prerequisites_met = fields[ "text_option_"..k ] - end - end - - -- detect changes to d_id_: target dialog for option - for k, v in pairs(dialog.n_dialogs[ d_id ].d_options) do - if(fields[ "d_id_"..k ]) then - local new_target_dialog = yl_speak_up.prepare_new_dialog_for_option( - dialog, pname, n_id, d_id, k, fields[ "d_id_"..k ], v.o_results) - if(new_target_dialog ~= fields[ "d_id_"..k ]) then - fields[ "d_id_"..k ] = new_target_dialog - -- in options edit menu: show this update - result["show_next_option"] = k - end - end - end - end - - -- add a new dialog; either via "+" button or "New dialog" in dialog dropdown menu - -- this has to be done after all the other changes because those (text changes etc.) still - -- apply to the *old* dialog - if(fields.show_new_dialog - or(fields["d_id"] and fields["d_id"] == yl_speak_up.text_new_dialog_id)) then - -- create the new dialog and make sure it gets shown - local d_id = yl_speak_up.add_new_dialog(dialog, pname, nil) - -- actually show the new dialog - fields["d_id"] = d_id - fields["show_new_dialog"] = nil - end - - -- delete one empty dialog - if(fields.delete_this_empty_dialog) then - local anz_options = 0 - -- we need to show a new dialog after this one was deleted - local new_dialog = d_id - local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs, "d_sort") - for i, k in ipairs(sorted_list) do - -- count the options of this dialog - if(k == d_id) then - if(dialog.n_dialogs[d_id].d_options) then - for o, w in pairs(dialog.n_dialogs[d_id].d_options) do - anz_options = anz_options + 1 - end - end - if(sorted_list[i+1]) then - new_dialog = sorted_list[i+1] - elseif(sorted_list[i-1]) then - new_dialog = sorted_list[i-1] - end - end - end - -- there needs to be one dialog left after deleting this one, - if(#sorted_list > 1 - -- this dialog isn't allowed to hold any more options/answers - and anz_options == 0 - -- we really found a new dialog to show - and new_dialog ~= d_id - -- and the text needs to be empty - and dialog.n_dialogs[ d_id ].d_text == "") then - -- actually delete this dialog - dialog.n_dialogs[ d_id ] = nil - -- ..and store it to disk - yl_speak_up.delete_dialog(n_id, d_id) - yl_speak_up.log_change(pname, n_id, - "Deleted dialog "..tostring(d_id)..".") - -- switch to another dialog (this one was deleted after all) - fields["d_id"] = new_dialog - fields["show_new_dialog"] = nil - else - -- deleting is only possible from the talk menu, and there the delete - -- button is only shown if the dialog can be deleted; so this can remain - -- a chat message - minetest.chat_send_player(pname, "Sorry. This dialog cannot be deleted (yet). ".. - "It is either the only dialog left or has a non-empty text or has at ".. - "least on remaining option/answer.") - end - end - - -- not in options edit menu? - local o_id = fields.o_id - if(not(o_id)) then - return result - end - - local d_option = dialog.n_dialogs[ d_id ].d_options[ o_id ] - -- change alternate text when preconditions are not met - -- (only happens in options edit menu) - if(fields.option_text_not_met and d_option - and d_option.o_text_when_prerequisites_not_met ~= fields.option_text_not_met) then - -- add change to changelog - table.insert(yl_speak_up.npc_was_changed[ n_id ], - "Dialog "..d_id..": The alternate text for option "..tostring(o_id).. - " was changed from \"".. - tostring(d_option.o_text_when_prerequisites_not_met).."\" to \"".. - tostring(fields.option_text_not_met).."\".") - -- actually change the text of the option - d_option.o_text_when_prerequisites_not_met = fields.option_text_not_met - end - - -- toggle autoselection/autoclick of an option - if(d_option and fields.option_autoanswer and fields.option_autoanswer ~= "") then - if(d_option.o_autoanswer and d_option.o_autoanswer == 1 - and fields.option_autoanswer == "by clicking on it") then - d_option.o_autoanswer = nil - table.insert(yl_speak_up.npc_was_changed[ n_id ], - "Dialog "..d_id..": The modus for option "..tostring(o_id).. - " was changed from \"automaticly\" to \"by clicking on it\".") - elseif((not(d_option.o_autoanswer) or d_option.o_autoanswer ~= 1) - and fields.option_autoanswer == "automaticly") then - d_option.o_autoanswer = 1 - table.insert(yl_speak_up.npc_was_changed[ n_id ], - "Dialog "..d_id..": The modus for option "..tostring(o_id).. - " was changed from \"by clicking on it\" to \"automaticly\".") - end - end - - -- handle hide/grey out/show alternate answer - -- (only happens in options edit menu) - if(fields.hide_or_grey_or_alternate_answer and d_option) then - if(fields.hide_or_grey_or_alternate_answer == "..hide this answer." - and d_option.o_hide_when_prerequisites_not_met ~= "true") then - d_option.o_hide_when_prerequisites_not_met = "true" - d_option.o_grey_when_prerequisites_not_met = "false" - table.insert(yl_speak_up.npc_was_changed[ n_id ], - "Dialog "..d_id..": If precondition for option "..tostring(o_id).. - " is not met, hide option/answer.") - -- make sure we show this options update next - result["show_next_option"] = o_id - elseif(fields.hide_or_grey_or_alternate_answer == "..grey out the following answer:" - and d_option.o_grey_when_prerequisites_not_met ~= "true") then - d_option.o_hide_when_prerequisites_not_met = "false" - d_option.o_grey_when_prerequisites_not_met = "true" - table.insert(yl_speak_up.npc_was_changed[ n_id ], - "Dialog "..d_id..": If precondition for option "..tostring(o_id).. - " is not met, grey out option/answer.") - result["show_next_option"] = o_id - elseif(fields.hide_or_grey_or_alternate_answer == "..display the following alternate answer:" - and (d_option.o_hide_when_prerequisites_not_met ~= "false" - or d_option.o_grey_when_prerequisites_not_met) ~= "false") then - d_option.o_hide_when_prerequisites_not_met = "false" - d_option.o_grey_when_prerequisites_not_met = "false" - table.insert(yl_speak_up.npc_was_changed[ n_id ], - "Dialog "..d_id..": If precondition for option "..tostring(o_id).. - " is not met, show alternate option/answer.") - result["show_next_option"] = o_id - end - end - - -- how many times can the player fail to execute the action successfully? - if(fields[ "timer_max_attempts_on_failure"]) then - local field_name = "timer_max_attempts_on_failure" - local timer_name = "timer_on_failure_"..tostring(d_id).."_"..tostring(o_id) - if(not(tonumber(fields[ field_name ]))) then - fields[ field_name ] = 0 - end - -- make sure the variable exists - if(yl_speak_up.add_time_based_variable(timer_name)) then - yl_speak_up.set_variable_metadata(timer_name, nil, "parameter", "max_attempts", - fields[ field_name ]) - end - end - -- ..and how long has the player to wait in order to try again? - if(fields[ "timer_max_seconds_on_failure"]) then - local field_name = "timer_max_seconds_on_failure" - local timer_name = "timer_on_failure_"..tostring(d_id).."_"..tostring(o_id) - if(not(tonumber(fields[ field_name ]))) then - fields[ field_name ] = 0 - end - -- make sure the variable exists - if(yl_speak_up.add_time_based_variable(timer_name)) then - yl_speak_up.set_variable_metadata(timer_name, nil, "parameter", "duration", - fields[ field_name ]) - end - end - if(fields[ "timer_max_seconds_on_success"]) then - local field_name = "timer_max_seconds_on_success" - local timer_name = "timer_on_success_"..tostring(d_id).."_"..tostring(o_id) - if(not(tonumber(fields[ field_name ]))) then - fields[ field_name ] = 0 - end - -- make sure the variable exists - if(yl_speak_up.add_time_based_variable(timer_name)) then - yl_speak_up.set_variable_metadata(timer_name, nil, "parameter", "duration", - fields[ field_name ]) - end - end - -- currently only contains result["show_new_option"] (which is needed for options edit menu) - return result -end --- end of yl_speak_up.edit_mode_apply_changes - - - -- identify multiple results that lead to target dialogs yl_speak_up.check_for_disambigous_results = function(n_id, pname) local errors_found = false diff --git a/init.lua b/init.lua index bdee6eb..ebbc497 100644 --- a/init.lua +++ b/init.lua @@ -23,6 +23,8 @@ dofile(modpath .. "show_fs.lua") dofile(modpath .. "fs_talkdialog.lua") -- ask if the player wants to save, discard or go back in edit mode dofile(modpath .. "fs_save_or_discard_or_back.lua") +-- the player wants to change something regarding the dialog +dofile(modpath .. "edit_mode_apply_changes.lua") -- as the name says: a collection of custom functions that you can -- override on your server or in your game to suit your needs; -- Note: No special privs are needed to call custom functions. But...