forked from your-land-mirror/yl_speak_up
		
	
		
			
				
	
	
		
			1063 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			1063 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| -- This is the main talkdialog the NPC shows when right-clicked.
 | |
| 
 | |
| yl_speak_up.stop_talking = function(pname)
 | |
| 	if(not(pname)) then
 | |
| 		return
 | |
| 	end
 | |
| 	yl_speak_up.edit_mode[pname] = nil
 | |
| 	yl_speak_up.speak_to[pname] = nil
 | |
| 	minetest.close_formspec(pname, "yl_speak_up:talk")
 | |
| 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 = ""
 | |
| 
 | |
| 	-- error: not talking?
 | |
| 	if(not(yl_speak_up.speak_to[pname])) then
 | |
| 		return
 | |
| 	end
 | |
| 	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
 | |
| 
 | |
| 	if(fields.show_log) then
 | |
| 		-- show a log
 | |
| 		yl_speak_up.show_fs(player, "show_log", {log_type = "full"})
 | |
| 		return
 | |
| 	end
 | |
| 
 | |
| 	-- mobs_redo based NPC may follow their owner, stand or wander around
 | |
| 	local new_move_order = ""
 | |
| 	if(fields.order_stand) then
 | |
| 		new_move_order = "stand"
 | |
| 	elseif(fields.order_follow) then
 | |
| 		new_move_order = "follow"
 | |
| 	elseif(fields.order_wander) then
 | |
| 		new_move_order = "wander"
 | |
| 	end
 | |
| 	if(new_move_order ~= "") then
 | |
| 		local dialog = yl_speak_up.speak_to[pname].dialog
 | |
| 		yl_speak_up.set_npc_property(pname, "self.order", new_move_order, "move_order")
 | |
| 		minetest.chat_send_player(pname,
 | |
| 			tostring(dialog.n_npc or "NPC").." tells you: "..
 | |
| 				"Ok. I will "..tostring(new_move_order)..".")
 | |
| 		yl_speak_up.stop_talking(pname)
 | |
| 		return
 | |
| 	end
 | |
| 
 | |
| 	-- Is the player working on this particular npc?
 | |
| 	local edit_mode = (yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id)
 | |
| 
 | |
| 	-- if in edit mode: detect if something was changed;
 | |
| 	if(edit_mode or fields.button_edit_name_and_description) then
 | |
| 		local result = yl_speak_up.edit_mode_apply_changes(pname, fields)
 | |
| 	end
 | |
| 
 | |
| 	-- show which dialogs point to this one
 | |
| 	if(edit_mode and fields.show_what_points_to_this_dialog) then
 | |
| 		local dialog = yl_speak_up.speak_to[pname].dialog
 | |
| 		local d_id = yl_speak_up.speak_to[pname].d_id
 | |
| 		yl_speak_up.show_fs(player, "show_what_points_to_this_dialog",
 | |
| 			yl_speak_up.speak_to[pname].d_id)
 | |
| 		return
 | |
| 	end
 | |
| 
 | |
| 	-- the player wants to change name and description; show the formspec
 | |
| 	if(edit_mode and fields.button_edit_name_and_description) then
 | |
| 		-- this is not the initial config - but the same formspec can be used
 | |
| 		yl_speak_up.show_fs(player, "initial_config",
 | |
| 			{n_id = n_id, d_id = yl_speak_up.speak_to[pname].d_id, false})
 | |
| 		return
 | |
| 	end
 | |
| 
 | |
| 	-- the player wants to access the inventory of the NPC
 | |
| 	if((edit_mode and fields.show_inventory)
 | |
| 	  or (fields.show_inventory and yl_speak_up.may_edit_npc(player, n_id))) then
 | |
| 		-- the inventory is just an inventory with a back button; come back to this dialog here
 | |
| 		yl_speak_up.show_fs(player, "inventory")
 | |
| 		return
 | |
| 	end
 | |
| 
 | |
| 	-- change skin, cape and wielded items
 | |
| 	if(edit_mode and fields.edit_skin) then
 | |
| 		local dialog = yl_speak_up.speak_to[pname].dialog
 | |
| 		-- necessary so that the fashin formspec can be created
 | |
| 		yl_speak_up.speak_to[pname].n_npc = dialog.n_npc
 | |
| 		yl_speak_up.show_fs(player, "fashion")
 | |
| 		return
 | |
| 	end
 | |
| 
 | |
| 	if(edit_mode and fields.button_save_dialog) then
 | |
| 		yl_speak_up.show_fs(player, "talk",
 | |
| 			{n_id = n_id, d_id = yl_speak_up.speak_to[pname].d_id, do_save = true})
 | |
| 		return
 | |
| 	end
 | |
| 
 | |
| 	-- 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
 | |
| 		-- for older formspec versions: reset scroll counter
 | |
| 		yl_speak_up.speak_to[pname].counter = 1
 | |
| 		yl_speak_up.speak_to[pname].option_index = 1
 | |
| 		-- enter edit mode with that particular NPC
 | |
| 		yl_speak_up.edit_mode[pname] = yl_speak_up.speak_to[pname].n_id
 | |
| 		-- load the NPC dialog anew - but only what the NPC itself has to say, no generic dialogs
 | |
| 		yl_speak_up.speak_to[pname].dialog = yl_speak_up.load_dialog(n_id, false)
 | |
| 		-- start a new chat - but this time in edit mode
 | |
| 		yl_speak_up.speak_to[pname].d_id = nil
 | |
| 		yl_speak_up.show_fs(player, "talk", {n_id = yl_speak_up.speak_to[pname].n_id, d_id = nil})
 | |
| 		return
 | |
| 	-- end edit mode (does not require the priv; will only switch back to normal behaviour)
 | |
| 	elseif fields.button_end_edit_mode then
 | |
| 		-- if there are any changes done: ask first and don't end edit mode yet
 | |
| 		yl_speak_up.show_fs(player, "quit", nil)
 | |
| 		return
 | |
| 	end
 | |
| 
 | |
| 	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
 | |
| 		-- only split into 2 parts at max
 | |
| 		local s = string.split(k, "_", false, 2)
 | |
| 
 | |
| 		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
 | |
| 
 | |
| 	-- if(edit_mode) (the other case has been dealt with above)
 | |
| 	-- all three buttons (pre(C)onditions, (Ef)fects, edit option) lead to the same new formspec
 | |
| 	local n_dialog = dialog.n_dialogs[d_id]
 | |
| 
 | |
| 	if(n_dialog and n_dialog.d_options) then
 | |
| 		for o_id,v in pairs(n_dialog.d_options) do
 | |
| 			if( fields["edit_option_"..o_id]
 | |
| 			 or fields["conditions_"..o_id]
 | |
| 			 or fields["actions_"..o_id]
 | |
| 			 or fields["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
 | |
| 
 | |
| 
 | |
| -- helper function for
 | |
| -- 	yl_speak_up.get_fs_talkdialog and
 | |
| -- 	yl_speak_up.check_and_add_as_generic_dialog
 | |
| -- find the dialog with d_sort == 0 or lowest number
 | |
| yl_speak_up.get_start_dialog_id = function(dialog)
 | |
| 	if(not(dialog) or not(dialog.n_dialogs)) then
 | |
| 		return nil
 | |
| 	end
 | |
| 	-- Find the dialog with d_sort = 0 or alternatively with the lowest number
 | |
| 	local lowest_sort = nil
 | |
| 	local d_id = nil
 | |
| 	for k, v in pairs(dialog.n_dialogs) do
 | |
| 		local nr = tonumber(v.d_sort)
 | |
| 		if(not(lowest_sort) or (nr and nr >= 0 and nr < lowest_sort)) then
 | |
| 			lowest_sort = nr
 | |
| 			d_id = k
 | |
| 		end
 | |
| 	end
 | |
| 	return d_id
 | |
| end
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| -- helper function for yl_speak_up.get_fs_talkdialog:
 | |
| --   shows the text the NPC "speaks" and adds edit and navigation buttons
 | |
| --   (all only in *edit_mode*)
 | |
| yl_speak_up.get_fs_talkdialog_main_text_in_edit_mode = function(
 | |
| 			formspec, h, dialog, dialog_list, edit_mode, c_d_id, active_dialog)
 | |
| 	local d_id_to_dropdown_index = {}
 | |
| 	if(not(edit_mode)) then
 | |
| 		return {h = h, formspec = formspec, d_id_to_dropdown_index = {}, dialog_list = dialog_list}
 | |
| 	end
 | |
| 	-- allow to change skin, wielded items etc.
 | |
| 	table.insert(formspec, "button[15.75,3.5;3.5,0.9;edit_skin;Edit Skin]")
 | |
| 
 | |
| 	if(not(dialog) or not(dialog.n_dialogs)) then
 | |
| 		return {h = h, formspec = formspec, d_id_to_dropdown_index = {}, dialog_list = dialog_list}
 | |
| 	end
 | |
| 
 | |
| 	-- display the window with the text the NPC is saying
 | |
| 	-- 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) then
 | |
| 			local prev_dialog = tostring(minetest.formspec_escape(sorted_list[i-1]))
 | |
| 			yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
 | |
| 				"button", "8.5,4.0;2,0.9", "prev_dialog_"..prev_dialog,
 | |
| 				"<",
 | |
| 				"Go to previous dialog "..prev_dialog..".",
 | |
| 				(sorted_list[ i-1 ]))
 | |
| 			local next_dialog = tostring(minetest.formspec_escape(sorted_list[i+1]))
 | |
| 			yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
 | |
| 				"button", "11,4.0;2,0.9", "next_dialog_"..next_dialog,
 | |
| 				">",
 | |
| 				"Go to next dialog "..next_dialog..".",
 | |
| 				(sorted_list[ i+1 ]))
 | |
| 		end
 | |
| 		d_id_to_dropdown_index[d] = i + 1
 | |
| 	end
 | |
| 	dialog_list = dialog_list..",d_end"
 | |
| 	d_id_to_dropdown_index["d_end"] = #sorted_list + 2
 | |
| 
 | |
| 	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] or "1")..",]")
 | |
| 	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]")
 | |
| 
 | |
| 	yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
 | |
| 		"button", "13.9,4.0;1,0.9", "show_new_dialog",
 | |
| 		"+",
 | |
| 		"Create a new dialog.",
 | |
| 		true)
 | |
| 	yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
 | |
| 		"button", "13.4,0.3;2,0.9", "button_edit_name_and_description",
 | |
| 		"Edit",
 | |
| 		"Edit name and description of your NPC.",
 | |
| 		true)
 | |
| 	yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
 | |
| 		"button", "15.7,0.3;2,0.9", "button_save_dialog",
 | |
| 		"Save",
 | |
| 		"Save this dialog.",
 | |
| 		true)
 | |
| 
 | |
| 	-- static help text instead of text input field for d_got_item
 | |
| 	if(c_d_id == "d_got_item") then
 | |
| 		table.insert(formspec, "hypertext[0.2,5;19.6,17.8;d_text;"..
 | |
| 			"<normal>Note:\nThis is a special dialog."..
 | |
| 				"It will be called when the player clicks on "..
 | |
| 				"<b>I want to give you something</b>."..
 | |
| 			"\nMost of the things listed below will be added automaticly when you add a "..
 | |
| 				"new option to this dialog. In most cases you may just have to edit the "..
 | |
| 				"<b>precondition</b> so that the <i>right item</i> is accepted, and then "..
 | |
| 				"set the <b>target dialog</b> <i>according to your needs</i>. Please also "..
 | |
| 				"edit the <b>alternate text</b> so that it fits your <i>item</i>!"..
 | |
| 			"\nThis is how it works in detail:"..
 | |
| 			"\n<b>Each option</b> you add here ought to deal with one item(stack) that "..
 | |
| 				"the NPC expects from the player, i.e. <i>farming:bread 2</i>. "..
 | |
| 				"Each option needs to be selected <b>automaticly</b> and ought to contain:"..
 | |
| 			"\n* a <b>precondition</b> regarding "..
 | |
| 				"<b>an item the player offered/gave to the NPC</b> "..
 | |
| 				"(shown as <b>player_offered_item</b> in overview) "..
 | |
| 				"where you define which item(stack) is relevant for this option"..
 | |
| 			"\n* an <b>effect</b> regarding <b>an item the player offered to the NPC</b> "..
 | |
| 				"(shown as <b>deal_with_offered_item</b> in overview) "..
 | |
| 				"where you define what shall happen to the offered item. Usually "..
 | |
| 				"the NPC will accept the item and put it into its inventory."..
 | |
| 			"\n* Don't forget to set a suitable target dialog for the <b>effect</b>! "..
 | |
| 				"Your NPC ought to comment on what he got, i.e. "..
 | |
| 				"<i>Thank you for those two breads! You saved me from starving.</i>"..
 | |
| 				"You can also work with an alternate text here (as is done in the "..
 | |
| 				"default setup when adding a new option here)."..
 | |
| 			"\n</normal>]")
 | |
| 	-- static help text instead of text input field for d_trade
 | |
| 	elseif(c_d_id == "d_trade") then
 | |
| 		table.insert(formspec, "hypertext[0.2,5;19.6,17.8;d_text;"..
 | |
| 			"<normal>Note:\nThis is a special dialog."..
 | |
| 				"It will be called when the player clicks on "..
 | |
| 				"<b>Let's trade!</b>."..
 | |
| 			"\nSome of the things listed below will be added automaticly when you add a "..
 | |
| 				"new option to this dialog. In most cases you may just have to edit the "..
 | |
| 				"<b>precondition</b> so that the <i>right item(stack)</i> is beeing "..
 | |
| 				"searched for, and you need to add suitable effects. The ones added "..
 | |
| 				"automaticly are just an example."..
 | |
| 			"\nNote that once the NPC found a matching precondition, it will execute the "..
 | |
| 			"relevant effects and present the player the trade list. Any further options "..
 | |
| 			"that might also fit will not be executed this time. Only <b>one</b> option "..
 | |
| 			"(or none) will be selected each time."..
 | |
| 			"\nThis is how it works in detail:"..
 | |
| 			"\n<b>Each option</b> you add here ought to deal with one item(stack) that "..
 | |
| 				"the NPC might or might not have in its inventory, "..
 | |
| 				"i.e. <i>default:stick 4</i>. "..
 | |
| 				"Each option needs to be selected <b>automaticly</b> and ought to contain:"..
 | |
| 			"\n* at least one <b>precondition</b> regarding "..
 | |
| 				"<b>the inventory of the NPC</b> "..
 | |
| 				"where you define which item(stack) is relevant for this option "..
 | |
| 				"(you can add multiple such preconditions for each option)"..
 | |
| 			"\n* at least one <b>effect</b> regarding what the NPC shall do if the "..
 | |
| 				"precondition matches. In most cases, <b>NPC crafts something</b>, "..
 | |
| 				"<b>put item from the NPC's inventory into a chest etc.</b> or "..
 | |
| 				"<b>take item from a chest etc. and put it into the NPC's inventory</b> "..
 | |
| 				"will be what you are looking for. More than one effect is possible."..
 | |
| 			"\n* In this particular case, no target dialog needs to be selected. After "..
 | |
| 				"executing the effect(s), the trade list view will be shown to the "..
 | |
| 				"player."..
 | |
| 			"\n</normal>]")
 | |
| 	elseif(active_dialog and active_dialog.d_text) then
 | |
| 		table.insert(formspec, "textarea[0.2,5;19.6,17.8;d_text;;"..
 | |
| 			minetest.formspec_escape(active_dialog.d_text or "?")..
 | |
| 			"]")
 | |
| 	else
 | |
| 		table.insert(formspec, "textarea[0.2,5;19.6,17.8;d_text;;"..
 | |
| 			minetest.formspec_escape("[no text]")..
 | |
| 			"]")
 | |
| 	end
 | |
| 	return {h = h, formspec = formspec, d_id_to_dropdown_index = d_id_to_dropdown_index,
 | |
| 		dialog_list = dialog_list}
 | |
| end
 | |
| 
 | |
| 
 | |
| -- helper function for yl_speak_up.get_fs_talkdialog:
 | |
| --   prints one entry (option/answer) in normal mode - not in edit_mode
 | |
| yl_speak_up.get_fs_talkdialog_line_in_normal_mode = function(
 | |
| 				formspec, h, pname_for_old_fs, oid, sb_v,
 | |
| 				dialog, allowed, pname)
 | |
| 	local t = "- no text given -"
 | |
| 	local t_alt = nil
 | |
| 	-- the preconditions are fulfilled; showe the option
 | |
| 	if(allowed[sb_v.o_id] == true) then
 | |
| 		-- replace $NPC_NAME$ etc.
 | |
| 		t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text(
 | |
| 			sb_v.o_text_when_prerequisites_met, dialog, pname))
 | |
| 	-- precondition not fulfilled? the option shall be hidden
 | |
| 	elseif(sb_v.o_hide_when_prerequisites_not_met == "true") then
 | |
| 		-- show nothing; t_alt remains nil
 | |
| 		t = nil
 | |
| 	-- precondition not fulfilled, and autoanswer active? Then hide this option.
 | |
| 	elseif(sb_v.o_autoanswer) then
 | |
| 		-- show nothing; t_alt remains nil
 | |
| 		t = nil
 | |
| 	-- precondition not fulfilled? the option shall be greyed out
 | |
| 	-- default to greyed out (this option cannot be selected)
 | |
| 	elseif(sb_v.o_grey_when_prerequisites_not_met == "true") then
 | |
| 		local text = sb_v.o_text_when_prerequisites_not_met
 | |
| 		if(not(text) or text == "") then
 | |
| 			text = t or yl_speak_up.message_button_option_prerequisites_not_met_default
 | |
| 		end
 | |
| 		t = nil
 | |
| 		-- replace $NPC_NAME$ etc.
 | |
| 		t_alt = minetest.formspec_escape(yl_speak_up.replace_vars_in_text(
 | |
| 			text, dialog, pname))
 | |
| 	elseif(sb_v.o_grey_when_prerequisites_not_met == "false"
 | |
| 	   and sb_v.o_text_when_prerequisites_not_met ~= "") then
 | |
| 		-- show in normal coor
 | |
| 		t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text(
 | |
| 			sb_v.o_text_when_prerequisites_not_met, dialog, pname))
 | |
| 	end
 | |
| 	if(t or t_alt) then
 | |
| 		-- actually show the button
 | |
| 		h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 			"button_" .. oid,
 | |
| 			t,
 | |
| 			t,
 | |
| 			(t and not(t_alt)),
 | |
| 			t_alt,
 | |
| 			nil, pname_for_old_fs)
 | |
| 	end
 | |
| 	return {h = h, formspec = formspec}
 | |
| end
 | |
| 
 | |
| 
 | |
| -- helper function for yl_speak_up.get_fs_talkdialog:
 | |
| --   prints one entry (option/answer) in *edit_mode*
 | |
| yl_speak_up.get_fs_talkdialog_line_in_edit_mode = function(
 | |
| 				formspec, h, pname_for_old_fs, oid, sb_v,
 | |
| 				dialog, active_dialog, dialog_list, d_id_to_dropdown_index,
 | |
| 				current_index, anz_options)
 | |
| 	local offset = 0.0
 | |
| 	local field_length = 44.4
 | |
| 	if(pname_for_old_fs) then
 | |
| 		offset = 0.7
 | |
| 		field_length = 42.4
 | |
| 	end
 | |
| 	h = h + 1
 | |
| 	-- add a button "o_<nr>:" that leads to an edit formspec for this option
 | |
| 	yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
 | |
| 		"button", tostring(2.3+offset).."," .. h .. ";2,0.9", "edit_option_" .. oid,
 | |
| 		oid,
 | |
| 		"Edit target dialog, pre(C)onditions and (Ef)fects for option "..oid..".",
 | |
| 		true)
 | |
| 	-- 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
 | |
| 			  or v.r_value == "d_end"
 | |
| 			  or v.r_value == "d_trade"
 | |
| 			  or v.r_value == "d_got_item") 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
 | |
| 	-- add a button "-> d_<nr>" that leads to the target dialog (if one is set)
 | |
| 	-- selecting an option this way MUST NOT execute the pre(C)onditions or (Ef)fects!
 | |
| 	yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
 | |
| 		"button", tostring(9.0+offset)..","..h..";1,0.9", "button_" .. oid,
 | |
| 		"->",
 | |
| 		"Go to target dialog "..minetest.formspec_escape(target_dialog or "")..
 | |
| 			" that will be shown when this option ("..oid..") is selected.",
 | |
| 		(target_dialog))
 | |
| 
 | |
| 	-- allow to set a new target dialog
 | |
| 	table.insert(formspec, "dropdown["..tostring(4.4+offset)..","..h..";4.7,1;d_id_"..
 | |
| 		oid..";"..
 | |
| 		dialog_list..";"..
 | |
| 		(d_id_to_dropdown_index[(target_dialog or "?")] 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
 | |
| 	yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
 | |
| 		"button", tostring(0.5+offset)..","..h..";0.5,0.9", "conditions_"..oid,
 | |
| 		"C",
 | |
| 		"There are pre(C)onditions required for showing this option. Display them.",
 | |
| 		(prereq and next(prereq)))
 | |
| 
 | |
| 	yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
 | |
| 		"button", tostring(1.6+offset)..","..h..";0.6,0.9", "effects_"..oid,
 | |
| 		"Ef",
 | |
| 		"There are further (Ef)fects (apart from switching\n"..
 | |
| 			"to a new dialog) set for this option. Display them.",
 | |
| 		(has_other_results))
 | |
| 
 | |
| 	-- are there any actions defined?
 | |
| 	local actions = active_dialog.d_options[sb_v.o_id].actions
 | |
| 	yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
 | |
| 		"button", tostring(1.1+offset)..","..h..";0.5,0.9", "actions_"..oid,
 | |
| 		"A",
 | |
| 		"There is an (A)ction (i.e. a trade) that will happen\n"..
 | |
| 			"when switching to a new dialog. Display actions and\n"..
 | |
| 			"trade of this option.",
 | |
| 		(actions and next(actions)))
 | |
| 
 | |
| 	if(sb_v.o_autoanswer) then
 | |
| 		table.insert(formspec,
 | |
| 			"label["..tostring(9.9+offset+0.2)..","..tostring(h+0.5)..";"..
 | |
| 				minetest.formspec_escape("[Automaticly selected if preconditions are met]")..
 | |
| 				"]")
 | |
| 	elseif(active_dialog.o_random) then
 | |
| 		table.insert(formspec,
 | |
| 			"label["..tostring(9.9+offset+0.2)..","..tostring(h+0.5)..";"..
 | |
| 				minetest.formspec_escape("[One of these options is randomly selected]")..
 | |
| 				"]")
 | |
| 	else
 | |
| 		-- show the actual text for the option
 | |
| 		yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
 | |
| 			"field", tostring(9.9+offset)..","..h..";"..
 | |
| 				 tostring(field_length-2.3)..",0.9",
 | |
| 			"text_option_" .. oid,
 | |
| 			";"..minetest.formspec_escape(sb_v.o_text_when_prerequisites_met),
 | |
| 			"Edit the text that is displayed on button "..oid..".",
 | |
| 			true)
 | |
| 	end
 | |
| 	yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
 | |
| 		"button", tostring(9.9+offset+field_length-2.2)..","..h..";1.0,0.9", "delete_option_"..oid,
 | |
| 		"Del",
 | |
| 		"Delete this option/answer.",
 | |
| 		true)
 | |
| 
 | |
| 	yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
 | |
| --		"image_button", tostring(9.9+offset+field_length-0.5)..","..h..";0.5,0.9"..
 | |
| --			";gui_furnace_arrow_bg.png^[transformR180",
 | |
| 		"button", tostring(9.9+offset+field_length-1.1)..","..h..";0.5,0.9",
 | |
| 		"option_move_down_"..oid,
 | |
| 		"v",
 | |
| 		"Move this option/answer one down.",
 | |
| 		(current_index < anz_options))
 | |
| 	yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
 | |
| --		"image_button", tostring(9.9+offset+field_length-1.0)..","..h..";0.5,0.9"..
 | |
| --			";gui_furnace_arrow_bg.png",
 | |
| 		"button", tostring(9.9+offset+field_length-0.5)..","..h..";0.5,0.9",
 | |
| 		"option_move_up_"..oid,
 | |
| 		"^",
 | |
| 		"Move this option/answer one up.",
 | |
| 		(current_index > 1))
 | |
| 	return {h = h, formspec = formspec}
 | |
| end
 | |
| 
 | |
| 
 | |
| -- helper function for yl_speak_up.get_fs_talkdialog:
 | |
| --   if the player can edit the NPC,
 | |
| --   either add a button for entering edit mode
 | |
| --   or add the buttons needed to edit the dialog when in *edit mode*
 | |
| yl_speak_up.get_fs_talkdialog_add_edit_buttons = function(
 | |
| 				formspec, h, pname_for_old_fs, is_a_start_dialog,
 | |
| 				active_dialog, luaentity, edit_mode, may_edit_npc, anz_options)
 | |
| 	if(not(may_edit_npc)) then
 | |
| 		return {h = h, formspec = formspec}
 | |
| 	end
 | |
| 	-- button "show log" for those who can edit the NPC (entering edit mode is not required)
 | |
| 	text = minetest.formspec_escape(
 | |
| 		"[Log] Show me your log.")
 | |
| 	h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 		"show_log",
 | |
| 		text, text,
 | |
| 		true, nil, nil, pname_for_old_fs)
 | |
| 	-- Offer to enter edit mode if the player has the npc_talk_owner priv OR is allowed to edit the NPC.
 | |
| 	-- The npc_master priv allows to edit all NPC.
 | |
| 	if(not(edit_mode)) then
 | |
| 		h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 			"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.",
 | |
| 			-- chat option: "I am your owner. I have new orders for you.
 | |
| 			"I am your owner. I have new orders for you.",
 | |
| 			true, nil, true, pname_for_old_fs) -- is button_exit
 | |
| 		return {h = h, formspec = formspec}
 | |
| 	end
 | |
| 
 | |
| 	local offset = 0.0
 | |
| 	-- If in edit mode, add new menu entries: "add new options", "end edit mode" and what else is needed.
 | |
| 	h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 		"add_option",
 | |
| 		-- chat option: "Add a new answer/option to this dialog."
 | |
| 		"Adds a new option to this dialog. You can delete options via the option edit menu.",
 | |
| 		"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
 | |
| 		(anz_options < yl_speak_up.max_number_of_options_per_dialog),
 | |
| 			"Maximum number of allowed answers/options reached. No further options/answers "..
 | |
| 				"can be added.", nil, pname_for_old_fs)
 | |
| 
 | |
| 	h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 		"delete_this_empty_dialog",
 | |
| 		-- chat option: "Delete this 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.",
 | |
| 		"Delete this empty dialog.",
 | |
| 		(active_dialog and active_dialog.d_text == "" and anz_options == 0),
 | |
| 		-- (but only show this option if the dialog is empty)
 | |
| 		"If you want to delete this dialog, you need to delete all options and its "..
 | |
| 			"text first.", nil, pname_for_old_fs)
 | |
| 
 | |
| 	h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 		"show_what_points_to_this_dialog",
 | |
| 		-- chat option: "Show what points to this dialog."
 | |
| 		"Show which other dialog options or failed actions\n"..
 | |
| 			"or effects lead the player to this dialog here.",
 | |
| 		"Show what points to this dialog.",
 | |
| 		-- there is no alternate text to show
 | |
| 		true, nil, nil, pname_for_old_fs)
 | |
| 
 | |
| 	h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 		"make_first_option",
 | |
| 		-- chat option: "Make this dialog the first one shown when starting to talk."
 | |
| 		"The NPC has to start with one dialog when he is right-clicked. "..
 | |
| 			"Make this dialog the one shown.",
 | |
| 		"Make this dialog the first one shown when starting a conversation.",
 | |
| 		(active_dialog and active_dialog.d_sort and tonumber(active_dialog.d_sort) ~= 0),
 | |
| 		-- (but only show this option if it's not already the first one)
 | |
| 		"This dialog will be shown whenever a conversation is started.", nil,pname_for_old_fs)
 | |
| 
 | |
| 	local b_text = "Turn this into"
 | |
| 	if(is_a_start_dialog) then
 | |
| 		b_text = "This shall no longer be"
 | |
| 	end
 | |
| 	h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 		"turn_into_a_start_dialog",
 | |
| 		"With automatic selection of options, it is possible that the real\n"..
 | |
| 			"start dialog will never be shown to the player. However, we need\n"..
 | |
| 			"to add some buttons to that start dialog for i.e. giving items\n"..
 | |
| 			"to the NPC and for trading. Therefore, dialogs can be marked as\n"..
 | |
| 			"*a* start dialog so that these buttons will be added to those dialogs.",
 | |
| 		b_text.." *a* start dialog where buttons for trade etc. are shown.",
 | |
| 		not(active_dialog and active_dialog.d_sort and tonumber(active_dialog.d_sort) == 0),
 | |
| 		"The start dialog automaticly counts as *a* start dialog where buttons for "..
 | |
| 			"trade etc. are shown.", nil, pname_for_old_fs)
 | |
| 
 | |
| 	-- chat option: Mute/Unmute NPC
 | |
| 	h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 		"mute_npc",
 | |
| 		-- chat option: mute the 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.",
 | |
| 		"State: Not muted. Stop talking to other players while I give you new orders.",
 | |
| 		(luaentity and luaentity.yl_speak_up.talk), nil, nil, pname_for_old_fs)
 | |
| 	h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 		"un_mute_npc",
 | |
| 		-- unmute the 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.",
 | |
| 		"State: You are currently muted. Talk to anyone again who wants to talk to you.",
 | |
| 		-- the NPC has to be there
 | |
| 		(luaentity and not(luaentity.yl_speak_up.talk)), nil, nil, pname_for_old_fs)
 | |
| 
 | |
| 
 | |
| 	h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 		"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.",
 | |
| 		-- chat option:"That was all. I'm finished with giving you new orders. Remember them!"
 | |
| 		"That was all. I'm finished with giving you new orders. Remember them!",
 | |
| 		true, nil, true, pname_for_old_fs) -- is button_exit
 | |
| 	return {h = h, formspec = formspec}
 | |
| 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(dialog)) then
 | |
| 		yl_speak_up.log_change(pname, n_id,
 | |
| 			"unconfigured NPC beeing talked to at "..
 | |
| 			minetest.pos_to_string(player:get_pos()), "action")
 | |
| 		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
 | |
| 
 | |
| 	-- add a d_trade dialog if necessary
 | |
| 	if(dialog and dialog.trades and dialog.n_dialogs and not(dialog.n_dialogs["d_trade"])) then
 | |
| 		yl_speak_up.add_new_dialog(dialog, pname, "trade", "[no text]")
 | |
| 	end
 | |
| 
 | |
| 	--[[ 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
 | |
| 	-- the generic start dialog contains only those options that are generic;
 | |
| 	-- choose the right start dialog of the NPC
 | |
| 	if(d_id ~= nil and d_id ~= "d_generic_start_dialog") then
 | |
| 		active_dialog = dialog.n_dialogs[d_id]
 | |
| 		c_d_id = d_id
 | |
| 	elseif(d_id and d_id ~= "d_generic_start_dialog" and 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
 | |
| 	c_d_id = yl_speak_up.get_start_dialog_id(dialog)
 | |
| 	if(c_d_id) then
 | |
| 		active_dialog = dialog.n_dialogs[c_d_id]
 | |
| 	end
 | |
| 	else
 | |
| 	-- it may be possible that this player can initialize this npc
 | |
| 		yl_speak_up.log_change(pname, n_id,
 | |
| 			"unconfigured NPC beeing talked to at "..
 | |
| 			minetest.pos_to_string(player:get_pos()), "action")
 | |
| 		-- 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))
 | |
| 	-- autoanswer or o_random may force to select a particular dialog
 | |
| 	local go_to_next_dialog = nil
 | |
| 	-- 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
 | |
| 		go_to_next_dialog = allowed["autoanswer"]
 | |
| 	-- randomly select an answer
 | |
| 	elseif(not(edit_mode) and allowed and active_dialog.o_random
 | |
| 	  and (recursion_depth < yl_speak_up.max_allowed_recursion_depth)) then
 | |
| 		local liste = {}
 | |
| 		-- only allowed options can be randomly selected from
 | |
| 		for o_id, v in pairs(allowed) do
 | |
| 			if(v) then
 | |
| 				table.insert(liste, o_id)
 | |
| 			end
 | |
| 		end
 | |
| 		-- randomly select one of the possible dialogs
 | |
| 		if(#liste > 0) then
 | |
| 			go_to_next_dialog = liste[math.random(1, #liste)]
 | |
| 		end
 | |
| 	end
 | |
| 
 | |
| 	if(go_to_next_dialog and go_to_next_dialog ~= "") then
 | |
| 		-- no actions shall be executed
 | |
| 		local o_id = go_to_next_dialog
 | |
| 		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
 | |
| 		-- end the conversation?
 | |
| 		if(target_dialog and target_dialog == "d_end") then
 | |
| 			yl_speak_up.stop_talking(pname)
 | |
| 			-- a formspec is expected here; provide one that has an exit button only
 | |
| 			return "size[2,1]"..
 | |
| 				"button_exit[0,0;1,1;Exit;exit]"
 | |
| 		end
 | |
| 		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 "formspec_version[1]"..
 | |
| 				yl_speak_up.show_fs_simple_deco(8, 2.5)..
 | |
| 				"label[0.5,0.5;"..
 | |
| 				minetest.formspec_escape(dialog.n_npc or "- ? -")..
 | |
| 				" does not seem to be intrested in that.\n"..
 | |
| 				"Please take your item back and try something else.]"..
 | |
| 				"button[3.5,1.5;1.5,1.0;show_player_offers_item;Ok]"
 | |
| 		end
 | |
| 	end
 | |
| 
 | |
| 
 | |
| 	yl_speak_up.speak_to[pname].allowed = allowed
 | |
| 
 | |
| 
 | |
| 	local pname_for_old_fs = yl_speak_up.get_pname_for_old_fs(pname)
 | |
| 	local fs_version = yl_speak_up.fs_version[pname]
 | |
| 	local formspec = {}
 | |
| 	local h
 | |
| 
 | |
| 	-- 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
 | |
| 	-- allow to change skin, wielded items etc.
 | |
| 	-- display the window with the text the NPC is saying in *edit_mode*
 | |
| 	local res_edit_top = yl_speak_up.get_fs_talkdialog_main_text_in_edit_mode(
 | |
| 				formspec, h, dialog, dialog_list, edit_mode, c_d_id, active_dialog)
 | |
| 	-- we are finished with adding buttons and text etc. to the left side of the formspec
 | |
| 	local left_window_fs = table.concat(res_edit_top.formspec, "\n")
 | |
| 	dialog_list = res_edit_top.dialog_list
 | |
| 	-- find the right index for the dialog_list dropdown above
 | |
| 	local d_id_to_dropdown_index = res_edit_top.d_id_to_dropdown_index
 | |
| 
 | |
| 	-- empty formspec for the bottom part
 | |
| 	formspec = {}
 | |
| 
 | |
| 	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_o_list = yl_speak_up.get_sorted_options(active_dialog.d_options, "o_sort")
 | |
| 		for _, sb_v in ipairs(sorted_o_list) do
 | |
| 			anz_options = anz_options + 1
 | |
| 		end
 | |
| 
 | |
| 		for i, s_o_id in ipairs(sorted_o_list) do
 | |
| 			local sb_v = active_dialog.d_options[s_o_id]
 | |
| 			local oid = minetest.formspec_escape(sb_v.o_id)
 | |
| 			local res = {}
 | |
| 			-- in edit_mode: show all options
 | |
| 			if(edit_mode) then
 | |
| 				res = yl_speak_up.get_fs_talkdialog_line_in_edit_mode(
 | |
| 					formspec, h, pname_for_old_fs, oid, sb_v,
 | |
| 					dialog, active_dialog, dialog_list, d_id_to_dropdown_index,
 | |
| 					i, #sorted_o_list)
 | |
| 			-- normal mode: show an option if the prerequirements (if any are defined) are met
 | |
| 			elseif(not(edit_mode)) then
 | |
| 				res = yl_speak_up.get_fs_talkdialog_line_in_normal_mode(
 | |
| 					formspec, h, pname_for_old_fs, oid, sb_v,
 | |
| 					dialog, allowed, pname)
 | |
| 			end
 | |
| 			formspec = res.formspec
 | |
| 			h = res.h
 | |
| 		end
 | |
| 	end
 | |
| 
 | |
| 	-- with automatic selection from the start dialog, it is possible that the
 | |
| 	-- real start dialog is never shown; thus, add those buttons which need to
 | |
| 	-- be shown just once to all dialogs with is_a_start_dialog set
 | |
| 	local is_a_start_dialog = (active_dialog and active_dialog.d_sort
 | |
| 				   and (tonumber(active_dialog.d_sort) == 0
 | |
| 				     or active_dialog.is_a_start_dialog))
 | |
| 	-- add a "I want to give you something" button to the first dialog if the NPC accepts items
 | |
| 	if(is_a_start_dialog) then
 | |
| 		local offer_item_add_text = ""
 | |
| 		if(edit_mode) then
 | |
| 			offer_item_add_text = minetest.formspec_escape("[dialog d_got_item] -> ")
 | |
| 		end
 | |
| 		h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 			"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.",
 | |
| 			offer_item_add_text.."I want to give you something.",
 | |
| 			-- show this in edit mode and when the NPC actually accepts items
 | |
| 			(edit_mode or dialog.n_dialogs["d_got_item"]), nil, nil, pname_for_old_fs)
 | |
| 	end
 | |
| 
 | |
| 	-- can the player edit this NPC?
 | |
| 	local may_edit_npc = yl_speak_up.may_edit_npc(player, n_id)
 | |
| 	-- for muting and for checking the owner/order, the luaentity is needed
 | |
| 	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 in edit mode, add new menu entries: "add new options", "end edit mode" and what else is needed.
 | |
| 	-- Else allow to enter edit mode
 | |
| 	local res = yl_speak_up.get_fs_talkdialog_add_edit_buttons(
 | |
| 				formspec, h, pname_for_old_fs, is_a_start_dialog,
 | |
| 				active_dialog, luaentity, edit_mode, may_edit_npc, anz_options)
 | |
| 	formspec = res.formspec
 | |
| 	h = res.h
 | |
| 
 | |
| 
 | |
| 	-- add a Let's trade button to the first dialog if the NPC has trades
 | |
| 	local has_trades = nil
 | |
| 	if(is_a_start_dialog and dialog.trades) then
 | |
| 		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
 | |
| 	end
 | |
| 	if(has_trades) then
 | |
| 		h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 			"show_trade_list",
 | |
| 			"Show a list of trades the NPC has to offer.",
 | |
| 			"Let's trade!",
 | |
| 			(has_trades), nil, nil, pname_for_old_fs)
 | |
| 
 | |
| 	elseif(is_a_start_dialog and may_edit_npc) then
 | |
| 		-- show the "show your inventory"-button even when not in edit mode
 | |
| 		h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 			"show_inventory",
 | |
| 			"Access and manage the inventory of the NPC. This is used for adding trade "..
 | |
| 				"items, getting collected payments and managing quest items.",
 | |
| 			"Show your inventory (only accessible to owner)!",
 | |
| 			true, nil, nil, pname_for_old_fs)
 | |
| 	end
 | |
| 
 | |
| 	-- mobs_redo based NPC can follow, stand or wander around
 | |
| 	if(luaentity and luaentity.order and may_edit_npc
 | |
| 	  -- not all mobs need or support this feature
 | |
| 	  and table.indexof(yl_speak_up.emulate_orders_on_rightclick, luaentity.name) > -1) then
 | |
| 		if(luaentity.order ~= "follow") then
 | |
| 			h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 				"order_follow",
 | |
| 				"The NPC will follow you.",
 | |
| 				"New order: Follow me!",
 | |
| 				((luaentity.owner == pname) and (luaentity.order ~= "follow")),
 | |
| 				"New order: Follow me. (Only available for owner).",
 | |
| 				nil, pname_for_old_fs)
 | |
| 			end
 | |
| 		h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 			"order_stand",
 | |
| 			"The NPC will wait here.",
 | |
| 			"New order: Stand here.",
 | |
| 			(luaentity.order ~= "stand"), nil, nil, pname_for_old_fs)
 | |
| 		h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
 | |
| 			"order_wander",
 | |
| 			"The NPC will wander around randomly.",
 | |
| 			"New order: Wander around a bit on your own.",
 | |
| 			(luaentity.order ~= "walking"), nil, nil, pname_for_old_fs)
 | |
| 	end
 | |
| 
 | |
| 	-- we are finished with adding buttons to the bottom of the formspec
 | |
| 	local bottom_window_fs = table.concat(formspec, "\n")
 | |
| 
 | |
| 	return yl_speak_up.show_fs_decorated(pname, edit_mode, h, alternate_text,
 | |
| 						left_window_fs, bottom_window_fs,
 | |
| 						active_dialog, h)
 | |
| end
 |