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