-- 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