diff --git a/fs_talkdialog.lua b/fs_talkdialog.lua new file mode 100644 index 0000000..11e1acf --- /dev/null +++ b/fs_talkdialog.lua @@ -0,0 +1,1099 @@ +-- This is the main talkdialog the NPC shows when right-clicked. + + +-- helper function for yl_speak_up.fs_talkdialog +yl_speak_up.calculate_portrait = function(pname, n_id) + local tex = yl_speak_up.speak_to[pname].textures + + local head = "" + + head = head .. "[combine:8x8:-8,-8=" .. tex[2] .. ":-40,-8=" .. tex[2] + + return head +end + + +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 = "" + + local n_id = yl_speak_up.speak_to[pname].n_id + + -- the NPC needs to be configured first; route input to the configuration dialog + if(not(yl_speak_up.speak_to[pname].dialog) + or not(yl_speak_up.speak_to[pname].dialog.n_npc) + or not(yl_speak_up.speak_to[pname].d_id)) then + yl_speak_up.input_fs_initial_config(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) 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 + + -- 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_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 + -- enter edit mode with that particular NPC + yl_speak_up.edit_mode[pname] = yl_speak_up.speak_to[pname].n_id + -- 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 + + 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) + return + end + + -- allow the player to take the item back + if(fields.show_player_offers_item and fields.show_player_offers_item ~= "") then + yl_speak_up.show_fs(player, "player_offers_item", nil) + return + end + + + if fields.button_up then + yl_speak_up.speak_to[pname].option_index = + yl_speak_up.speak_to[pname].option_index + yl_speak_up.max_number_of_buttons + yl_speak_up.show_fs(player, "talk", {n_id = yl_speak_up.speak_to[pname].n_id, + d_id = yl_speak_up.speak_to[pname].d_id}) + return + elseif fields.button_down then --and yl_speak_up.speak_to[pname].option_index > yl_speak_up.max_number_of_buttons then + yl_speak_up.speak_to[pname].option_index = + yl_speak_up.speak_to[pname].option_index - yl_speak_up.max_number_of_buttons + if yl_speak_up.speak_to[pname].option_index < 0 then + yl_speak_up.speak_to[pname].option_index = 1 + end + yl_speak_up.show_fs(player, "talk", {n_id = yl_speak_up.speak_to[pname].n_id, + d_id = yl_speak_up.speak_to[pname].d_id}) + return + else + 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 + + for k, v in pairs(fields) do + local s = string.split(k, "_") + + if + s[1] == "button" and s[2] ~= nil and s[2] ~= "" and s[2] ~= "exit" and s[2] ~= "back" and s[3] ~= nil and + s[2] ~= "up" and + s[2] ~= "down" + then + o = s[2] .. "_" .. s[3] + end + end + + if not(edit_mode) and 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 + 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 + 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) + return + end + + -- all three buttons (pre(C)onditions, (Ef)fects, edit option) lead to the same new formspec + if( edit_mode ) then + 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["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 + return + end +end + + +function yl_speak_up.on_rightclick(self, clicker) + --local item = clicker:get_wielded_item() + local name = clicker:get_player_name() + + -- Take the mob only with net or lasso + if self.owner and self.owner == name then + if mobs:capture_mob(self, clicker, nil, 100, 100, true, nil) then + return + end + end + + -- protect npc with mobs:protector + if mobs:protect(self, clicker) then + return + end + + -- bring up the dialog options + if clicker then + yl_speak_up.talk(self, clicker) + return + end +end + +function yl_speak_up.on_spawn(self) + --Let's assign an ID + + local m_talk = yl_speak_up.talk_after_spawn or true + local m_id = yl_speak_up.number_of_npcs + 1 + + yl_speak_up.number_of_npcs = m_id + yl_speak_up.modstorage:set_int("amount", m_id) + + yl_speak_up.mob_table[m_id] = "yl_speak_up:test_npc" + + self.yl_speak_up = { + talk = m_talk, + id = m_id, + textures = self.textures + } + + --Let's protect it + self.protected = true + self.tamed = true + self.object:set_armor_groups({immortal = 100}) + + minetest.log( + "action", + "[MOD] yl_speak_up: NPC with ID n_" .. + self.yl_speak_up.id .. " spawned at " .. minetest.pos_to_string(self.object:get_pos(), 0) + ) + + --Let's do it only once + return true +end + +-- recursion_depth is increased each time autoanswer is automaticly selected +yl_speak_up.get_fs_talkdialog = function(player, n_id, d_id, alternate_text, recursion_depth) + local pname = player:get_player_name() + local dialog = yl_speak_up.speak_to[pname].dialog + local context_d_id = yl_speak_up.speak_to[pname].d_id + local active_dialog + + if not player and not player:is_player() then + minetest.log( + "action", + "[MOD] yl_speak_up: User " .. + pname .. + " talked to unconfigured NPC with ID n_" .. + n_id .. ", position of user was " .. minetest.pos_to_string(player:get_pos(), 0) + ) + return yl_speak_up.get_error_message() + end + + -- currently no trade running (we're editing options) + yl_speak_up.trade[pname] = nil + yl_speak_up.speak_to[pname].trade_id = nil + + --[[ If we have an explicit call for a certain d_id, we grab it from parameters. + If not, we grab in from context. + When neither are present, we grab it from d_sort + ]]-- + + local c_d_id + + if d_id ~= nil then + active_dialog = dialog.n_dialogs[d_id] + c_d_id = d_id + elseif yl_speak_up.speak_to[pname].d_id ~= nil then + c_d_id = yl_speak_up.speak_to[pname].d_id + active_dialog = dialog.n_dialogs[c_d_id] + elseif dialog.n_dialogs ~= nil then + -- Find the dialog with d_sort = 0 + local lowest_sort = nil + for k, v in pairs(dialog.n_dialogs) do + if tonumber(v.d_sort) ~= nil then + if not lowest_sort or tonumber(v.d_sort) < tonumber(lowest_sort) or v.d_sort == "0" then + if tonumber(v.d_sort) >= 0 then + lowest_sort = v.d_sort + active_dialog = v + c_d_id = k + end + end + end + end + else + -- it may be possible that this player can initialize this npc + minetest.log( + "action", + "[MOD] yl_speak_up: User " .. + pname .. + " talked to unconfigured NPC with ID n_" .. + n_id .. ", position of user was " .. minetest.pos_to_string(player:get_pos(), 0) + ) + -- this is the initial config + -- (input ends up at yl_speak_up.input_talk and needs to be rerouted) + return yl_speak_up.get_fs_initial_config(player, n_id, d_id, true) + end + + if c_d_id == nil then return yl_speak_up.get_error_message() end + + yl_speak_up.speak_to[pname].d_id = c_d_id + + -- Now we have a dialog to display to the user + + -- do not crash in case of error + if(not(active_dialog)) then + return "size[6,2]".. + "label[0.2,0.5;Ups! Something went wrong. Please try again.]" + 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) + + -- evaluate the preconditions of each option and check if the option can be offered + local allowed = yl_speak_up.calculate_displayable_options(pname, active_dialog.d_options, edit_mode, + -- avoid loops by limiting max recoursion depths for autoanswers + (recursion_depth < yl_speak_up.max_allowed_recursion_depth)) + -- abort here if needed - the autoanswer/autoselection did choose an option for us alread + if(not(edit_mode) and allowed and allowed["autoanswer"] and allowed["autoanswer"] ~= "") then + -- no actions shall be executed + local o_id = allowed["autoanswer"] + local effects = active_dialog.d_options[o_id].o_results + -- execute all effects/results + local res = yl_speak_up.execute_all_relevant_effects(player, effects, o_id, true) + local target_dialog = res.next_dialog + yl_speak_up.speak_to[pname].o_id = nil + yl_speak_up.speak_to[pname].a_id = nil + if(not(target_dialog) + or target_dialog == "" + or not(dialog.n_dialogs[target_dialog])) then + target_dialog = yl_speak_up.speak_to[pname].d_id + end + -- show the new target dialog and exit + -- the recursion_depth will be increased by one (we did autoselect here and need to + -- avoid infinite loops) + return yl_speak_up.get_fs_talkdialog(player, n_id, target_dialog, res.alternate_text, + recursion_depth + 1) + end + + -- is the player comming back from trying to offer something to the NPC? + -- And is the NPC trying to return the item? + if(not(edit_mode) and d_id == "d_got_item") then + local pname = player:get_player_name() + local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname}) + if(not(trade_inv:is_empty("npc_wants"))) then + return "size[8,1.5]".. + "label[0.2,-0.2;".. + minetest.formspec_escape(dialog.n_npc or "- ? -").. + " does not seem to be intrested.\n".. + "Please take your item back and try something else.]".. + "button[2,1.0;1.5,0.9;show_player_offers_item;Ok]" + end + end + + yl_speak_up.speak_to[pname].allowed = allowed + + local portrait = yl_speak_up.calculate_portrait(pname, n_id) + + local formspec_v = minetest.get_player_information(pname).formspec_version + local protocol_v = minetest.get_player_information(pname).protocol_version + local formspec = {} + local h + + -- show who owns the NPC (and is thus more or less responsible for what it says) + local owner_info = "" + if(yl_speak_up.npc_owner[ n_id ]) then + owner_info = "\n\n(owned by "..minetest.formspec_escape(yl_speak_up.npc_owner[ n_id ])..")" + end + + if formspec_v >= 4 then + formspec = { + "formspec_version[3]", + "size[57,33]", + "position[0,0.45]", + "anchor[0,0.45]", + "no_prepend[]", + "bgcolor[#00000000;false]", + -- Container + + "container[2,0.75]", + -- Background + + "background[0,0;20,23;yl_speak_up_bg_dialog.png;false]", + "background[0,24;54.5,7.5;yl_speak_up_bg_dialog.png;false]", + -- Frame Dialog + + "image[-0.25,-0.25;1,1;yl_speak_up_bg_dialog_tl.png]", + "image[-0.25,22.25;1,1;yl_speak_up_bg_dialog_bl.png]", + "image[19.25,-0.25;1,1;yl_speak_up_bg_dialog_tr.png]", + "image[19.25,22.25;1,1;yl_speak_up_bg_dialog_br.png]", + "image[-0.25,0.75;1,21.5;yl_speak_up_bg_dialog_hl.png]", + "image[19.25,0.75;1,21.5;yl_speak_up_bg_dialog_hr.png]", + "image[0.75,-0.25;18.5,1;yl_speak_up_bg_dialog_vt.png]", + "image[0.75,22.25;18.5,1;yl_speak_up_bg_dialog_vb.png]", + -- Frame Options + + "image[-0.25,23.75;1,1;yl_speak_up_bg_dialog_tl.png]", + "image[-0.25,30.75;1,1;yl_speak_up_bg_dialog_bl.png]", + "image[53.75,23.75;1,1;yl_speak_up_bg_dialog_tr.png]", + "image[53.75,30.75;1,1;yl_speak_up_bg_dialog_br.png]", + "image[-0.25,24.75;1,6;yl_speak_up_bg_dialog_hl.png]", + "image[53.75,24.75;1,6;yl_speak_up_bg_dialog_hr.png]", + "image[0.75,23.75;53,1;yl_speak_up_bg_dialog_vt.png]", + "image[0.75,30.75;53,1;yl_speak_up_bg_dialog_vb.png]", + "style_type[button;bgcolor=#a37e45]", + "style_type[button_exit;bgcolor=#a37e45]", -- Dialog + + --[[ + "background[-1,-1;22,25;yl_speak_up_bg_dialog2.png;false]", + "background[-1,23;58,10;yl_speak_up_bg_dialog2.png;false]", + "style_type[button;bgcolor=#a37e45]", + ]]-- + "label[0.3,0.6;", + minetest.formspec_escape(dialog.n_npc), + "]", + "label[0.3,1.8;", + minetest.formspec_escape(dialog.n_description)..owner_info, + "]", + "image[15.5,0.5;4,4;", + portrait, + "]", + } + + -- this is used to build a list of all available dialogs for a dropdown menu in edit mode + -- (only relevant in edit mode) + local dialog_list = yl_speak_up.text_new_dialog_id + -- find the right index for the dialog_list dropdown above + local d_id_to_dropdown_index = {} + + -- allow to change skin, wielded items etc. + if(edit_mode) then + table.insert(formspec, "button[15.75,3.5;3.5,0.9;edit_skin;Edit Skin]") + end + + -- display the window with the text the NPC is saying + if(edit_mode and dialog and dialog.n_dialogs) then + -- sort all dialogs by d_sort + local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs, "d_sort") + -- add buttons for previous/next dialog + for i, d in ipairs(sorted_list) do + -- build the list of available dialogs for the dropdown list(s) + dialog_list = dialog_list..","..minetest.formspec_escape(d) + if(d == c_d_id and sorted_list[ i-1 ]) then + local prev_dialog = minetest.formspec_escape(sorted_list[i-1]) + table.insert(formspec, "button[8.5,4.0;2,0.9;prev_dialog_"..prev_dialog..";<]") + table.insert(formspec, "tooltip[prev_dialog_"..prev_dialog.. + ";Go to previous dialog "..prev_dialog..".]") + end + if(d == c_d_id and sorted_list[ i+1 ]) then + local next_dialog = minetest.formspec_escape(sorted_list[i+1]) + table.insert(formspec, "button[11,4.0;2,0.9;next_dialog_"..next_dialog..";>]") + table.insert(formspec, "tooltip[next_dialog_"..next_dialog.. + ";Go to next dialog "..next_dialog..".]") + end + d_id_to_dropdown_index[d] = i + 1 + end + + table.insert(formspec, "label[0.2,4.6;Dialog:]") -- "..minetest.formspec_escape(c_d_id)..":]") + table.insert(formspec, "dropdown[3.0,4.0;5,1;d_id;"..dialog_list..";"..d_id_to_dropdown_index[c_d_id]..",]") + table.insert(formspec, "tooltip[3.0,4.0;5,1;Select the dialog you want to edit. Currently, dialog "..c_d_id.." is beeing displayed.;#FFFFFF;#000000]") + + -- add a "+" button for creating a new dialog + table.insert(formspec, "button[13.9,4.0;1,0.9;show_new_dialog;+]") + table.insert(formspec, "tooltip[show_new_dialog;Create a new dialog.]") + + -- allow to edit name and description of the NPC + table.insert(formspec, "button[13.4,0.3;2,0.9;button_edit_name_and_description;Edit]") + table.insert(formspec, "tooltip[button_edit_name_and_description;Edit name and description of your NPC.]") + table.insert(formspec, "button[15.7,0.3;2,0.9;button_save_dialog;Save]") + table.insert(formspec, "tooltip[button_save_dialog;Save this dialog.]") + + table.insert(formspec, "textarea[0.2,5;19.6,17.8;d_text;;") + table.insert(formspec, minetest.formspec_escape(active_dialog.d_text)) + table.insert(formspec, "]") + else + if(alternate_text) then + alternate_text = string.gsub(alternate_text, "%$TEXT%$", active_dialog.d_text) + end + -- replace $NPC_NAME$ etc. + local t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text( + (alternate_text or active_dialog.d_text), dialog, pname)) + table.insert(formspec, "hypertext[0.2,5;19.6,17.8;d_text;") + table.insert(formspec, t .. "\n") + table.insert(formspec, "]") + table.insert(formspec, "tooltip[d_text;") + table.insert(formspec, t:trim()) + table.insert(formspec, ";#000000;#FFFFFF]") + end + + table.insert(formspec, "scrollbaroptions[min=0;max=14;smallstep=1;largestep=2;arrows=show]") + table.insert(formspec, "scrollbar[0.2,24.2;0.2,7;vertical;scr0;0]") + table.insert(formspec, "scroll_container[0,24;56,7;scr0;vertical;1]") + h = -0.8 + + -- allow to delete entries that have no options later on + local anz_options = 0 + -- Let#s sort the options by o_sort + if active_dialog ~= nil and active_dialog.d_options ~= nil then + local sorted_buttons = {} + for _, ad_v in pairs(active_dialog.d_options) do + sorted_buttons[tonumber(ad_v.o_sort)] = ad_v + anz_options = anz_options + 1 + end + + for _, sb_v in pairs(sorted_buttons) do + local oid = minetest.formspec_escape(sb_v.o_id) + -- in edit_mode: show all options + if edit_mode then + h = h + 1 + -- add a button "o_:" that leads to an edit formspec for this option + table.insert(formspec, "button[2.3," .. h .. ";2,0.9;edit_option_" .. oid .. ";"..oid..":]") + -- add a tooltip "Edit target dialog, pre(C)onditions and (Ef)fects for option o_" + table.insert(formspec, "tooltip[edit_option_" .. oid .. ";Edit target dialog, pre(C)onditions and (Ef)fects for option "..oid..".]") + + -- find the right target dialog for this option (if it exists): + local target_dialog = nil + local results = active_dialog.d_options[sb_v.o_id].o_results + -- has this option more results/effects than just switching to another dialog? + local has_other_results = false + if(results ~= nil) then + for k, v in pairs(results) do + if v.r_type == "dialog" and dialog.n_dialogs[v.r_value] ~= nil then + -- there may be more than one in the data structure + target_dialog = v.r_value + elseif v.r_type ~= "dialog" then + has_other_results = true + end + end + end + if(target_dialog) then + -- add a button "-> d_" that leads to the target dialog (if one is set) + table.insert(formspec, "button[9.0," .. h .. ";1,0.9;button_" .. oid .. ";->]") + -- add a tooltip "Go to target dialog d_" + table.insert(formspec, "tooltip[button_" .. oid .. ";Go to target dialog "..minetest.formspec_escape(target_dialog).." that will be shown when this option ("..oid..") is selected.]") + + -- selecting an option this way MUST NOT execute the pre(C)onditions or (Ef)fects! + end + -- allow to set a new target dialog + table.insert(formspec, "dropdown[4.4,"..h..";4.7,1;d_id_"..oid..";"..dialog_list..";"..minetest.formspec_escape(d_id_to_dropdown_index[target_dialog] or "0")..",]") + -- add a tooltip "Change target dialog" + table.insert(formspec, "tooltip[4.4,"..h..";4.7,1;Change target dialog for option "..oid..".;#FFFFFF;#000000]") + + -- are there any prerequirements? + local prereq = active_dialog.d_options[sb_v.o_id].o_prerequisites + if(prereq and next(prereq)) then + table.insert(formspec, "button[0.5," .. h .. ";0.5,0.9;conditions_"..oid..";C]") + -- label: "There are pre(C)onditions required for showing this option. Display them." + table.insert(formspec, "tooltip[conditions_" .. oid .. ";There are pre(C)onditions required for showing this option. Display them.]") + end + if(has_other_results) then + table.insert(formspec, "button[1.6," .. h .. ";0.6,0.9;effects_"..oid..";Ef]") + -- label: "There are further (Ef)fects (apart from switching to a new dialog) + -- set for this option. Display them." + table.insert(formspec, "tooltip[effects_" .. oid .. ";".. + "There are further (Ef)fects (apart from switching\n".. + "to a new dialog) set for this option. Display them.]") + end + local actions = active_dialog.d_options[sb_v.o_id].actions + -- are there any actions defined? + if(actions and next(actions)) then + table.insert(formspec, "button[1.1," .. h .. ";0.5,0.9;effects_"..oid..";A]") + table.insert(formspec, "tooltip[effects_" .. oid .. ";".. + "There is an (A)ction (i.e. a trade) that will happen\n".. + "when switching to a new dialog. Display effects and\n".. + "trade of this option.]") + end + + -- show the actual text for the option + table.insert(formspec, "field[9.9," .. h .. ";44.4,0.9;text_option_" .. oid .. ";;".. + minetest.formspec_escape(sb_v.o_text_when_prerequisites_met).."]") + -- add a tooltip "Edit the text that is displayed on button o_." + table.insert(formspec, "tooltip[text_option_" .. oid .. ";Edit the text that is displayed on button "..oid..".]") + + -- normal mode: show an option if the prerequirements (if any are defined) are met + elseif allowed[sb_v.o_id] == true then + h = h + 1 + -- replace $NPC_NAME$ etc. + local t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text( + sb_v.o_text_when_prerequisites_met, dialog, pname)) + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. oid .. ";]") + table.insert( + formspec, + "tooltip[button_" .. oid .. ";" .. t .. "]" + ) + local l = h + 0.45 + table.insert(formspec, "label[0.7," .. l .. ";" .. t .. "]") + else + if sb_v.o_hide_when_prerequisites_not_met == "true" then + else + -- replace $NPC_NAME$ etc. + local t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text( + sb_v.o_text_when_prerequisites_not_met, dialog, pname)) + if + sb_v.o_grey_when_prerequisites_not_met == "true" and + sb_v.o_text_when_prerequisites_not_met == "" + then + h = h + 1 + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. oid .. ";]") + table.insert( + formspec, + "tooltip[button_" .. + oid .. + ";" .. yl_speak_up.message_button_option_prerequisites_not_met_default .. "]" + ) + local l = h + 0.45 + table.insert( + formspec, + "label[0.7," .. + l .. ";" .. yl_speak_up.message_button_option_prerequisites_not_met_default .. "]" + ) + table.insert(formspec, "box[0.5," .. h .. ";53.8,0.9;#BBBBBB]") + end + if + sb_v.o_grey_when_prerequisites_not_met == "true" and + sb_v.o_text_when_prerequisites_not_met ~= "" + then + h = h + 1 + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. oid .. ";]") + table.insert( + formspec, + "tooltip[button_" .. oid .. ";" .. t .. "]" + ) + local l = h + 0.45 + table.insert( + formspec, + "label[0.7," .. l .. ";" .. t .. "]" + ) + table.insert(formspec, "box[0.5," .. h .. ";53.8,0.9;#BBBBBB]") + end + if + sb_v.o_grey_when_prerequisites_not_met == "false" and + sb_v.o_text_when_prerequisites_not_met == "" + then + -- no hide, no grey, no text + end + if + sb_v.o_grey_when_prerequisites_not_met == "false" and + sb_v.o_text_when_prerequisites_not_met ~= "" + then + -- no grey, but text + h = h + 1 + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. oid .. ";]") + table.insert( + formspec, + "tooltip[button_" .. oid .. ";" .. t .. "]" + ) + local l = h + 0.45 + table.insert( + formspec, + "label[0.7," .. l .. ";" .. t .. "]" + ) + end + end + end + end + end + + -- add a "I want to give you something" button to the first dialog if the NPC accepts items + if(active_dialog and active_dialog.d_sort and tonumber(active_dialog.d_sort) == 0) then + local offer_item_add_text = "" + if(edit_mode) then + offer_item_add_text = minetest.formspec_escape("[dialog d_got_item] -> ") + end + -- show this in edit mode and when the NPC actually accepts items + if(edit_mode or dialog.n_dialogs["d_got_item"]) then + h = h + 1 + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;player_offers_item;]") + table.insert(formspec, "tooltip[player_offers_item;".. + "If you want to give something (items) to this NPC\n".. + "- either because he requested it or as a present -\n".. + "click here. The NPC will return items he doesn't want.]") + table.insert(formspec, "label[0.7,"..(h+0.45)..";"..offer_item_add_text.. + "I want to give you something.]") + end + end + + -- If in edit mode, add two new menu entries: "add new options" and "end edit mode". + if(edit_mode) then + -- chat option: "Add a new answer/option to this dialog." + h = h + 1 + if(anz_options < yl_speak_up.max_number_of_options_per_dialog) then + 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.]") + -- the amount of allowed options/answers has been reached + else + table.insert(formspec, "box[0.5,"..h..";53.8,0.9;#BBBBBB]") + table.insert(formspec, "label[0.7,"..(h+0.45)..";Maximum number of allowed answers/options ".. + "reached. No further options/answers can be added.]") + end + + -- chat option: "Delete this dialog." + h = h + 1 + if(active_dialog + and active_dialog.d_text == "" + and anz_options == 0) then + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;delete_this_empty_dialog;]") + table.insert(formspec, "tooltip[delete_this_empty_dialog;Dialogs can only be deleted ".. + "when they are empty and have no more options/answers. This is the case here, ".. + "so the dialog can be deleted.]") + table.insert(formspec, "label[0.7,"..(h+0.45)..";Delete this empty dialog.]") + -- (but only show this option if the dialog is empty) + else + table.insert(formspec, "box[0.5,"..h..";53.8,0.9;#BBBBBB]") + table.insert(formspec, "label[0.7,"..(h+0.45)..";If you want to delete this dialog, you ".. + "need to delete all options and its text first.]") + end + + -- chat option: "Show what points to this dialog." + h = h + 1 + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;show_what_points_to_this_dialog;]") + table.insert(formspec, "tooltip[show_what_points_to_this_dialog;".. + "Show which other dialog options or failed actions\n".. + "or effects lead the player to this dialog here.]") + table.insert(formspec, "label[0.7,"..(h+0.45)..";Show what points to this dialog.]") + + -- chat option: "Make this dialog the first one shown when starting to talk." + h = h + 1 + if(active_dialog and active_dialog.d_sort and tonumber(active_dialog.d_sort) ~= 0) then + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;make_first_option;]") + table.insert(formspec, "tooltip[make_first_option;The NPC has to start with one dialog ".. + "when he is right-clicked. Make this dialog the one shown.]") + table.insert(formspec, "label[0.7,"..(h+0.45)..";Make this dialog the first one shown ".. + "when starting a conversation.]") + -- (but only show this option if it's not already the first one) + else + table.insert(formspec, "box[0.5,"..h..";53.8,0.9;#BBBBBB]") + table.insert(formspec, "label[0.7,"..(h+0.45)..";This dialog will be shown whenever ".. + "a conversation is started.]") + end + + -- access the inventory of the NPC + h = h + 1 + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;show_inventory;]") + table.insert(formspec, "tooltip[show_inventory;Access and manage the inventory of the NPC. ".. + "This is used for adding trade items, getting collected payments and managing ".. + "quest items.]") + table.insert(formspec, "label[0.7,"..(h+0.45)..";".. + "Show your inventory (only accessible to owner)!]") + + -- chat option: Mute/Unmute NPC + h = h + 1 + local obj = yl_speak_up.speak_to[pname].obj + -- some precautions - someone else might have eliminated the NPC in the meantime + local luaentity = nil + if(obj) then + luaentity = obj:get_luaentity() + end + if(luaentity and luaentity.yl_speak_up.talk) then + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;mute_npc;]") + table.insert(formspec, "tooltip[mute_npc;The NPC will no longer show his dialogs ".. + "when he is right-clicked. This is useful while you edit the NPC and don't ".. + "want players to see unfinished entries and/or quests.]") + table.insert(formspec, "label[0.7,"..(h+0.45)..";State: Not muted. Stop talking to other ".. + "players while I give you new orders.]") + elseif(luaentity) then + -- unmute the NPC + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;un_mute_npc;]") + table.insert(formspec, "tooltip[un_mute_npc;The NPC will show his dialogs to other ".. + "players when he is right-clicked. This is the normal mode of operation. Choose ".. + "this when you are finished editing.]") + table.insert(formspec, "label[0.7,"..(h+0.45)..";State: You are currently muted. Talk to ".. + "anyone again who wants to talk to you.]") + end + + -- chat option: "That was all. I'm finished with giving you new orders. Remember them!" + h = h + 1 + table.insert(formspec, "button_exit[0.5," .. h .. ";53.8,0.9;button_end_edit_mode;]") + table.insert(formspec, "tooltip[button_end_edit_mode;Ends edit mode. From now on, your NPC will talk to you like he talks to other players. You can always give him new orders by entering edit mode again.]") + table.insert(formspec, "label[0.7,"..(h+0.45)..";That was all. I'm finished with giving you new orders. Remember them!]") + + -- Offer to enter edit mode if the player has the npc_talk_owner priv AND owns the npc. + -- The npc_master priv allows to edit all NPC. + elseif(yl_speak_up.may_edit_npc(player, n_id)) then + -- chat option: "I am your owner. I have new orders for you. + h = h + 1 + table.insert(formspec, "button_exit[0.5," .. h .. ";53.8,0.9;button_start_edit_mode;]") + table.insert(formspec, "tooltip[button_start_edit_mode;Enters edit mode. In this mode, you can edit the texts the NPC says and the answers that can be given.]") + table.insert(formspec, "label[0.7,"..(h+0.45)..";I am your owner. I have new orders for you.]") + end + + -- add a Let's trade button to the first dialog if the NPC has trades + if(active_dialog and active_dialog.d_sort and tonumber(active_dialog.d_sort) == 0 + and dialog.trades) then + local has_trades = nil + for k, v in pairs(dialog.trades) do + -- has the NPC any *public* trades that are not effects/results? + if(not(v.hide) and not(v.d_id)) then + has_trades = true + break + end + end + if(has_trades) then + h = h + 1 + table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;show_trade_list;]") + table.insert(formspec, "tooltip[show_trade_list;Show a list of trades the NPC has to offer.]") + table.insert(formspec, "label[0.7,"..(h+0.45)..";Let's trade!]") + end + end + + + h = h + 1 + table.insert(formspec, "button_exit[0.5," .. h .. ";53.8,0.9;button_exit;]") + table.insert(formspec, "tooltip[button_exit;" .. yl_speak_up.message_button_option_exit .. "]") + local l = h + 0.45 + table.insert(formspec, "label[0.7," .. l .. ";" .. yl_speak_up.message_button_option_exit .. "]") + + table.insert(formspec, "scroll_container_end[]") + + table.insert(formspec, "container_end[]") + + -- if the player has an older formspec version + -- TODO: the version for older formspec versions lacks quite a lot - in particular regarding editing + else + minetest.log( + "info", + "[MOD] yl_speak_up: User " .. pname .. " talked to NPC ID n_" .. n_id .. " with an old formspec version!" + ) + + local upgrade_warning = "" + local max_number_of_buttons = yl_speak_up.max_number_of_buttons + local start_index = yl_speak_up.speak_to[pname].option_index + + if formspec_v < 3 or protocol_v < 39 then + local warn = { + "box[0.3,3;15,2;red]", + "label[0.7,3.2;", + yl_speak_up.text_version_warning, + "]" + } + upgrade_warning = table.concat(warn, "") + end + + if(alternate_text) then + alternate_text = string.gsub(alternate_text, "%$TEXT%$", active_dialog.d_text) + end + -- replace $NPC_NAME$ etc. + local t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text( + (alternate_text or active_dialog.d_text), dialog, pname)) + formspec = { + "formspec_version[1]", + "size[48,28]", + "position[0,0.45]", + "anchor[0,0.45]", + "no_prepend[]", + "bgcolor[#00000000;false]", + -- Container + + "container[2,0.75]", + -- Background + + "background[0,0;20,17;yl_speak_up_bg_dialog.png]", + "background[0,18;45,8;yl_speak_up_bg_dialog.png]", + -- Frame Dialog + + "image[-0.35,-0.35;1,1;yl_speak_up_bg_dialog_tl.png]", + "image[-0.35,16.25;1,1;yl_speak_up_bg_dialog_bl.png]", + "image[19.35,-0.35;1,1;yl_speak_up_bg_dialog_tr.png]", + "image[19.35,16.25;1,1;yl_speak_up_bg_dialog_br.png]", + "image[-0.35,0.35;1,19.5;yl_speak_up_bg_dialog_hl.png]", + "image[19.35,0.35;1,19.5;yl_speak_up_bg_dialog_hr.png]", + "image[0.35,-0.35;24.5,1;yl_speak_up_bg_dialog_vt.png]", + "image[0.35,16.25;24.5,1;yl_speak_up_bg_dialog_vb.png]", + -- Frame Options + + "image[-0.35,17.65;1,1;yl_speak_up_bg_dialog_tl.png]", + "image[-0.35,25.35;1,1;yl_speak_up_bg_dialog_bl.png]", + "image[44.35,17.65;1,1;yl_speak_up_bg_dialog_tr.png]", + "image[44.35,25.35;1,1;yl_speak_up_bg_dialog_br.png]", + "image[-0.35,18.35;1,8.5;yl_speak_up_bg_dialog_hl.png]", + "image[44.35,18.35;1,8.5;yl_speak_up_bg_dialog_hr.png]", + "image[0.35,17.65;56.5,1;yl_speak_up_bg_dialog_vt.png]", + "image[0.35,25.35;56.5,1;yl_speak_up_bg_dialog_vb.png]", + -- Upgrade Warning + + upgrade_warning, + -- Dialog + + "label[0.3,0.6;", + minetest.formspec_escape(dialog.n_npc), + "]", + "label[0.3,1.8;", + minetest.formspec_escape(dialog.n_description), + "]", + "image[15.5,0.5;4,4;", + portrait, + "]", + "textarea[0.5,5;19.6,13.5;;;", + t .. "\n", + "]", + "container[0,18]" + } + h = -1 + + if active_dialog ~= nil and active_dialog.d_options ~= nil then + -- Let's sort the options by o_sort. + local sorted_buttons = {} + for _, ad_v in pairs(active_dialog.d_options) do + sorted_buttons[tonumber(ad_v.o_sort)] = ad_v + end + + if #sorted_buttons > max_number_of_buttons then + -- Generate arrows + table.insert(formspec, "button[0.1,0;1,0.9;button_down;^]") + table.insert(formspec, "button[0.1,7.0;1,0.9;button_up;v]") + end + + local counter = 1 + + for _, sb_v in pairs(sorted_buttons) do + local end_index = start_index + max_number_of_buttons + if counter >= start_index and counter < end_index then + local oid = minetest.formspec_escape(sb_v.o_id) + if allowed[sb_v.o_id] == true then + h = h + 1 + table.insert( + formspec, + "button[1," .. + h .. ";44,0.9;button_" .. oid .. ";" .. minetest.formspec_escape(sb_v.o_text_when_prerequisites_met) .. "]" + ) + table.insert( + formspec, + "tooltip[button_" .. oid .. ";" .. minetest.formspec_escape(sb_v.o_text_when_prerequisites_met) .. "]" + ) + else + if sb_v.o_hide_when_prerequisites_not_met == "true" then + -- hide! Nothing to do + else + if + sb_v.o_grey_when_prerequisites_not_met == "true" and + sb_v.o_text_when_prerequisites_not_met == "" + then + h = h + 1 + local l = h - 0.2 + table.insert(formspec, "box[1," .. l .. ";43,1;#BBBBBB]") + table.insert( + formspec, + "label[3," .. + h .. + ";" .. + yl_speak_up.message_button_option_prerequisites_not_met_default .. "]" + ) + end + if + sb_v.o_grey_when_prerequisites_not_met == "true" and + sb_v.o_text_when_prerequisites_not_met ~= "" + then + h = h + 1 + local l = h - 0.2 + table.insert(formspec, "box[1," .. l .. ";43,1;#BBBBBB]") + table.insert( + formspec, + "label[3," .. + h .. + ";" .. + yl_speak_up.message_button_option_prerequisites_not_met_default .. + " : " .. minetest.formspec_escape(sb_v.o_text_when_prerequisites_not_met) .. "]" + ) + end + if + sb_v.o_grey_when_prerequisites_not_met == "false" and + sb_v.o_text_when_prerequisites_not_met == "" + then + -- no hide, no grey, no text + end + if + sb_v.o_grey_when_prerequisites_not_met == "false" and + sb_v.o_text_when_prerequisites_not_met ~= "" + then + h = h + 1 + table.insert( + formspec, + "button[1," .. + h .. + ";44,0.9;button_" .. + oid .. ";" .. minetest.formspec_escape(sb_v.o_text_when_prerequisites_not_met) .. "]" + ) + table.insert( + formspec, + "tooltip[button_" .. + oid .. ";" .. minetest.formspec_escape(sb_v.o_text_when_prerequisites_not_met) .. "]" + ) + end + end + end + end + + counter = counter + 1 + end + end + h = h + 1 + table.insert( + formspec, + "button_exit[1," .. h .. ";44,0.9;button_exit;" .. yl_speak_up.message_button_option_exit .. "]" + ) + table.insert(formspec, "tooltip[button_exit;" .. yl_speak_up.message_button_option_exit .. "]") + + table.insert(formspec, "container_end[]") + table.insert(formspec, "container_end[]") + end + + return table.concat(formspec, "") +end + diff --git a/functions.lua b/functions.lua index 140486c..92e2ff8 100644 --- a/functions.lua +++ b/functions.lua @@ -213,16 +213,6 @@ yl_speak_up.delete_dialog = function(n_id, d_id) end -local function calculate_portrait(pname, n_id) - local tex = yl_speak_up.speak_to[pname].textures - - local head = "" - - head = head .. "[combine:8x8:-8,-8=" .. tex[2] .. ":-40,-8=" .. tex[2] - - return head -end - --### --Formspecs --### @@ -231,777 +221,6 @@ end -- talk --- recursion_depth is increased each time autoanswer is automaticly selected -yl_speak_up.get_fs_talkdialog = function(player, n_id, d_id, alternate_text, recursion_depth) - local pname = player:get_player_name() - local dialog = yl_speak_up.speak_to[pname].dialog - local context_d_id = yl_speak_up.speak_to[pname].d_id - local active_dialog - - if not player and not player:is_player() then - minetest.log( - "action", - "[MOD] yl_speak_up: User " .. - pname .. - " talked to unconfigured NPC with ID n_" .. - n_id .. ", position of user was " .. minetest.pos_to_string(player:get_pos(), 0) - ) - return yl_speak_up.get_error_message() - end - - -- currently no trade running (we're editing options) - yl_speak_up.trade[pname] = nil - yl_speak_up.speak_to[pname].trade_id = nil - - --[[ If we have an explicit call for a certain d_id, we grab it from parameters. - If not, we grab in from context. - When neither are present, we grab it from d_sort - ]]-- - - local c_d_id - - if d_id ~= nil then - active_dialog = dialog.n_dialogs[d_id] - c_d_id = d_id - elseif yl_speak_up.speak_to[pname].d_id ~= nil then - c_d_id = yl_speak_up.speak_to[pname].d_id - active_dialog = dialog.n_dialogs[c_d_id] - elseif dialog.n_dialogs ~= nil then - -- Find the dialog with d_sort = 0 - local lowest_sort = nil - for k, v in pairs(dialog.n_dialogs) do - if tonumber(v.d_sort) ~= nil then - if not lowest_sort or tonumber(v.d_sort) < tonumber(lowest_sort) or v.d_sort == "0" then - if tonumber(v.d_sort) >= 0 then - lowest_sort = v.d_sort - active_dialog = v - c_d_id = k - end - end - end - end - else - -- it may be possible that this player can initialize this npc - minetest.log( - "action", - "[MOD] yl_speak_up: User " .. - pname .. - " talked to unconfigured NPC with ID n_" .. - n_id .. ", position of user was " .. minetest.pos_to_string(player:get_pos(), 0) - ) - -- this is the initial config - -- (input ends up at yl_speak_up.input_talk and needs to be rerouted) - return yl_speak_up.get_fs_initial_config(player, n_id, d_id, true) - end - - if c_d_id == nil then return yl_speak_up.get_error_message() end - - yl_speak_up.speak_to[pname].d_id = c_d_id - - -- Now we have a dialog to display to the user - - -- do not crash in case of error - if(not(active_dialog)) then - return "size[6,2]".. - "label[0.2,0.5;Ups! Something went wrong. Please try again.]" - 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) - - -- evaluate the preconditions of each option and check if the option can be offered - local allowed = yl_speak_up.calculate_displayable_options(pname, active_dialog.d_options, edit_mode, - -- avoid loops by limiting max recoursion depths for autoanswers - (recursion_depth < yl_speak_up.max_allowed_recursion_depth)) - -- abort here if needed - the autoanswer/autoselection did choose an option for us alread - if(not(edit_mode) and allowed and allowed["autoanswer"] and allowed["autoanswer"] ~= "") then - -- no actions shall be executed - local o_id = allowed["autoanswer"] - local effects = active_dialog.d_options[o_id].o_results - -- execute all effects/results - local res = yl_speak_up.execute_all_relevant_effects(player, effects, o_id, true) - local target_dialog = res.next_dialog - yl_speak_up.speak_to[pname].o_id = nil - yl_speak_up.speak_to[pname].a_id = nil - if(not(target_dialog) - or target_dialog == "" - or not(dialog.n_dialogs[target_dialog])) then - target_dialog = yl_speak_up.speak_to[pname].d_id - end - -- show the new target dialog and exit - -- the recursion_depth will be increased by one (we did autoselect here and need to - -- avoid infinite loops) - return yl_speak_up.get_fs_talkdialog(player, n_id, target_dialog, res.alternate_text, - recursion_depth + 1) - end - - -- is the player comming back from trying to offer something to the NPC? - -- And is the NPC trying to return the item? - if(not(edit_mode) and d_id == "d_got_item") then - local pname = player:get_player_name() - local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname}) - if(not(trade_inv:is_empty("npc_wants"))) then - return "size[8,1.5]".. - "label[0.2,-0.2;".. - minetest.formspec_escape(dialog.n_npc or "- ? -").. - " does not seem to be intrested.\n".. - "Please take your item back and try something else.]".. - "button[2,1.0;1.5,0.9;show_player_offers_item;Ok]" - end - end - - yl_speak_up.speak_to[pname].allowed = allowed - - local portrait = calculate_portrait(pname, n_id) - - local formspec_v = minetest.get_player_information(pname).formspec_version - local protocol_v = minetest.get_player_information(pname).protocol_version - local formspec = {} - local h - - -- show who owns the NPC (and is thus more or less responsible for what it says) - local owner_info = "" - if(yl_speak_up.npc_owner[ n_id ]) then - owner_info = "\n\n(owned by "..minetest.formspec_escape(yl_speak_up.npc_owner[ n_id ])..")" - end - - if formspec_v >= 4 then - formspec = { - "formspec_version[3]", - "size[57,33]", - "position[0,0.45]", - "anchor[0,0.45]", - "no_prepend[]", - "bgcolor[#00000000;false]", - -- Container - - "container[2,0.75]", - -- Background - - "background[0,0;20,23;yl_speak_up_bg_dialog.png;false]", - "background[0,24;54.5,7.5;yl_speak_up_bg_dialog.png;false]", - -- Frame Dialog - - "image[-0.25,-0.25;1,1;yl_speak_up_bg_dialog_tl.png]", - "image[-0.25,22.25;1,1;yl_speak_up_bg_dialog_bl.png]", - "image[19.25,-0.25;1,1;yl_speak_up_bg_dialog_tr.png]", - "image[19.25,22.25;1,1;yl_speak_up_bg_dialog_br.png]", - "image[-0.25,0.75;1,21.5;yl_speak_up_bg_dialog_hl.png]", - "image[19.25,0.75;1,21.5;yl_speak_up_bg_dialog_hr.png]", - "image[0.75,-0.25;18.5,1;yl_speak_up_bg_dialog_vt.png]", - "image[0.75,22.25;18.5,1;yl_speak_up_bg_dialog_vb.png]", - -- Frame Options - - "image[-0.25,23.75;1,1;yl_speak_up_bg_dialog_tl.png]", - "image[-0.25,30.75;1,1;yl_speak_up_bg_dialog_bl.png]", - "image[53.75,23.75;1,1;yl_speak_up_bg_dialog_tr.png]", - "image[53.75,30.75;1,1;yl_speak_up_bg_dialog_br.png]", - "image[-0.25,24.75;1,6;yl_speak_up_bg_dialog_hl.png]", - "image[53.75,24.75;1,6;yl_speak_up_bg_dialog_hr.png]", - "image[0.75,23.75;53,1;yl_speak_up_bg_dialog_vt.png]", - "image[0.75,30.75;53,1;yl_speak_up_bg_dialog_vb.png]", - "style_type[button;bgcolor=#a37e45]", - "style_type[button_exit;bgcolor=#a37e45]", -- Dialog - - --[[ - "background[-1,-1;22,25;yl_speak_up_bg_dialog2.png;false]", - "background[-1,23;58,10;yl_speak_up_bg_dialog2.png;false]", - "style_type[button;bgcolor=#a37e45]", - ]]-- - "label[0.3,0.6;", - minetest.formspec_escape(dialog.n_npc), - "]", - "label[0.3,1.8;", - minetest.formspec_escape(dialog.n_description)..owner_info, - "]", - "image[15.5,0.5;4,4;", - portrait, - "]", - } - - -- this is used to build a list of all available dialogs for a dropdown menu in edit mode - -- (only relevant in edit mode) - local dialog_list = yl_speak_up.text_new_dialog_id - -- find the right index for the dialog_list dropdown above - local d_id_to_dropdown_index = {} - - -- allow to change skin, wielded items etc. - if(edit_mode) then - table.insert(formspec, "button[15.75,3.5;3.5,0.9;edit_skin;Edit Skin]") - end - - -- display the window with the text the NPC is saying - if(edit_mode and dialog and dialog.n_dialogs) then - -- sort all dialogs by d_sort - local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs, "d_sort") - -- add buttons for previous/next dialog - for i, d in ipairs(sorted_list) do - -- build the list of available dialogs for the dropdown list(s) - dialog_list = dialog_list..","..minetest.formspec_escape(d) - if(d == c_d_id and sorted_list[ i-1 ]) then - local prev_dialog = minetest.formspec_escape(sorted_list[i-1]) - table.insert(formspec, "button[8.5,4.0;2,0.9;prev_dialog_"..prev_dialog..";<]") - table.insert(formspec, "tooltip[prev_dialog_"..prev_dialog.. - ";Go to previous dialog "..prev_dialog..".]") - end - if(d == c_d_id and sorted_list[ i+1 ]) then - local next_dialog = minetest.formspec_escape(sorted_list[i+1]) - table.insert(formspec, "button[11,4.0;2,0.9;next_dialog_"..next_dialog..";>]") - table.insert(formspec, "tooltip[next_dialog_"..next_dialog.. - ";Go to next dialog "..next_dialog..".]") - end - d_id_to_dropdown_index[d] = i + 1 - end - - table.insert(formspec, "label[0.2,4.6;Dialog:]") -- "..minetest.formspec_escape(c_d_id)..":]") - table.insert(formspec, "dropdown[3.0,4.0;5,1;d_id;"..dialog_list..";"..d_id_to_dropdown_index[c_d_id]..",]") - table.insert(formspec, "tooltip[3.0,4.0;5,1;Select the dialog you want to edit. Currently, dialog "..c_d_id.." is beeing displayed.;#FFFFFF;#000000]") - - -- add a "+" button for creating a new dialog - table.insert(formspec, "button[13.9,4.0;1,0.9;show_new_dialog;+]") - table.insert(formspec, "tooltip[show_new_dialog;Create a new dialog.]") - - -- allow to edit name and description of the NPC - table.insert(formspec, "button[13.4,0.3;2,0.9;button_edit_name_and_description;Edit]") - table.insert(formspec, "tooltip[button_edit_name_and_description;Edit name and description of your NPC.]") - table.insert(formspec, "button[15.7,0.3;2,0.9;button_save_dialog;Save]") - table.insert(formspec, "tooltip[button_save_dialog;Save this dialog.]") - - table.insert(formspec, "textarea[0.2,5;19.6,17.8;d_text;;") - table.insert(formspec, minetest.formspec_escape(active_dialog.d_text)) - table.insert(formspec, "]") - else - if(alternate_text) then - alternate_text = string.gsub(alternate_text, "%$TEXT%$", active_dialog.d_text) - end - -- replace $NPC_NAME$ etc. - local t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text( - (alternate_text or active_dialog.d_text), dialog, pname)) - table.insert(formspec, "hypertext[0.2,5;19.6,17.8;d_text;") - table.insert(formspec, t .. "\n") - table.insert(formspec, "]") - table.insert(formspec, "tooltip[d_text;") - table.insert(formspec, t:trim()) - table.insert(formspec, ";#000000;#FFFFFF]") - end - - table.insert(formspec, "scrollbaroptions[min=0;max=14;smallstep=1;largestep=2;arrows=show]") - table.insert(formspec, "scrollbar[0.2,24.2;0.2,7;vertical;scr0;0]") - table.insert(formspec, "scroll_container[0,24;56,7;scr0;vertical;1]") - h = -0.8 - - -- allow to delete entries that have no options later on - local anz_options = 0 - -- Let#s sort the options by o_sort - if active_dialog ~= nil and active_dialog.d_options ~= nil then - local sorted_buttons = {} - for _, ad_v in pairs(active_dialog.d_options) do - sorted_buttons[tonumber(ad_v.o_sort)] = ad_v - anz_options = anz_options + 1 - end - - for _, sb_v in pairs(sorted_buttons) do - local oid = minetest.formspec_escape(sb_v.o_id) - -- in edit_mode: show all options - if edit_mode then - h = h + 1 - -- add a button "o_:" that leads to an edit formspec for this option - table.insert(formspec, "button[2.3," .. h .. ";2,0.9;edit_option_" .. oid .. ";"..oid..":]") - -- add a tooltip "Edit target dialog, pre(C)onditions and (Ef)fects for option o_" - table.insert(formspec, "tooltip[edit_option_" .. oid .. ";Edit target dialog, pre(C)onditions and (Ef)fects for option "..oid..".]") - - -- find the right target dialog for this option (if it exists): - local target_dialog = nil - local results = active_dialog.d_options[sb_v.o_id].o_results - -- has this option more results/effects than just switching to another dialog? - local has_other_results = false - if(results ~= nil) then - for k, v in pairs(results) do - if v.r_type == "dialog" and dialog.n_dialogs[v.r_value] ~= nil then - -- there may be more than one in the data structure - target_dialog = v.r_value - elseif v.r_type ~= "dialog" then - has_other_results = true - end - end - end - if(target_dialog) then - -- add a button "-> d_" that leads to the target dialog (if one is set) - table.insert(formspec, "button[9.0," .. h .. ";1,0.9;button_" .. oid .. ";->]") - -- add a tooltip "Go to target dialog d_" - table.insert(formspec, "tooltip[button_" .. oid .. ";Go to target dialog "..minetest.formspec_escape(target_dialog).." that will be shown when this option ("..oid..") is selected.]") - - -- selecting an option this way MUST NOT execute the pre(C)onditions or (Ef)fects! - end - -- allow to set a new target dialog - table.insert(formspec, "dropdown[4.4,"..h..";4.7,1;d_id_"..oid..";"..dialog_list..";"..minetest.formspec_escape(d_id_to_dropdown_index[target_dialog] or "0")..",]") - -- add a tooltip "Change target dialog" - table.insert(formspec, "tooltip[4.4,"..h..";4.7,1;Change target dialog for option "..oid..".;#FFFFFF;#000000]") - - -- are there any prerequirements? - local prereq = active_dialog.d_options[sb_v.o_id].o_prerequisites - if(prereq and next(prereq)) then - table.insert(formspec, "button[0.5," .. h .. ";0.5,0.9;conditions_"..oid..";C]") - -- label: "There are pre(C)onditions required for showing this option. Display them." - table.insert(formspec, "tooltip[conditions_" .. oid .. ";There are pre(C)onditions required for showing this option. Display them.]") - end - if(has_other_results) then - table.insert(formspec, "button[1.6," .. h .. ";0.6,0.9;effects_"..oid..";Ef]") - -- label: "There are further (Ef)fects (apart from switching to a new dialog) - -- set for this option. Display them." - table.insert(formspec, "tooltip[effects_" .. oid .. ";".. - "There are further (Ef)fects (apart from switching\n".. - "to a new dialog) set for this option. Display them.]") - end - local actions = active_dialog.d_options[sb_v.o_id].actions - -- are there any actions defined? - if(actions and next(actions)) then - table.insert(formspec, "button[1.1," .. h .. ";0.5,0.9;effects_"..oid..";A]") - table.insert(formspec, "tooltip[effects_" .. oid .. ";".. - "There is an (A)ction (i.e. a trade) that will happen\n".. - "when switching to a new dialog. Display effects and\n".. - "trade of this option.]") - end - - -- show the actual text for the option - table.insert(formspec, "field[9.9," .. h .. ";44.4,0.9;text_option_" .. oid .. ";;".. - minetest.formspec_escape(sb_v.o_text_when_prerequisites_met).."]") - -- add a tooltip "Edit the text that is displayed on button o_." - table.insert(formspec, "tooltip[text_option_" .. oid .. ";Edit the text that is displayed on button "..oid..".]") - - -- normal mode: show an option if the prerequirements (if any are defined) are met - elseif allowed[sb_v.o_id] == true then - h = h + 1 - -- replace $NPC_NAME$ etc. - local t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text( - sb_v.o_text_when_prerequisites_met, dialog, pname)) - table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. oid .. ";]") - table.insert( - formspec, - "tooltip[button_" .. oid .. ";" .. t .. "]" - ) - local l = h + 0.45 - table.insert(formspec, "label[0.7," .. l .. ";" .. t .. "]") - else - if sb_v.o_hide_when_prerequisites_not_met == "true" then - else - -- replace $NPC_NAME$ etc. - local t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text( - sb_v.o_text_when_prerequisites_not_met, dialog, pname)) - if - sb_v.o_grey_when_prerequisites_not_met == "true" and - sb_v.o_text_when_prerequisites_not_met == "" - then - h = h + 1 - table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. oid .. ";]") - table.insert( - formspec, - "tooltip[button_" .. - oid .. - ";" .. yl_speak_up.message_button_option_prerequisites_not_met_default .. "]" - ) - local l = h + 0.45 - table.insert( - formspec, - "label[0.7," .. - l .. ";" .. yl_speak_up.message_button_option_prerequisites_not_met_default .. "]" - ) - table.insert(formspec, "box[0.5," .. h .. ";53.8,0.9;#BBBBBB]") - end - if - sb_v.o_grey_when_prerequisites_not_met == "true" and - sb_v.o_text_when_prerequisites_not_met ~= "" - then - h = h + 1 - table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. oid .. ";]") - table.insert( - formspec, - "tooltip[button_" .. oid .. ";" .. t .. "]" - ) - local l = h + 0.45 - table.insert( - formspec, - "label[0.7," .. l .. ";" .. t .. "]" - ) - table.insert(formspec, "box[0.5," .. h .. ";53.8,0.9;#BBBBBB]") - end - if - sb_v.o_grey_when_prerequisites_not_met == "false" and - sb_v.o_text_when_prerequisites_not_met == "" - then - -- no hide, no grey, no text - end - if - sb_v.o_grey_when_prerequisites_not_met == "false" and - sb_v.o_text_when_prerequisites_not_met ~= "" - then - -- no grey, but text - h = h + 1 - table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. oid .. ";]") - table.insert( - formspec, - "tooltip[button_" .. oid .. ";" .. t .. "]" - ) - local l = h + 0.45 - table.insert( - formspec, - "label[0.7," .. l .. ";" .. t .. "]" - ) - end - end - end - end - end - - -- add a "I want to give you something" button to the first dialog if the NPC accepts items - if(active_dialog and active_dialog.d_sort and tonumber(active_dialog.d_sort) == 0) then - local offer_item_add_text = "" - if(edit_mode) then - offer_item_add_text = minetest.formspec_escape("[dialog d_got_item] -> ") - end - -- show this in edit mode and when the NPC actually accepts items - if(edit_mode or dialog.n_dialogs["d_got_item"]) then - h = h + 1 - table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;player_offers_item;]") - table.insert(formspec, "tooltip[player_offers_item;".. - "If you want to give something (items) to this NPC\n".. - "- either because he requested it or as a present -\n".. - "click here. The NPC will return items he doesn't want.]") - table.insert(formspec, "label[0.7,"..(h+0.45)..";"..offer_item_add_text.. - "I want to give you something.]") - end - end - - -- If in edit mode, add two new menu entries: "add new options" and "end edit mode". - if(edit_mode) then - -- chat option: "Add a new answer/option to this dialog." - h = h + 1 - if(anz_options < yl_speak_up.max_number_of_options_per_dialog) then - 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.]") - -- the amount of allowed options/answers has been reached - else - table.insert(formspec, "box[0.5,"..h..";53.8,0.9;#BBBBBB]") - table.insert(formspec, "label[0.7,"..(h+0.45)..";Maximum number of allowed answers/options ".. - "reached. No further options/answers can be added.]") - end - - -- chat option: "Delete this dialog." - h = h + 1 - if(active_dialog - and active_dialog.d_text == "" - and anz_options == 0) then - table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;delete_this_empty_dialog;]") - table.insert(formspec, "tooltip[delete_this_empty_dialog;Dialogs can only be deleted ".. - "when they are empty and have no more options/answers. This is the case here, ".. - "so the dialog can be deleted.]") - table.insert(formspec, "label[0.7,"..(h+0.45)..";Delete this empty dialog.]") - -- (but only show this option if the dialog is empty) - else - table.insert(formspec, "box[0.5,"..h..";53.8,0.9;#BBBBBB]") - table.insert(formspec, "label[0.7,"..(h+0.45)..";If you want to delete this dialog, you ".. - "need to delete all options and its text first.]") - end - - -- chat option: "Show what points to this dialog." - h = h + 1 - table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;show_what_points_to_this_dialog;]") - table.insert(formspec, "tooltip[show_what_points_to_this_dialog;".. - "Show which other dialog options or failed actions\n".. - "or effects lead the player to this dialog here.]") - table.insert(formspec, "label[0.7,"..(h+0.45)..";Show what points to this dialog.]") - - -- chat option: "Make this dialog the first one shown when starting to talk." - h = h + 1 - if(active_dialog and active_dialog.d_sort and tonumber(active_dialog.d_sort) ~= 0) then - table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;make_first_option;]") - table.insert(formspec, "tooltip[make_first_option;The NPC has to start with one dialog ".. - "when he is right-clicked. Make this dialog the one shown.]") - table.insert(formspec, "label[0.7,"..(h+0.45)..";Make this dialog the first one shown ".. - "when starting a conversation.]") - -- (but only show this option if it's not already the first one) - else - table.insert(formspec, "box[0.5,"..h..";53.8,0.9;#BBBBBB]") - table.insert(formspec, "label[0.7,"..(h+0.45)..";This dialog will be shown whenever ".. - "a conversation is started.]") - end - - -- access the inventory of the NPC - h = h + 1 - table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;show_inventory;]") - table.insert(formspec, "tooltip[show_inventory;Access and manage the inventory of the NPC. ".. - "This is used for adding trade items, getting collected payments and managing ".. - "quest items.]") - table.insert(formspec, "label[0.7,"..(h+0.45)..";".. - "Show your inventory (only accessible to owner)!]") - - -- chat option: Mute/Unmute NPC - h = h + 1 - local obj = yl_speak_up.speak_to[pname].obj - -- some precautions - someone else might have eliminated the NPC in the meantime - local luaentity = nil - if(obj) then - luaentity = obj:get_luaentity() - end - if(luaentity and luaentity.yl_speak_up.talk) then - table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;mute_npc;]") - table.insert(formspec, "tooltip[mute_npc;The NPC will no longer show his dialogs ".. - "when he is right-clicked. This is useful while you edit the NPC and don't ".. - "want players to see unfinished entries and/or quests.]") - table.insert(formspec, "label[0.7,"..(h+0.45)..";State: Not muted. Stop talking to other ".. - "players while I give you new orders.]") - elseif(luaentity) then - -- unmute the NPC - table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;un_mute_npc;]") - table.insert(formspec, "tooltip[un_mute_npc;The NPC will show his dialogs to other ".. - "players when he is right-clicked. This is the normal mode of operation. Choose ".. - "this when you are finished editing.]") - table.insert(formspec, "label[0.7,"..(h+0.45)..";State: You are currently muted. Talk to ".. - "anyone again who wants to talk to you.]") - end - - -- chat option: "That was all. I'm finished with giving you new orders. Remember them!" - h = h + 1 - table.insert(formspec, "button_exit[0.5," .. h .. ";53.8,0.9;button_end_edit_mode;]") - table.insert(formspec, "tooltip[button_end_edit_mode;Ends edit mode. From now on, your NPC will talk to you like he talks to other players. You can always give him new orders by entering edit mode again.]") - table.insert(formspec, "label[0.7,"..(h+0.45)..";That was all. I'm finished with giving you new orders. Remember them!]") - - -- Offer to enter edit mode if the player has the npc_talk_owner priv AND owns the npc. - -- The npc_master priv allows to edit all NPC. - elseif(yl_speak_up.may_edit_npc(player, n_id)) then - -- chat option: "I am your owner. I have new orders for you. - h = h + 1 - table.insert(formspec, "button_exit[0.5," .. h .. ";53.8,0.9;button_start_edit_mode;]") - table.insert(formspec, "tooltip[button_start_edit_mode;Enters edit mode. In this mode, you can edit the texts the NPC says and the answers that can be given.]") - table.insert(formspec, "label[0.7,"..(h+0.45)..";I am your owner. I have new orders for you.]") - end - - -- add a Let's trade button to the first dialog if the NPC has trades - if(active_dialog and active_dialog.d_sort and tonumber(active_dialog.d_sort) == 0 - and dialog.trades) then - local has_trades = nil - for k, v in pairs(dialog.trades) do - -- has the NPC any *public* trades that are not effects/results? - if(not(v.hide) and not(v.d_id)) then - has_trades = true - break - end - end - if(has_trades) then - h = h + 1 - table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;show_trade_list;]") - table.insert(formspec, "tooltip[show_trade_list;Show a list of trades the NPC has to offer.]") - table.insert(formspec, "label[0.7,"..(h+0.45)..";Let's trade!]") - end - end - - - h = h + 1 - table.insert(formspec, "button_exit[0.5," .. h .. ";53.8,0.9;button_exit;]") - table.insert(formspec, "tooltip[button_exit;" .. yl_speak_up.message_button_option_exit .. "]") - local l = h + 0.45 - table.insert(formspec, "label[0.7," .. l .. ";" .. yl_speak_up.message_button_option_exit .. "]") - - table.insert(formspec, "scroll_container_end[]") - - table.insert(formspec, "container_end[]") - - -- if the player has an older formspec version - -- TODO: the version for older formspec versions lacks quite a lot - in particular regarding editing - else - minetest.log( - "info", - "[MOD] yl_speak_up: User " .. pname .. " talked to NPC ID n_" .. n_id .. " with an old formspec version!" - ) - - local upgrade_warning = "" - local max_number_of_buttons = yl_speak_up.max_number_of_buttons - local start_index = yl_speak_up.speak_to[pname].option_index - - if formspec_v < 3 or protocol_v < 39 then - local warn = { - "box[0.3,3;15,2;red]", - "label[0.7,3.2;", - yl_speak_up.text_version_warning, - "]" - } - upgrade_warning = table.concat(warn, "") - end - - if(alternate_text) then - alternate_text = string.gsub(alternate_text, "%$TEXT%$", active_dialog.d_text) - end - -- replace $NPC_NAME$ etc. - local t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text( - (alternate_text or active_dialog.d_text), dialog, pname)) - formspec = { - "formspec_version[1]", - "size[48,28]", - "position[0,0.45]", - "anchor[0,0.45]", - "no_prepend[]", - "bgcolor[#00000000;false]", - -- Container - - "container[2,0.75]", - -- Background - - "background[0,0;20,17;yl_speak_up_bg_dialog.png]", - "background[0,18;45,8;yl_speak_up_bg_dialog.png]", - -- Frame Dialog - - "image[-0.35,-0.35;1,1;yl_speak_up_bg_dialog_tl.png]", - "image[-0.35,16.25;1,1;yl_speak_up_bg_dialog_bl.png]", - "image[19.35,-0.35;1,1;yl_speak_up_bg_dialog_tr.png]", - "image[19.35,16.25;1,1;yl_speak_up_bg_dialog_br.png]", - "image[-0.35,0.35;1,19.5;yl_speak_up_bg_dialog_hl.png]", - "image[19.35,0.35;1,19.5;yl_speak_up_bg_dialog_hr.png]", - "image[0.35,-0.35;24.5,1;yl_speak_up_bg_dialog_vt.png]", - "image[0.35,16.25;24.5,1;yl_speak_up_bg_dialog_vb.png]", - -- Frame Options - - "image[-0.35,17.65;1,1;yl_speak_up_bg_dialog_tl.png]", - "image[-0.35,25.35;1,1;yl_speak_up_bg_dialog_bl.png]", - "image[44.35,17.65;1,1;yl_speak_up_bg_dialog_tr.png]", - "image[44.35,25.35;1,1;yl_speak_up_bg_dialog_br.png]", - "image[-0.35,18.35;1,8.5;yl_speak_up_bg_dialog_hl.png]", - "image[44.35,18.35;1,8.5;yl_speak_up_bg_dialog_hr.png]", - "image[0.35,17.65;56.5,1;yl_speak_up_bg_dialog_vt.png]", - "image[0.35,25.35;56.5,1;yl_speak_up_bg_dialog_vb.png]", - -- Upgrade Warning - - upgrade_warning, - -- Dialog - - "label[0.3,0.6;", - minetest.formspec_escape(dialog.n_npc), - "]", - "label[0.3,1.8;", - minetest.formspec_escape(dialog.n_description), - "]", - "image[15.5,0.5;4,4;", - portrait, - "]", - "textarea[0.5,5;19.6,13.5;;;", - t .. "\n", - "]", - "container[0,18]" - } - h = -1 - - if active_dialog ~= nil and active_dialog.d_options ~= nil then - -- Let's sort the options by o_sort. - local sorted_buttons = {} - for _, ad_v in pairs(active_dialog.d_options) do - sorted_buttons[tonumber(ad_v.o_sort)] = ad_v - end - - if #sorted_buttons > max_number_of_buttons then - -- Generate arrows - table.insert(formspec, "button[0.1,0;1,0.9;button_down;^]") - table.insert(formspec, "button[0.1,7.0;1,0.9;button_up;v]") - end - - local counter = 1 - - for _, sb_v in pairs(sorted_buttons) do - local end_index = start_index + max_number_of_buttons - if counter >= start_index and counter < end_index then - local oid = minetest.formspec_escape(sb_v.o_id) - if allowed[sb_v.o_id] == true then - h = h + 1 - table.insert( - formspec, - "button[1," .. - h .. ";44,0.9;button_" .. oid .. ";" .. minetest.formspec_escape(sb_v.o_text_when_prerequisites_met) .. "]" - ) - table.insert( - formspec, - "tooltip[button_" .. oid .. ";" .. minetest.formspec_escape(sb_v.o_text_when_prerequisites_met) .. "]" - ) - else - if sb_v.o_hide_when_prerequisites_not_met == "true" then - -- hide! Nothing to do - else - if - sb_v.o_grey_when_prerequisites_not_met == "true" and - sb_v.o_text_when_prerequisites_not_met == "" - then - h = h + 1 - local l = h - 0.2 - table.insert(formspec, "box[1," .. l .. ";43,1;#BBBBBB]") - table.insert( - formspec, - "label[3," .. - h .. - ";" .. - yl_speak_up.message_button_option_prerequisites_not_met_default .. "]" - ) - end - if - sb_v.o_grey_when_prerequisites_not_met == "true" and - sb_v.o_text_when_prerequisites_not_met ~= "" - then - h = h + 1 - local l = h - 0.2 - table.insert(formspec, "box[1," .. l .. ";43,1;#BBBBBB]") - table.insert( - formspec, - "label[3," .. - h .. - ";" .. - yl_speak_up.message_button_option_prerequisites_not_met_default .. - " : " .. minetest.formspec_escape(sb_v.o_text_when_prerequisites_not_met) .. "]" - ) - end - if - sb_v.o_grey_when_prerequisites_not_met == "false" and - sb_v.o_text_when_prerequisites_not_met == "" - then - -- no hide, no grey, no text - end - if - sb_v.o_grey_when_prerequisites_not_met == "false" and - sb_v.o_text_when_prerequisites_not_met ~= "" - then - h = h + 1 - table.insert( - formspec, - "button[1," .. - h .. - ";44,0.9;button_" .. - oid .. ";" .. minetest.formspec_escape(sb_v.o_text_when_prerequisites_not_met) .. "]" - ) - table.insert( - formspec, - "tooltip[button_" .. - oid .. ";" .. minetest.formspec_escape(sb_v.o_text_when_prerequisites_not_met) .. "]" - ) - end - end - end - end - - counter = counter + 1 - end - end - h = h + 1 - table.insert( - formspec, - "button_exit[1," .. h .. ";44,0.9;button_exit;" .. yl_speak_up.message_button_option_exit .. "]" - ) - table.insert(formspec, "tooltip[button_exit;" .. yl_speak_up.message_button_option_exit .. "]") - - table.insert(formspec, "container_end[]") - table.insert(formspec, "container_end[]") - end - - return table.concat(formspec, "") -end - -- receive fields @@ -1589,264 +808,6 @@ yl_speak_up.check_for_disambigous_results = function(n_id, pname) end -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 = "" - - local n_id = yl_speak_up.speak_to[pname].n_id - - -- the NPC needs to be configured first; route input to the configuration dialog - if(not(yl_speak_up.speak_to[pname].dialog) - or not(yl_speak_up.speak_to[pname].dialog.n_npc) - or not(yl_speak_up.speak_to[pname].d_id)) then - yl_speak_up.input_fs_initial_config(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) 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 - - -- 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_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 - -- enter edit mode with that particular NPC - yl_speak_up.edit_mode[pname] = yl_speak_up.speak_to[pname].n_id - -- 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 - - 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) - return - end - - -- allow the player to take the item back - if(fields.show_player_offers_item and fields.show_player_offers_item ~= "") then - yl_speak_up.show_fs(player, "player_offers_item", nil) - return - end - - - if fields.button_up then - yl_speak_up.speak_to[pname].option_index = - yl_speak_up.speak_to[pname].option_index + yl_speak_up.max_number_of_buttons - yl_speak_up.show_fs(player, "talk", {n_id = yl_speak_up.speak_to[pname].n_id, - d_id = yl_speak_up.speak_to[pname].d_id}) - return - elseif fields.button_down then --and yl_speak_up.speak_to[pname].option_index > yl_speak_up.max_number_of_buttons then - yl_speak_up.speak_to[pname].option_index = - yl_speak_up.speak_to[pname].option_index - yl_speak_up.max_number_of_buttons - if yl_speak_up.speak_to[pname].option_index < 0 then - yl_speak_up.speak_to[pname].option_index = 1 - end - yl_speak_up.show_fs(player, "talk", {n_id = yl_speak_up.speak_to[pname].n_id, - d_id = yl_speak_up.speak_to[pname].d_id}) - return - else - 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 - - for k, v in pairs(fields) do - local s = string.split(k, "_") - - if - s[1] == "button" and s[2] ~= nil and s[2] ~= "" and s[2] ~= "exit" and s[2] ~= "back" and s[3] ~= nil and - s[2] ~= "up" and - s[2] ~= "down" - then - o = s[2] .. "_" .. s[3] - end - end - - if not(edit_mode) and 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 - 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 - 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) - return - end - - -- all three buttons (pre(C)onditions, (Ef)fects, edit option) lead to the same new formspec - if( edit_mode ) then - 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["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 - return - end -end - - -- Make the NPC talk --### diff --git a/init.lua b/init.lua index a599dd4..bdee6eb 100644 --- a/init.lua +++ b/init.lua @@ -19,6 +19,8 @@ dofile(modpath .. "config.lua") dofile(modpath .. "privs.lua") -- handle on_player_receive_fields and showing of formspecs dofile(modpath .. "show_fs.lua") +-- the formspec and input handling for the main dialog +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") -- as the name says: a collection of custom functions that you can