diff --git a/edit_mode.lua b/edit_mode.lua index c7250c4..ebd76ab 100644 --- a/edit_mode.lua +++ b/edit_mode.lua @@ -7,6 +7,195 @@ yl_speak_up.in_edit_mode = function(pname) end +-- This is the main talkdialog the NPC shows when right-clicked. (now in edit_mode) +local old_input_talk = yl_speak_up.input_talk +yl_speak_up.input_talk = function(player, formname, fields) + local pname = player:get_player_name() + -- Is the player working on this particular npc? + local edit_mode = yl_speak_up.in_edit_mode(pname) + -- if not: do the normal things outside edit mode + if(not(edit_mode) and not(fields.button_start_edit_mode)) then + return old_input_talk(player, formname, fields) + end + -- selected option/answer + local o = "" + -- do all the processing and executing old_input_talk (in non-edit_mode) + -- can do - but do not execute any actions; just return o + fields.just_return_selected_option = true + o = old_input_talk(player, formname, fields) + -- old_input_talk handled it (including some error detection like + -- wrong formname, not talking to npc, npc not configured) + + -- if in edit mode: detect if something was changed; + local result = yl_speak_up.edit_mode_apply_changes(pname, fields) + -- o is only nil if the old function returned nil; it does that + -- when it found a fitting reaction to a button press + if(not(o) and not(fields.button_start_edit_mode)) then + return + end + local n_id = yl_speak_up.speak_to[pname].n_id + + -- start edit mode (requires npc_talk_owner) + if fields.button_start_edit_mode then + -- check if this particular NPC is really owned by this player or if the player has global privs + if(not(yl_speak_up.may_edit_npc(player, n_id))) then + minetest.chat_send_player(pname, "Sorry. You do not have the npc_talk_owner or npc_talk_master priv.") + return + end + -- the staff allows to create multiple target dialogs as result; this makes no sense + -- and is too disambigous + if(yl_speak_up.check_for_disambigous_results(n_id, pname)) then + -- this needs to be fixed by someone with a staff; we don't know which dialog is the right + -- result + return + end + -- for older formspec versions: reset scroll counter + yl_speak_up.speak_to[pname].counter = 1 + yl_speak_up.speak_to[pname].option_index = 1 + -- enter edit mode with that particular NPC + yl_speak_up.edit_mode[pname] = yl_speak_up.speak_to[pname].n_id + -- load the NPC dialog anew - but only what the NPC itself has to say, no generic dialogs + yl_speak_up.speak_to[pname].dialog = yl_speak_up.load_dialog(n_id, false) + -- start a new chat - but this time in edit mode + yl_speak_up.speak_to[pname].d_id = nil + yl_speak_up.show_fs(player, "talk", {n_id = yl_speak_up.speak_to[pname].n_id, d_id = nil}) + return + -- end edit mode (does not require the priv; will only switch back to normal behaviour) + elseif fields.button_end_edit_mode then + -- if there are any changes done: ask first and don't end edit mode yet + yl_speak_up.show_fs(player, "quit", nil) + return + end + + + -- show which dialogs point to this one + if(fields.show_what_points_to_this_dialog) then + local dialog = yl_speak_up.speak_to[pname].dialog + local d_id = yl_speak_up.speak_to[pname].d_id + yl_speak_up.show_fs(player, "show_what_points_to_this_dialog", + yl_speak_up.speak_to[pname].d_id) + return + end + + -- the player wants to change name and description; show the formspec + if(fields.button_edit_name_and_description) then + -- this is not the initial config - but the same formspec can be used + yl_speak_up.show_fs(player, "initial_config", + {n_id = n_id, d_id = yl_speak_up.speak_to[pname].d_id, false}) + return + end + + -- change skin, cape and wielded items + if(fields.edit_skin) then + local dialog = yl_speak_up.speak_to[pname].dialog + -- necessary so that the fashin formspec can be created + yl_speak_up.speak_to[pname].n_npc = dialog.n_npc + yl_speak_up.show_fs(player, "fashion") + return + end + + if(fields.button_save_dialog) then + yl_speak_up.show_fs(player, "talk", + {n_id = n_id, d_id = yl_speak_up.speak_to[pname].d_id, do_save = true}) + return + end + + if(fields.button_export_dialog) then + yl_speak_up.show_fs(player, "export") + return + end + + if(fields.button_edit_notes) then + yl_speak_up.show_fs(player, "notes") + return + end + + -- the player wants to give something to the NPC + -- (more complex in edit mode) + if(fields.player_offers_item) then + local dialog = yl_speak_up.speak_to[pname].dialog + local future_d_id = "d_got_item" + -- make sure this dialog exists; create if needed + if(not(dialog.n_dialogs[ future_d_id ])) then + dialog.n_dialogs[future_d_id] = { + d_id = future_d_id, + d_type = "text", + d_text = "", + d_sort = 9999 -- make this the last option + } + end + -- in edit mode: allow to edit the options + yl_speak_up.show_fs(player, "talk", {n_id = n_id, d_id = future_d_id}) + return + end + + + -- button was clicked, now let's execute the results + local d_id = yl_speak_up.speak_to[pname].d_id + local dialog = yl_speak_up.speak_to[pname].dialog + + -- all three buttons (pre(C)onditions, (Ef)fects, edit option) lead to the same new formspec + local n_dialog = dialog.n_dialogs[d_id] + + if(n_dialog and n_dialog.d_options) then + for o_id,v in pairs(n_dialog.d_options) do + if( fields["edit_option_"..o_id] + or fields["conditions_"..o_id] + or fields["actions_"..o_id] + or fields["quests_"..o_id] + or fields["effects_"..o_id]) then + -- store which option we want to edit + yl_speak_up.speak_to[pname].o_id = o_id + -- if something was changed: ask for confirmation + yl_speak_up.show_fs(player, "edit_option_dialog", + {n_id = yl_speak_up.speak_to[pname].n_id, + d_id = d_id, o_id = o_id, caller="button"}) + return + end + end + end + + -- we may soon need actions and o_results from the selected_option + local selected_option = {} + if(yl_speak_up.check_if_dialog_has_option(dialog, d_id, o)) then + selected_option = dialog.n_dialogs[d_id].d_options[o] + end + + -- in edit mode: has another dialog been selected? + -- if nothing better can be found: keep the old dialog + local show_dialog = d_id + -- an option button was selected; + -- since we do not execute actions and effects in edit mode, we need to find out the + -- right target dialog manually (and assume all went correct) + if( o ~= "" ) then + -- find out the normal target dialog of this option + if(selected_option and selected_option.o_results) then + for k, v in pairs(selected_option.o_results) do + if(v and v.r_type == "dialog") then + show_dialog = v.r_value + end + end + end + -- dropdown menu was used; provided the dialog exists (and it's not the "New dialog" option) + -- (if a new dialog was added using the "+" button, fields.d_id gets set accordingly) + elseif(fields.d_id and fields.d_id ~= show_dialog and dialog.n_dialogs[fields.d_id]) then + show_dialog = fields.d_id + -- in edit mode: prev_dialog_../next_dialog_.. was selected + else + for k,v in pairs(dialog.n_dialogs) do + if(fields["prev_dialog_"..k]) then + show_dialog = k + elseif(fields["next_dialog_"..k]) then + show_dialog = k + end + end + end + yl_speak_up.show_fs(player, "talk", {n_id = n_id, d_id = show_dialog}) + -- no option was selected - so we need to end this here + return +end + + -- in edit mode, *all* options are displayed local old_calculate_displayable_options = yl_speak_up.calculate_displayable_options yl_speak_up.calculate_displayable_options = function(pname, d_options, allow_recursion) diff --git a/edit_mode_apply_changes.lua b/edit_mode_apply_changes.lua index 34cc374..0b1628d 100644 --- a/edit_mode_apply_changes.lua +++ b/edit_mode_apply_changes.lua @@ -60,6 +60,9 @@ end -- (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] + if(not(n_id) or not(yl_speak_up.speak_to[pname])) then + return + end local d_id = yl_speak_up.speak_to[pname].d_id local dialog = yl_speak_up.speak_to[pname].dialog diff --git a/fs/fs_talkdialog.lua b/fs/fs_talkdialog.lua index dc8c221..aa84b49 100644 --- a/fs/fs_talkdialog.lua +++ b/fs/fs_talkdialog.lua @@ -1,11 +1,12 @@ -- This is the main talkdialog the NPC shows when right-clicked. +-- Returns o (selected dialog option) if fields.just_return_selected_option +-- is set (useful for edit_mode). yl_speak_up.input_talk = function(player, formname, fields) if formname ~= "yl_speak_up:talk" then return end local pname = player:get_player_name() - local o = "" -- error: not talking? if(not(yl_speak_up.speak_to[pname])) then @@ -62,96 +63,7 @@ yl_speak_up.input_talk = function(player, formname, fields) return 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 in edit mode: detect if something was changed; - if(edit_mode or fields.button_edit_name_and_description) then - local result = yl_speak_up.edit_mode_apply_changes(pname, fields) - end - - -- show which dialogs point to this one - if(edit_mode and fields.show_what_points_to_this_dialog) then - local dialog = yl_speak_up.speak_to[pname].dialog - local d_id = yl_speak_up.speak_to[pname].d_id - yl_speak_up.show_fs(player, "show_what_points_to_this_dialog", - yl_speak_up.speak_to[pname].d_id) - return - end - - -- the player wants to change name and description; show the formspec - if(edit_mode and fields.button_edit_name_and_description) then - -- this is not the initial config - but the same formspec can be used - yl_speak_up.show_fs(player, "initial_config", - {n_id = n_id, d_id = yl_speak_up.speak_to[pname].d_id, false}) - return - end - - -- the player wants to access the inventory of the NPC - if((edit_mode and fields.show_inventory) - or (fields.show_inventory and yl_speak_up.may_edit_npc(player, n_id))) then - -- the inventory is just an inventory with a back button; come back to this dialog here - yl_speak_up.show_fs(player, "inventory") - return - end - - -- change skin, cape and wielded items - if(edit_mode and fields.edit_skin) then - local dialog = yl_speak_up.speak_to[pname].dialog - -- necessary so that the fashin formspec can be created - yl_speak_up.speak_to[pname].n_npc = dialog.n_npc - yl_speak_up.show_fs(player, "fashion") - return - end - - if(edit_mode and fields.button_save_dialog) then - yl_speak_up.show_fs(player, "talk", - {n_id = n_id, d_id = yl_speak_up.speak_to[pname].d_id, do_save = true}) - return - end - - if(edit_mode and fields.button_export_dialog) then - yl_speak_up.show_fs(player, "export") - return - end - - if(edit_mode and fields.button_edit_notes) then - yl_speak_up.show_fs(player, "notes") - return - end - - -- start edit mode (requires npc_talk_owner) - if fields.button_start_edit_mode then - -- check if this particular NPC is really owned by this player or if the player has global privs - if(not(yl_speak_up.may_edit_npc(player, n_id))) then - minetest.chat_send_player(pname, "Sorry. You do not have the npc_talk_owner or npc_talk_master priv.") - return - end - -- the staff allows to create multiple target dialogs as result; this makes no sense - -- and is too disambigous - if(yl_speak_up.check_for_disambigous_results(n_id, pname)) then - -- this needs to be fixed by someone with a staff; we don't know which dialog is the right - -- result - return - end - -- for older formspec versions: reset scroll counter - yl_speak_up.speak_to[pname].counter = 1 - yl_speak_up.speak_to[pname].option_index = 1 - -- enter edit mode with that particular NPC - yl_speak_up.edit_mode[pname] = yl_speak_up.speak_to[pname].n_id - -- load the NPC dialog anew - but only what the NPC itself has to say, no generic dialogs - yl_speak_up.speak_to[pname].dialog = yl_speak_up.load_dialog(n_id, false) - -- start a new chat - but this time in edit mode - yl_speak_up.speak_to[pname].d_id = nil - yl_speak_up.show_fs(player, "talk", {n_id = yl_speak_up.speak_to[pname].n_id, d_id = nil}) - return - -- end edit mode (does not require the priv; will only switch back to normal behaviour) - elseif fields.button_end_edit_mode then - -- if there are any changes done: ask first and don't end edit mode yet - yl_speak_up.show_fs(player, "quit", nil) - return - end - + -- normal mode + edit_mode (not exclusive to edit_mode): if fields.quit or fields.button_exit then -- if there are any changes done: ask first and don't quit yet yl_speak_up.show_fs(player, "quit", nil) @@ -164,6 +76,27 @@ yl_speak_up.input_talk = function(player, formname, fields) return end + -- the player wants to give something to the NPC + -- (less complex outside edit mode) + if(fields.player_offers_item) then + -- normal mode: take the item the player wants to offer + yl_speak_up.show_fs(player, "player_offers_item", nil) + return + end + + -- the player wants to access the inventory of the NPC + if(fields.show_inventory and yl_speak_up.may_edit_npc(player, n_id)) then + -- the inventory is just an inventory with a back button; come back to this dialog here + yl_speak_up.show_fs(player, "inventory") + return + end + + -- the player wants to see the trade list + if(fields.show_trade_list) then + yl_speak_up.show_fs(player, "trade_list", nil) + return + end + if fields.button_up then yl_speak_up.speak_to[pname].option_index = @@ -184,35 +117,9 @@ yl_speak_up.input_talk = function(player, formname, fields) yl_speak_up.speak_to[pname].option_index = 1 end - -- the player wants to see the trade list - if(fields.show_trade_list) then - yl_speak_up.show_fs(player, "trade_list", nil) - return - end - - -- the player wants to give something to the NPC - if(fields.player_offers_item) then - if(not(edit_mode)) then - -- normal mode: take the item the player wants to offer - yl_speak_up.show_fs(player, "player_offers_item", nil) - else - local dialog = yl_speak_up.speak_to[pname].dialog - local future_d_id = "d_got_item" - -- make sure this dialog exists; create if needed - if(not(dialog.n_dialogs[ future_d_id ])) then - dialog.n_dialogs[future_d_id] = { - d_id = future_d_id, - d_type = "text", - d_text = "", - d_sort = 9999 -- make this the last option - } - end - -- in edit mode: allow to edit the options - yl_speak_up.show_fs(player, "talk", {n_id = n_id, d_id = future_d_id}) - end - return - end + -- has an option/answer been selected? + local o = "" for k, v in pairs(fields) do -- only split into 2 parts at max local s = string.split(k, "_", false, 2) @@ -223,102 +130,37 @@ yl_speak_up.input_talk = function(player, formname, fields) o = s[2] .. "_" .. s[3] end end - - if not(edit_mode) and o == "" then + -- this is for edit mode - we need a diffrent reaction there, not executing actions + if(fields.just_return_selected_option) then + return o + end + -- nothing selected + if(o == "") then return end -- Let's check if the button was among the "allowed buttons". Only those may be executed - if not(edit_mode) and (yl_speak_up.speak_to[pname].allowed and yl_speak_up.speak_to[pname].allowed[o] == false) then + if(not(yl_speak_up.speak_to[pname].allowed) or not(yl_speak_up.speak_to[pname].allowed[o])) then return end - -- button was clicked, now let's execute the results - - local d_id = yl_speak_up.speak_to[pname].d_id - - local dialog = yl_speak_up.speak_to[pname].dialog - -- we may soon need actions and o_results from the selected_option local selected_option = {} - if(d_id and o and dialog - and dialog.n_dialogs - and dialog.n_dialogs[d_id] - and dialog.n_dialogs[d_id].d_options - and dialog.n_dialogs[d_id].d_options[o]) then + if(yl_speak_up.check_if_dialog_has_option(dialog, d_id, o)) then selected_option = dialog.n_dialogs[d_id].d_options[o] end - if(not(edit_mode)) then - -- abort if the option does not exist - if(not(selected_option)) then - return - end - yl_speak_up.speak_to[pname].o_id = o - -- start with executing the first action - yl_speak_up.execute_next_action(player, nil, true, formname) + -- abort if the option does not exist + if(not(selected_option)) then return end - - -- if(edit_mode) (the other case has been dealt with above) - -- all three buttons (pre(C)onditions, (Ef)fects, edit option) lead to the same new formspec - local n_dialog = dialog.n_dialogs[d_id] - - if(n_dialog and n_dialog.d_options) then - for o_id,v in pairs(n_dialog.d_options) do - if( fields["edit_option_"..o_id] - or fields["conditions_"..o_id] - or fields["actions_"..o_id] - or fields["quests_"..o_id] - or fields["effects_"..o_id]) then - -- store which option we want to edit - yl_speak_up.speak_to[pname].o_id = o_id - -- if something was changed: ask for confirmation - yl_speak_up.show_fs(player, "edit_option_dialog", - {n_id = yl_speak_up.speak_to[pname].n_id, - d_id = d_id, o_id = o_id, caller="button"}) - return - end - end - end - - - -- in edit mode: has another dialog been selected? - -- if nothing better can be found: keep the old dialog - local show_dialog = d_id - -- an option button was selected; - -- since we do not execute actions and effects in edit mode, we need to find out the - -- right target dialog manually (and assume all went correct) - if( o ~= "" ) then - -- find out the normal target dialog of this option - if(selected_option and selected_option.o_results) then - for k, v in pairs(selected_option.o_results) do - if(v and v.r_type == "dialog") then - show_dialog = v.r_value - end - end - end - -- dropdown menu was used; provided the dialog exists (and it's not the "New dialog" option) - -- (if a new dialog was added using the "+" button, fields.d_id gets set accordingly) - elseif(fields.d_id and fields.d_id ~= show_dialog and dialog.n_dialogs[fields.d_id]) then - show_dialog = fields.d_id - -- in edit mode: prev_dialog_../next_dialog_.. was selected - else - for k,v in pairs(dialog.n_dialogs) do - if(fields["prev_dialog_"..k]) then - show_dialog = k - elseif(fields["next_dialog_"..k]) then - show_dialog = k - end - end - end - yl_speak_up.show_fs(player, "talk", {n_id = n_id, d_id = show_dialog}) - -- no option was selected - so we need to end this here + yl_speak_up.speak_to[pname].o_id = o + -- start with executing the first action + yl_speak_up.execute_next_action(player, nil, true, formname) return end - -- helper function for yl_speak_up.get_fs_talkdialog: -- shows the text the NPC "speaks" -- (this is pretty boring; the more intresting stuff happens in edit_mode)