From 3b90b1bb5d88015285b2bf6d563dc2ef6b9191ab Mon Sep 17 00:00:00 2001 From: Sokomine Date: Fri, 30 Apr 2021 21:26:48 +0200 Subject: [PATCH] apply edit changes but don't store permanent yet --- functions.lua | 170 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 164 insertions(+), 6 deletions(-) diff --git a/functions.lua b/functions.lua index 3c2de90..eeeae0c 100644 --- a/functions.lua +++ b/functions.lua @@ -37,6 +37,10 @@ yl_sokomine["q_apple_explorer"] = {} -- store if the player is editing a particular NPC; format: yl_speak_up.edit_mode[pname] = npc_id yl_speak_up.edit_mode = {} +-- changes applied in edit_mode are applied immediately - but not immediately stored to disk +-- (this gives the players a chance to back off in case of unwanted changes) +yl_speak_up.npc_was_changed = {} + function yl_speak_up.init_mob_table() return false end @@ -999,6 +1003,7 @@ local function get_fs_talkdialog(player, n_id, d_id) -- display the window with the text the NPC is saying -- TODO: make name and description likewise editable? + -- TODO: make name of dialog and sort option editable? if(edit_mode) then -- TODO: sort by name? @@ -1197,8 +1202,8 @@ local function get_fs_talkdialog(player, n_id, d_id) if(edit_mode) then -- chat option: "Add a new answer/option to this dialog." h = h + 1 - table.insert(formspec, "button_exit[0.5," .. h .. ";53.8,0.9;button_add_option;]") - table.insert(formspec, "tooltip[button_add_option;Adds a new option to this dialog. You can delete options via the option edit menu.]") + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;add_option;]") + table.insert(formspec, "tooltip[add_option;Adds a new option to this dialog. You can delete options via the option edit menu.]") table.insert(formspec, "label[0.7,"..(h+0.45)..";Add a new option/answer to this dialog. You can delete options via the option edit menu.]") -- chat option: "That was all. I'm finished with giving you new orders. Remember them!" h = h + 1 @@ -1659,6 +1664,153 @@ minetest.register_on_player_receive_fields( -- talk +-- helper function +yl_speak_up.add_new_dialog = function(dialog, pname) + future_d_id = "d_" .. find_next_id(dialog.n_dialogs) + -- Initialize empty dialog + dialog.n_dialogs[future_d_id] = { + d_id = future_d_id, + d_type = "text", + d_text = "", + d_sort = -1 -- TODO + } + -- store that there have been changes to this npc + table.insert(yl_speak_up.npc_was_changed[ yl_speak_up.edit_mode[pname] ], + "Dialog "..future_d_id..": New dialog added.") + return future_d_id +end + +-- helper function for formspec "yl_speak_up:talk" 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 +-- TODO: implement the change detection and ask the user what to do (save? discard?) +-- TODO: what if it is a new dialog? +-- Parameters: +-- pname player name +-- fields the fields returned from the formspec +yl_speak_up.edit_mode_apply_changes = function(pname, fields) + 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[ yl_speak_up.edit_mode[pname]])) then + yl_speak_up.npc_was_changed[ yl_speak_up.edit_mode[pname]] = {} + 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 + + -- detect changes to d_text: text of the dialog (what the npc is saying) + if(fields.d_text and dialog.n_dialogs[ d_id ].d_text ~= fields.d_text) then + --minetest.chat_send_player("singleplayer","CHANGED: d_text (what the NPC says) was changed\n from: "..tostring( dialog.n_dialogs[ d_id ].d_text).."\n to: "..tostring(fields.d_text)) + -- store that there have been changes to this npc + table.insert(yl_speak_up.npc_was_changed[ yl_speak_up.edit_mode[pname] ], + "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 + -- TODO: option to delete dialogs (when text empty and no options present?) + end + + -- add a new option/answer + -- TODO: limit the amount of possible answers to a sensible amount + if(fields[ "add_option"]) then + local future_o_id = "o_" .. 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 = {} + end + 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 = 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[ yl_speak_up.edit_mode[pname] ], + "Dialog "..d_id..": Added new option/answer "..future_o_id..".") + end + + -- changes to options are not possible if there are none + -- TODO: handle adding of a new dialog + if(not(dialog.n_dialogs[ d_id ].d_options)) then + return + end + + -- 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 + -- TODO: delete option if text is empty + --minetest.chat_send_player("singleplayer", "CHANGED: Text for option "..tostring(k).." was changed\n from: "..tostring(v.o_text_when_prerequisites_met).."\n to: "..tostring(fields[ "text_option_"..k])) + -- store that there have been changes to this npc + table.insert(yl_speak_up.npc_was_changed[ yl_speak_up.edit_mode[pname] ], + "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 + -- TODO: this may also point to a new dialog + for k, v in pairs(dialog.n_dialogs[ d_id ].d_options) do + if( fields[ "d_id_"..k ] and v.o_results) then + for kr, vr in pairs(v.o_results) do + if( vr.r_type == "dialog" and vr.r_value and vr.r_value ~= fields[ "d_id_"..k ]) then + --minetest.chat_send_player("singleplayer", "CHANGED: Target dialog for option "..tostring(k).." was changed\n from: "..tostring(vr.r_value).."\n to: "..tostring(fields[ "d_id_"..k])) + -- store that there have been changes to this npc + table.insert(yl_speak_up.npc_was_changed[ yl_speak_up.edit_mode[pname] ], + "Dialog "..d_id..": The target dialog for option "..tostring(k).." was changed from "..tostring(vr.r_value).." to "..tostring(fields[ "d_id_"..k])..".") + -- actually change the target dialog + vr.r_value = fields[ "d_id_"..k ] + end + end + -- this might be the first option + elseif( fields["d_id_"..k ]) then + --minetest.chat_send_player("singleplayer", "CHANGED: Target dialog for option "..tostring(k).." was added\n as: "..tostring(fields[ "d_id_"..k])) + -- store that a new option has been added to this dialog + table.insert(yl_speak_up.npc_was_changed[ yl_speak_up.edit_mode[pname] ], + "Dialog "..d_id..": The target dialog for option "..tostring(k).." was changed from -default- to "..tostring(fields[ "d_id_"..k])..".") + + -- create a new result (first the id, then the actual result) + local future_r_id = "r_" .. find_next_id(dialog.n_dialogs[d_id].d_options[k].o_results) + if future_r_id == "r_1" then + dialog.n_dialogs[d_id].d_options[k].o_results = {} + end + -- actually store the new result + dialog.n_dialogs[d_id].d_options[k].o_results[future_r_id] = { + r_id = future_r_id, + r_type = "dialog", + r_value = fields[ "d_id_"..k ]} + 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 + d_id = yl_speak_up.add_new_dialog(dialog, pname) + -- actually show the new dialog + fields.d_id = d_id + fields.show_new_dialog = nil + end + + -- TODO just for debugging + for k,v in pairs(yl_speak_up.npc_was_changed[ yl_speak_up.edit_mode[pname] ]) do + minetest.chat_send_player("singleplayer", v ) + end +end +-- end of yl_speak_up.edit_mode_apply_changes + + + minetest.register_on_player_receive_fields( function(player, formname, fields) if formname ~= "yl_speak_up:talk" then @@ -1668,6 +1820,15 @@ minetest.register_on_player_receive_fields( local pname = player:get_player_name() local o = "" + -- Is the player working on this particular npc? + local edit_mode = (yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id) + + -- if in edit mode: detect if something was changed; + if(edit_mode) then + yl_speak_up.edit_mode_apply_changes(pname, fields) + end + + -- start edit mode (requires npc_talk_owner) if fields.button_start_edit_mode then if( not(minetest.check_player_privs(player, {npc_talk_owner=true})) @@ -1734,9 +1895,6 @@ minetest.register_on_player_receive_fields( end end - -- Is the player working on this particular npc? - local edit_mode = (yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id) - if not(edit_mode) and o == "" then return end @@ -1757,7 +1915,7 @@ minetest.register_on_player_receive_fields( -- if nothing better can be found: keep the old dialog local show_dialog = d_id -- "New dialog" button or option from dropdown menu was selected - if(fields.show_new_dialog or fields.d_id and fields.d_id == yl_speak_up.text_new_option_id) then + if(fields.show_new_dialog or (fields.d_id and fields.d_id == yl_speak_up.text_new_option_id)) then -- TODO -- dropdown menu was used; provided the dialog exists (and it's not the "New dialog" option) elseif(fields.d_id and fields.d_id ~= show_dialog and dialog.n_dialogs[fields.d_id]) then