-- helper function; get a formspec with the inventory of the player (for selecting items)
yl_speak_up.fs_your_inventory_select_item = function(pname, data)
	return "label[0.2,4.2;Name of the item(stack):]"..
		"field[4.0,4.0;16.0,0.6;inv_stack_name;;"..(data.inv_stack_name or "").."]"..
		"tooltip[inv_stack_name;Enter name of the block and amount.\n"..
			"Example: \"default:apple 3\" for three apples,\n"..
			"         \"farming:bread\" for a bread.]"..
		"label[0.2,5.7;Or put the item in here\nand click on \"Update\":]"..
		"button[5.5,5.5;1.5,0.9;store_item_name;Update]"..
		"list[detached:yl_speak_up_player_"..pname..";npc_wants;4.0,5.5;1,1;]"..
		"label[8,4.9;Your inventory:]"..
		"list[current_player;main;8,5.3;8,4;]"
end
-- helper function: get the names of the inventory lists of the node at position
-- pos on the map and return the index of search_for_list_name in that index
yl_speak_up.get_node_inv_lists = function(pos, search_for_list_name)
	if(not(pos)) then
		return {inv_lists = {"- no inventory -"}, index = "1"}
	end
	local meta = minetest.get_meta(pos)
	if(not(meta)) then
		return {inv_lists = {"- no inventory -"}, index = "1"}
	end
	local inv_lists = {}
	local index = -1
	local inv = meta:get_inventory()
	table.insert(inv_lists, minetest.formspec_escape("- please select -"))
	for k,v in pairs(inv:get_lists()) do
		table.insert(inv_lists, k)
		if(search_for_list_name == k) then
			index = #inv_lists
		end
	end
	return {inv_lists = inv_lists, index = tostring(index)}
end
-- helper function for yl_speak_up.input_fs_edit_option_related
yl_speak_up.delete_element_p_or_a_or_e = function(
			player, pname, n_id, d_id, o_id, x_id, id_prefix,
			element_list_name, element_desc, formspec_input_to)
	-- does the dialog we want to modify exist?
	local dialog = yl_speak_up.speak_to[pname].dialog
	if(not(dialog and dialog.n_dialogs
	  and x_id
	  and dialog.n_dialogs[d_id]
	  and dialog.n_dialogs[d_id].d_options
	  and dialog.n_dialogs[d_id].d_options[o_id])) then
		yl_speak_up.show_fs(player, "msg", {
			input_to = "yl_speak_up:"..formspec_input_to,
			formspec = "size[9,2]"..
				"label[0.2,0.5;The dialog that is supposed to contain the\n"..
				"element that you want to delete does not exist.]"..
				"button[1.5,1.5;2,0.9;back_from_cannot_be_edited;Back]"})
		return
	end
	local old_elem = dialog.n_dialogs[d_id].d_options[o_id][ element_list_name ][ x_id ]
	if(id_prefix == "r_" and old_elem and old_elem.r_type == "dialog") then
		yl_speak_up.show_fs(player, "msg", {
			input_to = "yl_speak_up:"..formspec_input_to,
			formspec = "size[9,2]"..
				"label[0.2,0.5;Effects of the type \"dialog\" cannot be deleted.\n"..
				"Use the edit options or dialog menu to change the target dialog.]"..
				"button[1.5,1.5;2,0.9;back_from_cannot_be_edited;Back]"})
		return
	end
	-- actually delete the element
	dialog.n_dialogs[d_id].d_options[o_id][ element_list_name ][ x_id ] = nil
	-- record this as a change, but do not save do disk yet
	table.insert(yl_speak_up.npc_was_changed[ n_id ],
		"Dialog "..tostring(d_id)..": "..element_desc.." "..tostring( x_id )..
		" deleted for option "..tostring(o_id)..".")
	-- TODO: when trying to save: save to disk as well?
	-- show the new/changed element
	-- go back to the edit option dialog (after all we just deleted the prerequirement)
	yl_speak_up.show_fs(player, "msg", {
		input_to = "yl_speak_up:"..formspec_input_to,
		formspec = "size[6,2]"..
			"label[0.2,0.5;"..element_desc.." \""..
			minetest.formspec_escape(tostring( x_id ))..
			"\" has been deleted.]"..
			"button[1.5,1.5;2,0.9;back_from_delete_element;Back]"})
	return
end
-- helper function for yl_speak_up.input_fs_edit_option_related
yl_speak_up.save_element_p_or_a_or_e = function(
			player, pname, n_id, d_id, o_id, x_id, id_prefix, tmp_data_cache,
			element_list_name, element_desc, max_entries_allowed,
			values_what, values_operator, values_block, values_trade, values_inv,
			formspec_input_to, data, fields)
	-- for creating the new prerequirement; normal elements: p_type, p_value, p_id
	local v = {}
	-- determine p_type
	v[ id_prefix.."type" ] = values_what[ data.what ]
	-- so that we don't have to compare number values of data.what
	local what_type = values_what[ data.what ]
	local dialog = yl_speak_up.speak_to[pname].dialog
	if(not(dialog) or not(dialog.n_dialogs)
	  or not(dialog.n_dialogs[d_id])
	  or not(dialog.n_dialogs[d_id].d_options)
	  or not(dialog.n_dialogs[d_id].d_options[o_id])) then
		-- this really should not happen during the normal course of operation
		-- (only if the player sends forged formspec data or a bug occoured)
		minetest.chat_send_player(pname, "Dialog or option does not exist.")
		return
	end
	local elements = dialog.n_dialogs[d_id].d_options[o_id][ element_list_name ]
	if(not(elements)) then
		dialog.n_dialogs[d_id].d_options[o_id][ element_list_name ] = {}
		elements = dialog.n_dialogs[d_id].d_options[o_id][ element_list_name ]
		x_id = "new"
	end
	-- set x_id appropriately
	if(not(x_id) or x_id == "new") then
		x_id = id_prefix..yl_speak_up.find_next_id(elements)
	end
	v[ id_prefix.."id" ] = x_id
	-- if needed: show a message after successful save so that the player can take
	-- his items back from the trade_inv slots
	local show_save_msg = nil
	local sorted_key_list = yl_speak_up.sort_keys(elements)
	if( x_id == "new" and #sorted_key_list >= max_entries_allowed) then
		-- this really should not happen during the normal course of operation
		-- (only if the player sends forged formspec data or a bug occoured)
		minetest.chat_send_player(pname, "Maximum number of allowed entries exceeded.")
		return
	end
	-- "an internal state (i.e. of a quest)", -- 2
	if(what_type == "state" and id_prefix ~= "a_") then
		v[ id_prefix.."value" ] = "expression"
		v[ id_prefix.."operator" ] = values_operator[ data.operator ]
		v[ id_prefix.."var_cmp_value" ] = (data.var_cmp_value or "")
		-- if it is a custom server function,then do not preifx it with $ playername
		if(id_prefix == "p_") then
			local idx = table.indexof(yl_speak_up.custom_server_functions.precondition_descriptions,
			                          data.variable_name)
			if(idx > -1) then
				v[ id_prefix.."variable" ] = data.variable_name
			else
				v[ id_prefix.."variable" ] = yl_speak_up.add_pname_to_var(data.variable_name, pname)
			end
		else
			v[ id_prefix.."variable" ] = yl_speak_up.add_pname_to_var(data.variable_name, pname)
		end
	-- "the value of a property of the NPC (for generic NPC)"
	elseif(what_type == "property" and id_prefix ~= "a_") then
		v[ id_prefix.."value" ] = (data.property or "")
		v[ id_prefix.."operator" ] = values_operator[ data.operator ]
		v[ id_prefix.."var_cmp_value" ] = (data.var_cmp_value or "")
	-- "something that has to be calculated or evaluated (=call a function)"
	elseif(what_type == "evaluate") then
		v[ id_prefix.."value" ] = (data.function_name or "")
		v[ id_prefix.."operator" ] = values_operator[ data.operator ]
		v[ id_prefix.."var_cmp_value" ] = (data.var_cmp_value or "")
		-- transfer the parameters
		for i = 1, 9 do
			local s = "param"..tostring(i)
			v[ id_prefix..s ] = (data[s] or "")
		end
	-- "a block somewhere", -- 3
	elseif(what_type == "block" and id_prefix ~= "a_") then
		v[ id_prefix.."value" ] = values_block[ data.block ]
		if(not(data.block_pos) or not(data.node_data) or not(data.node_data.name)) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[8,2]"..
					"label[0.2,0.5;Error: Please select a block first!]"..
					"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
			return
		end
		-- for "node_is_air", there is no need to store node name and parameter
		if(v[ id_prefix.."value" ]
		  and (v[ id_prefix.."value" ] == "node_is_like"
		    or v[ id_prefix.."value" ] == "node_is_diffrent_from")
		    or v[ id_prefix.."value" ] == "place"
		    or v[ id_prefix.."value" ] == "dig"
		    or v[ id_prefix.."value" ] == "punch"
		    or v[ id_prefix.."value" ] == "right-click") then
			v[ id_prefix.."node" ]   = data.node_data.name
			v[ id_prefix.."param2" ] = data.node_data.param2
		end
		-- preconditions can be applied to all blocks; effects may be more limited
		if(id_prefix == "r_"
		  and yl_speak_up.check_blacklisted(v[id_prefix.."value"],
				-- we don't know yet which node will be there later on
				data.node_data.name, data.node_data.name)) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[8,2]"..
					"label[0.2,0.5;Error: Blocks of type \""..
					tostring(data.node_data.name).."\" do not allow\n"..
					"interaction of type \""..tostring(v[id_prefix.."value"])..
					"\" for NPC.]"..
					"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
			return
		end
		-- we also need to store the position of the node
		v[ id_prefix.."pos" ] = {x = data.block_pos.x, y = data.block_pos.y, z = data.block_pos.z }
		-- "I can't punch it. The block is as the block *above* the one I punched.",
		if(id_prefix == "p_" and data.block == 5) then
			v.p_pos.y = v.p_pos.y + 1
		end
	-- "a trade", -- 4
	-- (only for preconditions; not for effects)
	elseif(what_type == "trade" and id_prefix == "p_") then
		-- this depends on the trade associated with that option; therefore,
		-- it does not need any more parameters (they come dynamicly from the
		-- trade)
		v.p_value = values_trade[ data.trade ]
	-- "the inventory of the player", -- 5
	-- "the inventory of the NPC", -- 6
	-- "the inventory of a block somewhere", -- 7
	-- (only for preconditions; not for effects)
	elseif((id_prefix == "p_"
	       and (what_type == "player_inv" or what_type == "npc_inv" or what_type == "block_inv"))
	     or(id_prefix == "r_"
	       and (what_type == "put_into_block_inv" or what_type == "take_from_block_inv"))) then
		-- changing the inventory of a block? we need to set p_value to something
		if(id_prefix == "r_") then
			-- just to be sure something is stored there...
			v.r_value = data.inv_stack_name
			-- for easier access in the formspec
			v.r_itemstack = data.inv_stack_name
		-- store in p_value what we want to check regarding the inv (contains/contains not/empty/..)
		else
			v.p_value = values_inv[ data.inv ]
		end
		if(v.p_value and v.p_value ~= "inv_is_empty") then
			if(not(data.inv_stack_name) or data.inv_stack_name == "") then
				yl_speak_up.show_fs(player, "msg", {
					input_to = "yl_speak_up:"..formspec_input_to,
					formspec = "size[8,2]"..
						"label[0.2,0.5;Error: Please provide the name of the "..
							"\nitem you want to check for!]"..
						"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
				return
			end
			-- we have checked this value earlier on
			v[ id_prefix.."itemstack" ] = data.inv_stack_name
		end
		if(data and data.what_type == "player_inv") then
			data.inv_list_name = "main"
		elseif(data and data.what_type == "npc_inv") then
			data.inv_list_name = "npc_main"
		elseif(data and data.what_type == "block_inv") then
			data.inv_list_name = "main"
		end
		if(not(data.inv_list_name) or data.inv_list_name == "") then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[8,2]"..
					"label[0.2,0.5;Error: Please provide the name of the "..
						"\ninventory you want to access!]"..
					"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
			return
		end
		-- the name of the inventory list we want to access
		v[ id_prefix.."inv_list_name" ] = data.inv_list_name
		-- the inventory of a block
		if(what_type == "block_inv"
		  or what_type == "put_into_block_inv"
		  or what_type == "take_from_block_inv") then
			if(not(data.block_pos) or not(data.node_data) or not(data.node_data.name)) then
				yl_speak_up.show_fs(player, "msg", {
					input_to = "yl_speak_up:"..formspec_input_to,
					formspec = "size[8,2]"..
						"label[0.2,0.5;Error: Please select a block first!]"..
						"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
				return
			end
			-- we also need to store the position of the node
			v[ id_prefix.."pos" ] = {x = data.block_pos.x, y = data.block_pos.y, z = data.block_pos.z }
		end
	-- "give item (created out of thin air) to player (requires npc_master priv)", -- 9
	-- "take item from player and destroy it (requires npc_master priv)", -- 10
	elseif(id_prefix == "r_" and (what_type == "give_item" or what_type == "take_item")) then
		if(not(data.inv_stack_name) or data.inv_stack_name == "") then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[8,2]"..
					"label[0.2,0.5;Error: Please provide the name of the "..
						"\nitem you want to give or take!]"..
					"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
			return
		end
		if(not(minetest.check_player_privs(player, {npc_master=true}))) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[9,2]"..
					"label[0.2,0.5;Error: You need the \"npc_master\" priv "..
						" in order to set this effect.]"..
					"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
			return
		end
		v[ "r_value" ] = data.inv_stack_name
	-- "move the player to a given position (requires npc_master priv)", -- 11
	elseif(what_type == "move" and id_prefix == "r_") then
		if(not(data.move_to_x) or not(data.move_to_y) or not(data.move_to_z)) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[9,2]"..
					"label[0.2,0.5;Error: Please provide valid coordinates "..
						" x, y and z!]"..
					"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
			return
		end
		if(not(minetest.check_player_privs(player, {npc_master=true}))) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[9,2]"..
					"label[0.2,0.5;Error: You need the \"npc_master\" priv "..
						" in order to set this effect.]"..
					"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
			return
		end
		v[ "r_value" ] = minetest.pos_to_string(
			{x = data.move_to_x, y = data.move_to_y, z = data.move_to_z})
	-- effect "execute Lua code (requires npc_master priv)", -- precondition: 8; effect: 12
	elseif((what_type == "function" and id_prefix == "p_")
	    or (what_type == "function" and id_prefix == "r_")) then
		if(not(data.lua_code) or data.lua_code == "") then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[9,2]"..
					"label[0.2,0.5;Error: Please enter the Lua code you want "..
						"to execute!]"..
					"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
			return
		end
		if(not(minetest.check_player_privs(player, {npc_master=true}))) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[9,2]"..
					"label[0.2,0.5;Error: You need the \"npc_master\" priv "..
						" in order to set this.]"..
					"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
			return
		end
		v[ id_prefix.."value" ] = data.lua_code
	-- "NPC crafts something", -- 6
	-- (only for effects; not for preconditions)
	elseif(what_type == "craft" and id_prefix == "r_") then
		local player_inv = player:get_inventory()
		if(player_inv:get_stack("craftpreview", 1):is_empty()) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[8,2]"..
					"label[0.2,0.5;Error: Please prepare your craft grid first!"..
						"\nYour NPC needs to know what to craft.]"..
					"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
			return
		end
		-- store the craft result (Note: the craft result may change in the future
		-- if the server changes its craft result, making this craft invalid)
		v[ "r_value" ] = player_inv:get_stack("craftpreview", 1):to_string()
		v[ "r_craft_grid"] = {}
		for i = 1, 9 do
			-- store all the indigrents of the craft grid
			table.insert( v[ "r_craft_grid" ],
				player_inv:get_stack("craft", i):to_string())
		end
	-- "go to other dialog if the *previous* effect failed", -- 7
	-- (only for effects; not for preconditions)
	elseif(what_type == "on_failure" and id_prefix == "r_") then
		v[ "r_value" ] = data.on_failure
	-- "send a chat message to all players", -- 8
	-- (only for effects; not for preconditions)
	elseif(what_type == "chat_all" and id_prefix == "r_") then
		data.chat_msg_text = fields.chat_msg_text
		-- allow saving only if the placeholders are all present
		-- (reason for requiring them: players and server owners ought to
		-- be able to see who is responsible for a message)
		if(not(string.find(data.chat_msg_text, "%$NPC_NAME%$"))
		  or not(string.find(data.chat_msg_text, "%$PLAYER_NAME%$"))
		  or not(string.find(data.chat_msg_text, "%$OWNER_NAME%$"))) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[9,2.5]"..
					"label[0.2,0.5;Error: Your chat message needs to contain "..
						"the following\nplaceholders: $NPC_NAME$, "..
						"$PLAYER_NAME$ and $OWNER_NAME$.\nThat way, other "..
						"players will know who sent the message.]"..
					"button[1.5,2.0;2,0.9;back_from_error_msg;Back]"})
			return
		end
		v[ "r_value" ] = data.chat_msg_text
	-- "Normal trade - one item(stack) for another item(stack).", -- 3
	-- (only for actions)
	elseif(what_type == "trade" and id_prefix == "a_") then
		-- remember which option was selected
		yl_speak_up.speak_to[pname].o_id = o_id
		-- do not switch target dialog (we are in edit mode)
		yl_speak_up.speak_to[pname].target_d_id = nil
		-- just to make sure that the trade_id is properly set...
		if(not(data.trade_id)) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[9,2.5]"..
					"label[0.2,0.5;Error: Missing trade ID.]"..
					"button[1.5,2.0;2,0.9;back_from_error_msg;Back]"})
			return
		end
		-- the button is called store_trade_simple instead of save_element in
		-- the trade simple function(s); we want to store a trade
		fields.store_trade_simple = true
		local res = yl_speak_up.input_add_trade_simple(player, "", fields)
		-- the above function sets:
		--    dialog.trades[ trade_id ] = {pay={ps},buy={bs}, d_id = d_id, o_id = o_id}
		-- store the trade as an action:
		local dialog = yl_speak_up.speak_to[pname].dialog
		if(res and dialog.trades and dialog.trades[ data.trade_id ]) then
			v[ "a_value" ] = data.trade_id
			v[ "a_pay"   ] = dialog.trades[ data.trade_id ].pay
			v[ "a_buy"   ] = dialog.trades[ data.trade_id ].buy
			v[ "a_on_failure" ] = ""
			if(data.action_failure_dialog) then
				local dialog = yl_speak_up.speak_to[pname].dialog
				local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
				v[ "a_on_failure" ] = sorted_dialog_list[ data.action_failure_dialog ]
			end
		end
	-- "The NPC gives something to the player (i.e. a quest item).", -- 4
	-- "The player is expected to give something to the NPC (i.e. a quest item).", -- 5
	-- (only for actions)
	elseif(((what_type == "npc_gives" or what_type == "npc_wants") and  id_prefix == "a_")
	     or (what_type == "player_offered_item" and id_prefix == "p_")) then
		local trade_inv_list = what_type
		if(id_prefix == "p_") then
			trade_inv_list = "npc_wants"
		end
		local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname})
		if(not(trade_inv) or trade_inv:is_empty( trade_inv_list )) then
			local what = "give to"
			if(id_prefix == "p_") then
				what = "accept from"
			end
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[9,2.5]"..
					"label[0.2,0.5;Please insert an item first! Your NPC "..
						"needs\nto know what it shall "..what.." the player.]"..
					"button[1.5,2.0;2,0.9;back_from_error_msg;Back]"})
			return
		end
		v[ "a_on_failure" ] = ""
		if(data.action_failure_dialog) then
			local dialog = yl_speak_up.speak_to[pname].dialog
			local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
			v[ "a_on_failure" ] = sorted_dialog_list[ data.action_failure_dialog ]
		end
		-- change the node in the slot
		local stack = trade_inv:get_stack( trade_inv_list, 1)
		if(not(stack) or not(minetest.registered_items[ stack:get_name() ])) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[9,2.5]"..
					"label[0.2,0.5;This item is unkown. Please use only known"..
						"items.]"..
					"button[1.5,2.0;2,0.9;back_from_error_msg;Back]"})
			return
		end
		-- is this particular item blacklisted on this server?
		-- this is only relevant for actions, not for preconditions
		if(id_prefix ~= "p_" and yl_speak_up.blacklist_action_quest_item[ stack:get_name() ]) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[9,2.5]"..
					"label[0.2,0.5;Sorry. This item is blacklisted on this "..
						"server.\nYou can't use it as a quest item.]"..
					"button[1.5,2.0;2,0.9;back_from_error_msg;Back]"})
			return
		end
		local meta = stack:get_meta()
		-- what does the NPC want to give?
		v[ id_prefix.."value" ] = stack:get_name().." "..stack:get_count()
		-- for displaying as a background image
		data.item_string = v[ id_prefix.."value" ]
		if(what_type == "npc_wants" or what_type == "player_offered_item") then
			-- try to reconstruct $PLAYER_NAME$ (may not always work)
			local item_was_for = meta:get_string("yl_speak_up:quest_item_for")
			local new_desc = meta:get_string("description")
			if(item_was_for and item_was_for ~= "") then
				new_desc = string.gsub(new_desc, item_was_for, "$PLAYER_NAME$")
			end
			data.item_desc = new_desc
		end
		-- set new description if there is one set (optional)
		if(data.item_desc
		  and data.item_desc ~= ""
		  and data.item_desc ~= "- none set -") then
			if(what_type == "npc_gives") then
				meta:set_string("description", data.item_desc)
			end
			v[ id_prefix.."item_desc" ] = data.item_desc
		end
		if(what_type == "npc_wants" or what_type == "player_offers_item") then
			data.item_quest_id = meta:get_string("yl_speak_up:quest_id")
		end
		-- set special ID (optional)
		if(data.item_quest_id
		  and data.item_quest_id ~= ""
		  and data.item_quest_id ~= "- no item set -") then
			if(what_type == "npc_gives") then
				-- which player got this quest item?
				meta:set_string("yl_speak_up:quest_item_for", pname)
				-- include the NPC id so that we know which NPC gave it
				meta:set_string("yl_speak_up:quest_item_from", tostring(n_id))
				-- extend quest_id by NPC id so that it becomes more uniq
				meta:set_string("yl_speak_up:quest_id",
					tostring(n_id).." "..tostring(data.item_quest_id))
			end
			v[ id_prefix.."item_quest_id" ] = data.item_quest_id
		end
		if( v["a_item_quest_id"] and not(v[ "a_item_desc"]) and what_type == "npc_gives") then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[9,2.5]"..
					"label[0.2,0.5;You can't set a special quest ID without "..
					"also changing\nthe description. The player would be "..
					"unable to tell\nthe quest item and normal items "..
					"apartapart.]"..
					"button[1.5,2.0;2,0.9;back_from_error_msg;Back]"})
			return
		end
		if(data.item_group and data.item_group ~= ""
		   and data.item_group ~= "- no, just this one item -") then
			v["p_item_group"] = data.item_group
		end
		v["p_item_stack_size"] = data.item_stack_size
		v["p_match_stack_size"] = data.match_stack_size
		local player_inv = player:get_inventory()
		if(not(player_inv:room_for_item("main", stack))) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[9,2.5]"..
					"label[0.2,0.5;You have no room in your inventory for "..
					"the example\nitem. Please make room so that it can be"..
					"given back to you!]"..
					"button[1.5,2.0;2,0.9;back_from_error_msg;Back]"})
			return
		end
		player_inv:add_item("main", stack)
		trade_inv:remove_item(trade_inv_list, stack)
		-- just send a message that the save was successful and give the player time to
		-- take his items back
		show_save_msg = "size[9,2.5]"..
			"label[0.2,0.5;The information was saved successfully.\n"..
				"The item has been returned to your inventory.]"..
			"button[1.5,2.0;2,0.9;back_from_saving;Back]"
	-- "The player has to manually enter a password or passphrase or some other text.", -- 6
	elseif(what_type == "text_input" and id_prefix == "a_") then
		if(not(data.quest_question)) then
			data.quest_question = "Your answer:"
		end
		v[ "a_question" ] = data.quest_question
		-- the player setting this up needs to provide the correct answer
		if(not(data.quest_answer)) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[9,2.5]"..
					"label[0.2,0.5;Error: Please provide the correct answer!\n"..
					"The answer the player gives is checked against this.]"..
					"button[1.5,2.0;2,0.9;back_from_error_msg;Back]"})
			return
		end
		v[ "a_value" ] = data.quest_answer
		if(not(data.action_failure_dialog)) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[9,2.5]"..
					"label[0.2,0.5;Error: Please provide a target dialog if "..
					"the player gives the wrong answer.]"..
					"button[1.5,2.0;2,0.9;back_from_error_msg;Back]"})
			return
		end
		local dialog = yl_speak_up.speak_to[pname].dialog
		local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
		v[ "a_on_failure" ] = sorted_dialog_list[ data.action_failure_dialog ]
	elseif(what_type == "deal_with_offered_item" and id_prefix == "r_") then
		if(not(data.select_deal_with_offered_item) or data.select_deal_with_offered_item < 2) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[9,2.5]"..
					"label[0.2,0.5;Error: Please select what the NPC shall do!]"..
					"button[1.5,2.0;2,0.9;back_from_error_msg;Back]"})
			return
		end
		v[ "r_value" ] = yl_speak_up.dropdown_values_deal_with_offered_item[data.select_deal_with_offered_item]
	-- "Call custom functions that are supposed to be overridden by the server.", --
	-- precondition: 9; action: 7; effect: 13
	elseif((id_prefix == "a_" and what_type == "custom")
	    or (id_prefix == "p_" and what_type == "custom")
	    or (id_prefix == "r_" and what_type == "custom")) then
		v[ id_prefix.."value" ] = data.custom_param
		if(id_prefix == "a_") then
			local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
			v[ "a_on_failure" ] = sorted_dialog_list[ data.action_failure_dialog ]
		end
	-- "The preconditions of another dialog option are fulfilled/not fulfilled.", -- 10
	-- precondition: 10
	elseif(what_type == "other" and id_prefix == "p_") then
		if(data.other_o_id and data.other_o_id ~= "-select-") then
			v[ "p_value" ] = data.other_o_id
		end
		if(data.fulfilled  and data.fulfilled  ~= "-select-") then
			v[ "p_fulfilled" ] = data.fulfilled
		end
	elseif(what_type == "true") then
		v[ "p_value" ] = true -- doesn't matter here - just *some* value
	elseif(what_type == "false") then
		v[ "p_value" ] = true -- doesn't matter here - just *some* value
	end
	v[ "alternate_text" ] = data.alternate_text
	-- only save if something was actually selected
	if(v[ id_prefix.."value"]) then
		if(not(dialog.n_dialogs[d_id].d_options[o_id][ element_list_name ])) then
			dialog.n_dialogs[d_id].d_options[o_id][ element_list_name ] = {}
		end
		-- store the change in the dialog
		dialog.n_dialogs[d_id].d_options[o_id][ element_list_name ][ x_id ] = v
		-- clear up data
		yl_speak_up.speak_to[pname][ id_prefix.."id" ] = nil
		yl_speak_up.speak_to[pname][ tmp_data_cache ] = nil
		-- record this as a change, but do not save do disk yet
		table.insert(yl_speak_up.npc_was_changed[ n_id ],
			"Dialog "..tostring(d_id)..": "..element_desc.." "..tostring(x_id)..
			" added/changed for option "..tostring(o_id)..".")
		if(show_save_msg) then
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = show_save_msg})
			return
		end
		-- TODO: when trying to save: save to disk as well?
		-- show the new/changed precondition
		yl_speak_up.show_fs(player, formspec_input_to, x_id)
		return
	else
		-- make sure the player is informed that saving failed
		yl_speak_up.show_fs(player, "msg", {
			input_to = "yl_speak_up:"..formspec_input_to,
			formspec = "size[8,2]"..
				"label[0.2,0.5;Error: There is no \""..tostring(id_prefix)..
					"value\" set.\n"..
					"\nCould not save.]"..
				"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
		return
	end
end
-- These two functions
--   * yl_speak_up.input_fs_edit_option_related and
--   * yl_speak_up.get_fs_edit_option_related
-- are very similar for preconditions and effects. Therefore they're called here
-- with a lot of parameters. fs_edit_preconditions.lua and fs_edit_effects.lua
-- contain only wrappers.
yl_speak_up.input_fs_edit_option_related = function(player, formname, fields,
		id_prefix, element_list_name, max_entries_allowed,
		element_desc, tmp_data_cache,
		text_ask_for_punching,
		values_what, values_operator, values_block, values_trade, values_inv,
		check_what, check_operator, check_block, check_trade, check_inv,
		get_sorted_player_var_list_function,
		formspec_input_to
		)
	if(not(player)) then
		return
	end
	local pname = player:get_player_name()
	-- what are we talking about?
	local n_id = yl_speak_up.speak_to[pname].n_id
	local d_id = yl_speak_up.speak_to[pname].d_id
	local o_id = yl_speak_up.speak_to[pname].o_id
	local x_id = yl_speak_up.speak_to[pname][ id_prefix.."id"]
	-- this only works in edit mode
	if(not(n_id) or yl_speak_up.edit_mode[pname] ~= n_id) then
		return
	end
	if(fields.back_from_cannot_be_edited
	 or fields.back_from_show_var_usage) then
		yl_speak_up.show_fs(player, formspec_input_to, x_id)
		return
	end
	-- clear editing cache tmp_data_cache for all other types
	if(id_prefix ~= "p_") then
		yl_speak_up.speak_to[pname][ "tmp_prereq" ] = nil
	end
	if(id_prefix ~= "a_") then
		yl_speak_up.speak_to[pname][ "tmp_action" ] = nil
	end
	if(id_prefix ~= "r_") then
		yl_speak_up.speak_to[pname][ "tmp_effect" ] = nil
	end
	-- delete precondition, action or effect
	if(fields.delete_element) then
		yl_speak_up.delete_element_p_or_a_or_e( player, pname, n_id, d_id, o_id, x_id, id_prefix,
				element_list_name, element_desc, formspec_input_to)
		return
	end
	if(fields.select_block_pos) then
		minetest.chat_send_player(pname, text_ask_for_punching)
		-- this formspec expects the block punch:
		yl_speak_up.speak_to[pname].expect_block_punch = formspec_input_to
		return
	end
	-- field inputs: those do not trigger a sending of the formspec on their own
	local was_changed = false
	-- are we talking about an inventory?
	-- (inventory only applies to preconditions; not effects)
	local data = yl_speak_up.speak_to[pname][ tmp_data_cache ]
	local what_type = ""
	if(data and data.what and values_what[ data.what ]) then
		what_type = values_what[ data.what ]
	end
	if(((fields.inv_stack_name and fields.inv_stack_name ~= "")
	  or (fields.store_item_name and fields.store_item_name ~= ""))
	  and data and data.what
	  and ((id_prefix == "p_"
	        and (what_type == "player_inv" or what_type == "npc_inv" or what_type == "block_inv"))
	    -- "give item (created out of thin air) to player (requires npc_master priv)", -- 9
	    -- "take item from player and destroy it (requires npc_master priv)", -- 10
	    or (id_prefix == "r_"
	        and (what_type == "give_item" or what_type == "take_item"
		  or what_type == "put_into_block_inv" or what_type == "take_from_block_inv")))) then
		local wanted = ""
		local wanted_name = ""
		if(not(fields.store_item_name)) then
			local parts = fields.inv_stack_name:split(" ")
			local size = 1
			if(parts and #parts > 1) then
				size = tonumber(parts[2])
				if(not(size) or size < 1) then
					size = 1
				end
			end
			wanted = parts[1].." "..tostring(size)
			wanted_name = parts[1]
		else
			local trade_inv = minetest.get_inventory({type="detached",
						name="yl_speak_up_player_"..pname})
			if(not(trade_inv) or trade_inv:is_empty("npc_wants", 1)) then
				-- show error message
				yl_speak_up.show_fs(player, "msg", {
					input_to = "yl_speak_up:"..formspec_input_to,
					formspec = "size[8,2]"..
						"label[0.2,0.0;Please put an item(stack) into the slot "..
							"next to the\n\"Store\" button first!]"..
						"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
				return
			end
			local stack = trade_inv:get_stack("npc_wants", 1)
			wanted = stack:get_name().." "..stack:get_count()
			wanted_name = stack:get_name()
		end
		-- does the item exist?
		if(minetest.registered_items[ wanted_name ]) then
			data.inv_stack_name = wanted
			fields.inv_stack_name = wanted
		else
			-- show error message
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = "size[8,2]"..
					"label[0.2,0.5;Error: \""..
					minetest.formspec_escape(wanted)..
					"\" is not a valid item(stack).]"..
					"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
			return
		end
	elseif(fields.select_deal_with_offered_item and fields.select_deal_with_offered_item ~= "") then
		data.select_deal_with_offered_item = table.indexof(
				yl_speak_up.dropdown_list_deal_with_offered_item,
				fields.select_deal_with_offered_item)
	elseif(fields.select_accept_group and fields.select_accept_group ~= ""
	  and data and data.what and what_type == "player_offered_item" and id_prefix == "p_") then
		data.item_group = fields.select_accept_group
	elseif(fields.select_match_stack_size and fields.select_match_stack_size ~= ""
	  and data and data.what and what_type == "player_offered_item" and id_prefix == "p_") then
		data.match_stack_size = fields.select_match_stack_size:split(" ")[1]
	-- comparison value for a variable (same for both preconditions and effects)
	-- (also used for checking return values of functions and property values)
	elseif(fields.var_cmp_value
	  and data and data.what
	  and (what_type == "state" or what_type == "property" or what_type == "evaluate")) then
		data.var_cmp_value = fields.var_cmp_value
		was_changed = true
	-- text for a chat message
	elseif(fields.chat_msg_text
	  and data and data.what and what_type == "chat_all" and id_prefix == "r_") then
		data.chat_msg_text = fields.chat_msg_text
		was_changed = true
	elseif(fields.custom_param
	  and fields.custom_param ~= "- Insert a text that is passed on to your function here -"
	  and fields.custom_param ~= ""
	  and data and data.what
	  and ((id_prefix == "a_" and what_type == "custom")
	    or (id_prefix == "p_" and what_type == "custom")
	    or (id_prefix == "r_" and what_type == "custom"))) then
		data.custom_param = fields.custom_param
		was_changed = true
	elseif(fields.action_item_quest_id
	  and fields.action_item_quest_id ~= ""
	  and fields.action_item_quest_id ~= "- none set -"
	  and data and data.what and what_type == "npc_gives" and id_prefix == "a_") then
		data.item_quest_id = fields.action_item_quest_id
		was_changed = true
	end
	-- action_item_quest_id and action_item_desc can be set at the same time
	if(fields.action_item_desc
	  and fields.action_item_desc ~= ""
	  and fields.action_item_desc ~= "- no item set -"
	  and data and data.what and what_type == "npc_gives" and id_prefix == "a_") then
		-- TODO: check if it diffrent from the default one of the stack
		data.item_desc = fields.action_item_desc
		was_changed = true
	end
	if(fields.quest_question
	  and fields.quest_question ~= ""
	  and data and data.what and what_type == "text_input" and id_prefix == "a_") then
		data.quest_question = fields.quest_question
		was_changed = true
	end
	-- quest question and answer can be given with the same press of the save button
	if(fields.quest_answer
	  and fields.quest_answer ~= "- Insert the correct answer here -"
	  and fields.quest_answer ~= ""
	  and data and data.what and what_type == "text_input" and id_prefix == "a_") then
		data.quest_answer = fields.quest_answer
		was_changed = true
	end
	-- "move the player to a given position (requires npc_master priv)", -- 11
	if(fields.move_to_x or fields.move_to_y or fields.move_to_z) then
		local dimension = {"x","y","z"}
		for i, dim in ipairs(dimension) do
			local text = fields["move_to_"..dim]
			if(text and text ~= "") then
				local val = tonumber(text)
				if(not(val) or val < -32000 or val > 32000) then
					yl_speak_up.show_fs(player, "msg", {
						input_to = "yl_speak_up:"..formspec_input_to,
						formspec = "size[9,2]"..
							"label[0.2,0.5;Error: The coordinate values have "..
								"be in the range of -32000..32000.]"..
							"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
					return
				else
					data[ "move_to_"..dim ] = val
				end
			end
		end
	end
	-- lua code
	if(fields.lua_code) then
		data.lua_code = fields.lua_code
	end
	-- if the type of operator is changed: store any new values that may need storing
	if(what_type == "evaluate"
	   and (fields.set_param0 or fields.set_param1 or fields.set_param2 or fields.set_param3
	     or fields.set_param4 or fields.set_param5 or fields.set_param6 or fields.set_param7
	     or fields.set_param8 or fields.set_param9)) then
		for i = 1, 9 do
			local pn = "param"..tostring(i)
			if(fields["set_"..pn]) then
				if(data[pn] ~= fields["set_"..pn]) then
					data[pn] = fields["set_"..pn]
					was_changed = true
				end
			end
		end
	end
	-- the save button was pressed
	if(fields.save_element and data and data.what and values_what[ data.what ]) then
		local v = yl_speak_up.save_element_p_or_a_or_e(
			player, pname, n_id, d_id, o_id, x_id, id_prefix, tmp_data_cache,
			element_list_name, element_desc, max_entries_allowed,
			values_what, values_operator, values_block, values_trade, values_inv,
			formspec_input_to, data, fields)
		return
	end
	-- selections in a dropdown menu (they trigger sending the formspec)
	-- select a general direction/type first
	-- but *not* when enter was pressed (enter sends them all)
	if(fields.select_what and not(fields.key_enter) and not(fields.store_item_name)) then
		local nr = table.indexof(check_what, fields.select_what)
		yl_speak_up.speak_to[pname][ tmp_data_cache ] = { what = nr }
	end
	-- select a subtype for the "a trade" selection
	if(fields.select_trade) then
		local nr = table.indexof(check_trade, fields.select_trade)
		yl_speak_up.speak_to[pname][ tmp_data_cache ].trade = nr
	end
	-- select a subtype for the inventory selection (player or NPC)
	if(fields.select_inv) then
		local nr = table.indexof(check_inv, fields.select_inv)
		yl_speak_up.speak_to[pname][ tmp_data_cache ].inv = nr
	end
	-- select data regarding a block
	if(fields.select_block) then
		local nr = table.indexof(check_block, fields.select_block)
		yl_speak_up.speak_to[pname][ tmp_data_cache ].block = nr
	end
	-- select data regarding the inventory list of a block
	if(fields.inv_list_name and fields.inv_list_name ~= "") then
		local tmp = yl_speak_up.get_node_inv_lists(
					yl_speak_up.speak_to[pname][ tmp_data_cache ].block_pos,
					fields.inv_list_name)
		-- if that inventory list really exists in that block: all ok
		if(tmp and tmp.index ~= "" and tmp.index ~= "1") then
			yl_speak_up.speak_to[pname][ tmp_data_cache ].inv_list_name = fields.inv_list_name
		end
	end
	-- select data regarding a variable
	if(fields.select_variable) then
		-- get the list of available variables (with the same elements
		-- and the same sort order as when the dropdown was displayed)
		local var_list = get_sorted_player_var_list_function(pname)
		yl_speak_up.strip_pname_from_varlist(var_list, pname)
		local nr = table.indexof(var_list, fields.select_variable)
		if(nr) then
			yl_speak_up.speak_to[pname][ tmp_data_cache ].variable = nr
			yl_speak_up.speak_to[pname][ tmp_data_cache ].variable_name = var_list[ nr ]
		end
	end
	-- select data regarding an operator
	if(fields.select_operator) then
		local nr = table.indexof(check_operator, fields.select_operator)
		yl_speak_up.speak_to[pname][ tmp_data_cache ].operator = nr
	end
	-- "the value of a property of the NPC (for generic NPC)"
	if(fields.property and fields.property ~= "") then
		yl_speak_up.speak_to[pname][ tmp_data_cache ].property = fields.property
	end
	-- "something that has to be calculated or evaluated (=call a function)"
	if(fields.select_function_name and fields.select_function_name ~= "") then
		for k, v in pairs(yl_speak_up["custom_functions_"..id_prefix]) do
			if(v["description"] == fields.select_function_name) then
				yl_speak_up.speak_to[pname][ tmp_data_cache ].function_name = k
			end
		end
	end
	-- "something that has to be calculated or evaluated (=call a function)"
	if(fields.evaluate and fields.evaluate ~= "") then
		yl_speak_up.speak_to[pname][ tmp_data_cache ].evaluate = fields.evaluate
	end
	for i = 1,9 do
		local s = "param"..tostring(i)
		if(fields[s] and fields[s] ~= "") then
			yl_speak_up.speak_to[pname][ tmp_data_cache ][s] = fields[s]
		end
	end
	-- another dialog option is true or false
	-- Note: "-select-" can be choosen here as well
	if(fields.select_other_o_id and fields.select_other_o_id ~= "") then
		yl_speak_up.speak_to[pname][ tmp_data_cache ].other_o_id = fields.select_other_o_id
	end
	-- Note: "-select-" can be choosen here as well
	if(fields.select_fulfilled and fields.select_fulfilled ~= "") then
		yl_speak_up.speak_to[pname][ tmp_data_cache ].fulfilled = fields.select_fulfilled
	end
	if(fields.select_on_failure) then
		-- in this case we really want the name of the target dialog
		yl_speak_up.speak_to[pname][ tmp_data_cache ].on_failure = fields.select_on_failure
	end
	if(fields.select_on_action_failure
	  and data and data.what and id_prefix == "a_") then
		local dialog = yl_speak_up.speak_to[pname].dialog
		local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
		local nr = table.indexof(sorted_dialog_list, fields.select_on_action_failure)
		yl_speak_up.speak_to[pname][ tmp_data_cache ].action_failure_dialog = nr
	end
	-- new variables have to be added (and deleted) somewhere after all
	if(fields.manage_variables) then
		-- remember which formspec we are comming from
		yl_speak_up.speak_to[pname][ "working_at" ] = formspec_input_to
		if(data.variable) then
			yl_speak_up.speak_to[pname].tmp_index_variable = data.variable - 1
		end
		yl_speak_up.show_fs(player, "manage_variables")
		return
	end
	-- handle editing and changing of alternate texts for actions
	if(  fields.button_edit_action_on_failure_text_change
	  or fields.button_edit_effect_on_failure_text_change
	  or fields.turn_alternate_text_into_new_dialog
	  or fields.save_dialog_modification) then
		yl_speak_up.handle_edit_actions_alternate_text(
			player, pname, n_id, d_id, o_id, x_id, id_prefix,
			formspec_input_to, data, fields, tmp_data_cache)
		if(not(fields.save_dialog_modification)
		  and not(fields.turn_alternate_text_into_new_dialog)) then
			return
		end
		was_changed = true
	-- we are back from that submenu
	elseif(fields.back_from_edit_dialog_modification) then
		was_changed = true
	end
	-- show var usage - starting from clicking on a precondition or effect in the
	-- edit options menu and viewing the list containing that selected element
	if( fields.show_var_usage and x_id) then
		local dialog = yl_speak_up.speak_to[pname].dialog
		local element = dialog.n_dialogs[d_id].d_options[o_id][ element_list_name ][ x_id ]
		if(element and element[ id_prefix.."variable"]) then
			local effect_name = "(Ef)fect"
			if(id_prefix == "p_") then
				effect_name = "pre(C)ondition"
			end
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = yl_speak_up.fs_get_list_of_usage_of_variable(
					element[ id_prefix.."variable"], pname, true,
					"back_from_show_var_usage",
					"Back to select "..effect_name.." "..tostring(x_id)..
						" of option "..tostring(o_id)..
						" of dialog "..tostring(d_id),
					-- internal variable?
					(data and data.variable and data.variable < 3))
				})
			return
		end
	-- show var usuage - but this time from the edit dialog for that precondition or effect
	elseif(fields.show_var_usage_edit_element and x_id) then
		local dialog = yl_speak_up.speak_to[pname].dialog
		local element = nil
		-- x_id may be "new" and this may be the first element in element_list_name
		if(dialog.n_dialogs[d_id].d_options[o_id][ element_list_name ]) then
			element = dialog.n_dialogs[d_id].d_options[o_id][ element_list_name ][ x_id ]
		end
		if(not(element) or data.variable_name) then
			element = {}
			element[ id_prefix.."variable"] = data.variable_name
		end
		if(element and element[ id_prefix.."variable"]) then
			local effect_name = "(Ef)fect"
			if(id_prefix == "p_") then
				effect_name = "pre(C)ondition"
			end
			yl_speak_up.show_fs(player, "msg", {
				input_to = "yl_speak_up:"..formspec_input_to,
				formspec = yl_speak_up.fs_get_list_of_usage_of_variable(
					element[ id_prefix.."variable"], pname, true,
					"back_from_error_msg",
					"Back to select "..effect_name.." "..tostring(x_id)..
						" of option "..tostring(o_id)..
						" of dialog "..tostring(d_id),
					-- internal variable?
					(data and data.variable and data.variable < 3))
				})
			return
		end
	-- allow to delete unused variables
	elseif(fields.delete_unused_variable) then
		-- try to delete the variable (button comes from the show usage of variable formspec)
		local text = yl_speak_up.del_quest_variable(pname, data.variable_name, nil)
		yl_speak_up.show_fs(player, "msg", {
			input_to = "yl_speak_up:"..formspec_input_to,
			formspec = "size[10,2]"..
				"label[0.2,0.0;Trying to delete variable \""..
					minetest.formspec_escape(tostring(data.variable_name))..
					"\":\n"..text.."]"..
				"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
		return
	end
	-- the player wants to change/edit a precondition or effect
	if(not(fields.back)
	  and (fields.change_element or fields.select_what or fields.select_trade
	  or fields.select_inv or fields.select_block
	  or fields.inv_list_name
	  or fields.select_deal_with_offered_item
	  or fields.select_accept_group
	  or fields.select_match_stack_size
	  or fields.select_variable or fields.select_operator
	  or fields.select_on_failure
	  or fields.select_on_action_failure
	  or fields.back_from_error_msg
	  or fields.store_item_name
	  or fields.select_other_o_id
	  or fields.select_fulfilled
	  or fields.select_function_name
	  or was_changed
	  or fields.key_enter
	  or fields.quit
	  -- return was pressed
	  or fields.key_enter_field)) then
		yl_speak_up.show_fs(player, formspec_input_to)
		return
	end
	-- go back to the edit option dialog
	yl_speak_up.show_fs(player, "edit_option_dialog",
		{n_id = n_id, d_id = d_id, o_id = o_id, caller= formspec_input_to})
end
yl_speak_up.get_fs_edit_option_related = function(player, table_click_result,
		id_prefix, element_list_name, max_entries_allowed,
		element_desc, tmp_data_cache,
		what_do_you_want_txt,
		values_what, values_operator, values_block, values_trade, values_inv,
		check_what, check_operator, check_block, check_trade, check_inv,
		get_sorted_player_var_list_function,
		show_element_function,
		table_of_name,
		text_variable, text_select_operator, text_select_value,
		text_block_position)
	if(not(player)) then
		return ""
	end
	local pname = player:get_player_name()
	-- what are we talking about?
	local n_id = yl_speak_up.speak_to[pname].n_id
	local d_id = yl_speak_up.speak_to[pname].d_id
	local o_id = yl_speak_up.speak_to[pname].o_id
	local x_id = yl_speak_up.speak_to[pname][ id_prefix.."id" ]
	-- this only works in edit mode
	if(not(n_id) or yl_speak_up.edit_mode[pname] ~= n_id) then
		return "size[1,1]label[0,0;You cannot edit this NPC.]"
	end
	local dialog = yl_speak_up.speak_to[pname].dialog
	if(not(dialog) or not(dialog.n_dialogs)
	  or not(dialog.n_dialogs[d_id])
	  or not(dialog.n_dialogs[d_id].d_options)
	  or not(dialog.n_dialogs[d_id].d_options[o_id])) then
		return "size[4,1]label[0,0;Dialog option does not exist.]"
	end
	local elements = dialog.n_dialogs[d_id].d_options[o_id][ element_list_name ]
	if(not(elements)) then
		elements = {}
	end
	-- did we arrive here through clicking on an element in the dialog edit options menu?
	if(table_click_result or elements[ table_click_result ]) then
		if(not(elements[ table_click_result ])) then
			-- which element has the player selected?
			local sorted_key_list = yl_speak_up.sort_keys(elements)
			local selected = minetest.explode_table_event(table_click_result)
			-- use "new" if nothing fits
			x_id = "new"
			if((selected.type == "CHG" or selected.type == "DLC")
			  and selected.row <= #sorted_key_list) then
				x_id = sorted_key_list[ selected.row ]
			end
			if( x_id == "new" and #sorted_key_list >= max_entries_allowed) then
				return "size[9,1.5]"..
					"label[0.2,0.0;There are only up to "..
						minetest.formspec_escape(yl_speak_up.max_result_effects)..
						" "..element_desc.."s allowed per dialog option.]"..
						"button[2.0,0.8;1.0,0.9;back;Back]"
			end
		else
			-- allow to directly specify a x_id to show
			x_id = table_click_result
		end
		local show_var_usage = ""
		if(x_id
		  and elements[ x_id ]
		  and elements[ x_id ][ id_prefix.."type"]
		  and elements[ x_id ][ id_prefix.."type"] == "state"
		  and elements[ x_id ][ id_prefix.."variable"]) then
			show_var_usage = "button[12.0,1.8;6.5,0.9;show_var_usage;"..
					"Show where this variable is used]"
		end
		-- store which element we are talking about
		yl_speak_up.speak_to[pname][ id_prefix.."id" ] = x_id
		-- nothing selected yet
		yl_speak_up.speak_to[pname][ tmp_data_cache ] = nil
		-- display the selected element
		if(x_id ~= "new") then
			return "size[20,3]"..
				"bgcolor[#00000000;false]"..
				"label[0.2,0.5;Selected "..element_desc..":]"..
				"tablecolumns[text;color,span=1;text;text]"..
				"table[0.2,0.8;19.6,0.7;"..table_of_name..";"..
					minetest.formspec_escape(elements[ x_id ][ id_prefix.."id"])..
						",#FFFF00,"..
					minetest.formspec_escape(elements[ x_id ][ id_prefix.."type"])..
						","..
					minetest.formspec_escape(
						show_element_function(elements[ x_id ], pname))..";0]"..
				"button[2.0,1.8;1.5,0.9;delete_element;Delete]"..
				"button[4.0,1.8;1.5,0.9;change_element;Change]"..
				"button[6.0,1.8;5.5,0.9;back;Back to edit dialog option \""..
					tostring(o_id).."\"]"..
				show_var_usage
		end
	end
	local data = yl_speak_up.speak_to[pname][ tmp_data_cache ]
	if(not(data) or not(data.what)) then
		data = { what = 1}
	end
	-- fallback
	if(not(x_id)) then
		x_id = "new"
	end
	local e = nil
	-- does the element exist already? if so: use the existing values as presets for data
	-- (so that the element can be edited)
	-- does kind of the opposite than the saving of values starting in line 323 of this file
	if(x_id ~= "new" and data.what == 1 and elements[ x_id ]) then
		e = elements[ x_id ]
		if( id_prefix == "r_" and e[ "r_type" ] == "dialog") then
			-- dialog effects cannot be edited this way
			return "size[9,2]"..
				"label[0.2,0.5;Effects of the type \"dialog\" cannot be edited this way.\n"..
				"Use the edit options or dialog menu to change the target dialog.]"..
				"button[1.5,1.5;2,0.9;back_from_cannot_be_edited;Back]"
		end
		if( id_prefix == "p_" and e[ "p_type" ] == "item") then
			-- the staff-based item precondition can be translated to an editable
			-- inventory precondition which is equal
			e[ "p_type" ] = "player_inv"
			e[ "p_itemstack" ] = e[ "p_value"]
			e[ "p_value" ] = "inv_contains"
		end
		data.what = table.indexof(values_what, e[ id_prefix.."type" ])
		if(data.what == -1) then
			data.what = 1
		-- npc_gives/npc_wants (action)
		-- (two seperate functions, but can be handled here together)
		elseif(data.what and id_prefix == "a_" and (data.what == 4 or data.what == 5)) then
			local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
			data.action_failure_dialog = math.max(1,
					table.indexof(sorted_dialog_list, e[ "a_on_failure" ]))
			-- data.item_string is used to show a background image
			data.item_string = e[ "a_value"] -- stack name and count (as string)
			data.item_desc = e[ "a_item_desc" ]
			data.item_quest_id = e[ "a_item_quest_id" ]
		-- player_offered_item precondition
		elseif(data.what and id_prefix == "p_" and (data.what == 8)) then
			-- data.item_string is used to show a background image
			data.item_string = e[ "p_value"] -- stack name and count (as string)
			data.item_desc = e[ "p_item_desc" ]
			data.item_quest_id = e[ "p_item_quest_id" ]
			data.item_group    = e[ "p_item_group" ]
			data.item_stack_size = e["p_item_stack_size"]
			data.match_stack_size = e["p_match_stack_size"]
		end
		if(e[ "alternate_text"]) then
			data.alternate_text = e[ "alternate_text" ]
		end
		-- write that data back
		yl_speak_up.speak_to[pname][ tmp_data_cache ] = data
	end
	local save_button = "button[5.0,12.2;1,0.7;save_element;Save]"
	local formspec =
		"size[20,13]"..
		"label[5,0.5;Edit "..element_desc.." \""..minetest.formspec_escape(x_id)..
			"\" of option \""..minetest.formspec_escape(tostring(o_id))..
			"\" of dialog \""..minetest.formspec_escape(tostring(d_id)).."\"]"..
		"label[0.2,1.5;"..what_do_you_want_txt.."]"..
		"label[0.2,2.0;Something regarding...]"..
		"dropdown[4.0,1.8;14.0,0.6;select_what;"..
			table.concat(check_what, ",")..";"..
			tostring(data.what)..";]"..
		"button[3.0,12.2;1,0.7;back;Abort]"
	if(id_prefix ~= "a_") then
		formspec = formspec..
			"label[1,10.5;If you are unsure if your setup of pre(C)onditions and (Ef)fects "..
				"works as intended,\ntype \"/npc_talk debug "..tostring(n_id).."\" "..
				"in chat in order to enter debug mode. You can leave it with "..
				"\"/npc_talk debug off\".]"
	end
	if(data.what) then
		yl_speak_up.speak_to[pname][ tmp_data_cache ] = data
	end
	local what_type = ""
	if(data and data.what and values_what[ data.what ]) then
		what_type = values_what[ data.what ]
	end
	-- "an internal state (i.e. of a quest)", -- 2
	-- (state is the second offered option in both preconditions and effects list)
	if(data.what and what_type == "state" and id_prefix ~= "a_") then
		return yl_speak_up.get_fs_edit_option_p_and_e_state(
				pname, dialog, formspec, data, id_prefix, save_button, e,
				text_variable, text_select_value, text_select_operator,
				values_operator, check_operator, get_sorted_player_var_list_function )
	-- "the value of a property of the NPC (for generic NPC)"
	elseif(data.what and what_type == "property" and id_prefix ~= "a_") then
		return yl_speak_up.get_fs_edit_option_p_and_e_property(
				pname, dialog, formspec, data, id_prefix, save_button, e,
				text_select_operator, values_operator, check_operator)
	-- "something that has to be calculated or evaluated (=call a function)"
	elseif(data.what and what_type == "evaluate") then
	return yl_speak_up.get_fs_edit_option_p_and_e_evaluate(
			pname, dialog, formspec, data, id_prefix, save_button, e,
				text_select_operator, values_operator, check_operator)
	-- "a block somewhere", -- 3
	-- (block is the third offered option in both preconditions and effects list)
	elseif(data.what and what_type == "block" and id_prefix ~= "a_") then
		return yl_speak_up.get_fs_edit_option_p_and_e_block(
				pname, dialog, formspec, data, id_prefix, save_button, e,
				text_block_position, values_block, check_block)
	-- "a trade", -- 4
	-- (trade - only for preconditions; effects have something else here)
	elseif(data.what and id_prefix == "p_" and what_type == "trade") then
		return yl_speak_up.get_fs_edit_option_precondition_trade(
				pname, dialog, formspec, data, id_prefix, save_button, e,
				values_trade, check_trade)
	-- "the inventory of the player", -- 5
	-- "the inventory of the NPC", -- 6
	-- "the inventory of a block somewhere", -- 7
	-- "put item from the NPC's inventory into a chest etc.", -- 4  (effect)
	-- "take item from a chest etc. and put it into the NPC's inventory", -- 5 (effect)
	-- (inventory - only for preconditions; effects have something else here)
	elseif((data.what and id_prefix == "p_"
	  and (what_type == "player_inv" or what_type == "npc_inv" or what_type == "block_inv"))
	   or  (data.what and id_prefix == "r_"
	  and (what_type == "put_into_block_inv" or what_type == "take_from_block_inv"))) then
		-- the inventory of a block needs more input options (in particular block selection)
		data.what_type = what_type
		return yl_speak_up.get_fs_edit_option_precondition_inv(
				pname, dialog, formspec, data, id_prefix, save_button, e,
				values_inv, check_inv, values_block)
	elseif(data.what and id_prefix == "r_" and what_type == "deal_with_offered_item") then
		return yl_speak_up.get_fs_edit_option_effect_deal_with_offered_item(
				pname, dialog, formspec, data, id_prefix, save_button, e)
	-- "give item (created out of thin air) to player (requires npc_master priv)", -- 9
	-- "take item from player and destroy it (requires npc_master priv)", -- 10
	elseif(data.what and id_prefix == "r_" and (what_type == "give_item" or what_type=="take_item")) then
		return yl_speak_up.get_fs_edit_option_effect_give_item_or_take_item(
				pname, dialog, formspec, data, id_prefix, save_button, e)
	-- "move the player to a given position (requires npc_master priv)", -- 11
	elseif(data.what and id_prefix == "r_" and what_type == "move") then
		return yl_speak_up.get_fs_edit_option_effect_move(
				pname, dialog, formspec, data, id_prefix, save_button, e)
	-- "execute Lua code (requires npc_master priv)", -- precondition: 8; effect: 12
	elseif((data.what and id_prefix == "p_" and what_type == "function")
	    or (data.what and id_prefix == "r_" and what_type == "function")) then
		return yl_speak_up.get_fs_edit_option_p_and_e_function(
				pname, dialog, formspec, data, id_prefix, save_button, e)
	-- "NPC crafts something", -- 6
	-- (craft - only for effects - not for preconditions)
	elseif(data.what and id_prefix == "r_" and what_type == "craft") then
		return yl_speak_up.get_fs_edit_option_effect_craft(
				pname, dialog, formspec, data, id_prefix, save_button, e)
	-- "go to other dialog if the *previous* effect failed", -- 5
	-- (on_failure - only for effects - not for preconditions)
	elseif(data.what and id_prefix == "r_" and what_type == "on_failure") then
		return yl_speak_up.get_fs_edit_option_effect_on_failure(
				pname, dialog, formspec, data, id_prefix, save_button, e)
	-- "send a chat message to all players" -- 8
	elseif(data.what and id_prefix == "r_" and what_type == "chat_all") then
		return yl_speak_up.get_fs_edit_option_effect_chat_all(
				pname, dialog, formspec, data, id_prefix, save_button, e)
	-- "Normal trade - one item(stack) for another item(stack).", -- 3
	elseif(data.what and id_prefix == "a_" and what_type == "trade") then
		return yl_speak_up.get_fs_edit_option_action_trade(
				pname, dialog, formspec, data, id_prefix, save_button, e)
	-- "The NPC gives something to the player (i.e. a quest item).", -- 4
	-- (only for actions)
	elseif(data.what and id_prefix == "a_" and what_type == "npc_gives") then
		return yl_speak_up.get_fs_edit_option_action_npc_gives(
				pname, dialog, formspec, data, id_prefix, save_button, e)
	-- "The player is expected to give something to the NPC (i.e. a quest item).", -- 5
	-- (only for actions)
	-- "an item the player offered/gave to the NPC", (as precondition)
	elseif(data.what and ((id_prefix == "a_" and what_type == "npc_wants")
	                   or (id_prefix == "p_" and what_type == "player_offered_item"))) then
		return yl_speak_up.get_fs_edit_option_action_npc_wants_or_accepts(
				pname, dialog, formspec, data, id_prefix, save_button, e)
	-- "The player has to manually enter a password or passphrase or some other text.", -- 6
	-- (only for actions)
	elseif(data.what and id_prefix == "a_" and what_type == "text_input") then
		return yl_speak_up.get_fs_edit_option_action_text_input(
				pname, dialog, formspec, data, id_prefix, save_button, e)
	-- "Call custom functions that are supposed to be overridden by the server.", -- 7
	-- precondition: 9; action: 7; effect: 13
	elseif(data.what
	  and ((id_prefix == "a_" and what_type == "custom")
	    or (id_prefix == "p_" and what_type == "custom")
	    or (id_prefix == "r_" and what_type == "custom"))) then
		return yl_speak_up.get_fs_edit_option_all_custom(
				pname, dialog, formspec, data, id_prefix, save_button, e)
	-- "The preconditions of another dialog option are fulfilled/not fulfilled.", -- 10
	-- precondition: 9
	elseif(data.what and id_prefix == "p_" and what_type == "other") then
		return yl_speak_up.get_fs_other_option_preconditions(
				pname, dialog, formspec, data, id_prefix, save_button, e)
	end
	-- create a new precondition, action or effect
	return formspec..save_button
end
----------------------------------------------------------------------------
-- begin of formspecs for types of preconditions, actions and effects
-- helper function for "state", "property" and "evaluate";
-- shows dropdown for operator and input field for comparison value var_cmp_value
yl_speak_up.get_fs_operator_based_comparison = function(data, id_prefix, save_button, e,
					values_operator, check_operator,
					what_is_this, text_what_is_this,
					text_select_operator, text_select_value)
	if(e) then
		data.operator = math.max(1,table.indexof(values_operator, e[ id_prefix.."operator" ]))
		data.var_cmp_value = e[ id_prefix.."var_cmp_value" ]
	end
	if(not(data[what_is_this]) or data[what_is_this] == "" or tostring(data[what_is_this]) == -1
	  or tostring(data[what_is_this]) == "0") then
		-- not enough selected yet for saving
		save_button = ""
	elseif(not(data.operator) or data.operator == 1) then
		data.operator = 1
		save_button = ""
	end
	local field_for_value = "field[11.7,4.8;7.5,0.6;var_cmp_value;;"..
		minetest.formspec_escape(data.var_cmp_value or "- enter value -").."]"
	-- do not show value input field for unary operators
	-- (unary operators are diffrent for prerequirements and effects)
	if(not(data.operator)
	  or (id_prefix == "p_" and (data.operator == 1 or (data.operator>=8 and data.operator<11)))
	  -- "unset", "set_to_current_time"
	  or (id_prefix == "r_" and (data.operator == 3 or data.operator == 4))) then
		field_for_value = "label[11.7,5.1;- not used for this operator -]"
	end
	-- the list of available variables needs to be extended with the ones
	-- the player has read access to, and the order has to be constant
	-- (because dropdown just returns an index)
	return "label[0.2,3.3;"..text_what_is_this.."]"..
		"label[0.2,4.3;Name of "..what_is_this..":]"..
		"label[7.0,4.3;"..text_select_operator.."]"..
		"dropdown[7.0,4.8;4.5,0.6;select_operator;"..
			table.concat(check_operator, ",")..";"..
			tostring(data.operator)..";]"..
		"label[11.7,4.3;"..text_select_value.."]"..
		field_for_value..
		save_button
end
-- "an internal state (i.e. of a quest)", -- 2
-- (state is the second offered option in both preconditions and effects list)
yl_speak_up.get_fs_edit_option_p_and_e_state = function(
			pname, dialog, formspec, data, id_prefix, save_button, e,
			text_variable, text_select_value, text_select_operator,
			values_operator, check_operator, get_sorted_player_var_list_function )
	-- the list of available variables needs to be extended with the ones
	-- the player has read access to, and the order has to be constant
	-- (because dropdown just returns an index)
	local var_list = get_sorted_player_var_list_function(pname)
	local var_list_stripped = yl_speak_up.strip_pname_from_varlist(var_list, pname)
	if(e) then
		data.variable_name = yl_speak_up.strip_pname_from_var(e[ id_prefix.."variable" ], pname)
		data.variable = table.indexof(var_list, e[ id_prefix.."variable"])
	end
	if(not(data.variable) or data.variable < 1) then
		data.variable = 0
	end
	return formspec..
		yl_speak_up.get_fs_operator_based_comparison(data, id_prefix, save_button, e,
			values_operator, check_operator, "variable", text_variable,
			text_select_operator, text_select_value)..
		"dropdown[0.2,4.8;6.5,0.6;select_variable;"..
			"- please select -"..var_list_stripped..";"..
			tostring(data.variable + 1)..";]"..
		"button[0.2,6.0;4.0,0.6;manage_variables;Manage variables]"..
		"button[4.7,6.0;6.5,0.6;show_var_usage_edit_element;Show where this variable is used]"..
		"hypertext[1.2,7.0;16.0,2.5;some_text;"..
			"Note: Each variable is player-specific and will be set and "..
			"checked for the player that currently talks to your NPC.\n"..
			"Note: You can set a variable to the current time in an effect. "..
			"After that, use a precondition to check if that variable was set \"more "..
			"than x seconds ago\" or \"less than x seconds ago\". This can be "..
			"useful for prevending your NPC from handing out the same quest item again "..
			"too quickly (players are inventive and may use your quest item for their "..
			"own needs).\n]"
end
-- "the value of a property of the NPC (for generic NPC)"
yl_speak_up.get_fs_edit_option_p_and_e_property = function(
			pname, dialog, formspec, data, id_prefix, save_button, e,
			text_select_operator, values_operator, check_operator)
	if(e) then
		data.property = e[ id_prefix.."value"]
	end
	local operator_list = {}
	for i, v in ipairs(check_operator) do
		local v2 = values_operator[i]
		if(    v2 ~= "quest_step_done" and v2 ~= "quest_step_not_done"
		   and v2 ~= "true_for_param"  and v2 ~= "false_for_param") then
			table.insert(operator_list, v)
		end
	end
	local text_compare_with = "Compare property with this value:"
	if(id_prefix == "r_") then
		text_select_operator = "Set property to:"
		text_compare_with = "New value:"
	end
	-- the list of available variables needs to be extended with the ones
	return formspec..
		yl_speak_up.get_fs_operator_based_comparison(data, id_prefix, save_button, e,
			values_operator, operator_list, "property",
			"The NPC shall have the following property:",
			text_select_operator, text_compare_with)..
		"field[1.0,4.8;5.0,0.6;property;;"..
			minetest.formspec_escape(data.property or "- enter name -").."]"..
		"hypertext[1.2,7.0;16.0,2.5;some_text;"..
			"Note: Properties are useful for NPC that have a generic "..
			"behaviour and may vary their behaviour slightly.\n"..
			"Properties starting with \"server\" can only be set or changed by "..
			"players with the \"npc_master\" privilege."..
			"]"
end
-- "something that has to be calculated or evaluated (=call a function)"
yl_speak_up.get_fs_edit_option_p_and_e_evaluate = function(
			pname, dialog, formspec, data, id_prefix, save_button, e,
			text_select_operator, values_operator, check_operator)
	local fun_list = {}
	for k, v in pairs(yl_speak_up["custom_functions_"..id_prefix]) do
		table.insert(fun_list, v["description"] or k)
	end
	table.sort(fun_list)
	local func_selected = 0
	local func_data = nil
	if(e) then
		--data.function_name = e[ id_prefix.."value"]
		data.function_name = e[ id_prefix.."value"]
		for i = 1, 9 do
			local s = "param"..tostring(i)
			if(e[id_prefix..s]) then
				data[s] = e[id_prefix..s]
			end
		end
	end
	local add_description = "Nothing selected."
	if(data.function_name) then
		func_data = yl_speak_up["custom_functions_"..id_prefix][data.function_name]
		-- add the fields for param1..param9:
		if(func_data) then
			local xoff = 0
			for i = 1, 9 do
				if(i > 5) then
					xoff = 10
				end
				local paramn = "param"..tostring(i)
				local s = func_data[paramn.."_text"]
				if(s) then
					formspec = formspec..
						"label["..(0.2 + xoff)..","..(6.05 + ((i-1)%5)*0.8)..";"..
							minetest.formspec_escape(s).."]"..
						"field["..(4.0 + xoff)..","..(5.8 + ((i-1)%5)*0.8)..
							";5.0,0.6;set_"..paramn..";;"..
							minetest.formspec_escape(
								data[paramn] or "").."]"..
						"tooltip[set_"..paramn..";"..
							minetest.formspec_escape(
								func_data[paramn.."_desc"] or "?").."]"
				end
			end
			func_selected = table.indexof(fun_list, func_data["description"])
			add_description = func_data["description"]
			-- necessary so that the save_button can be shown
			data["function"] = func_selected
		end
	end
	local operator_list = {}
	for i, v in ipairs(check_operator) do
		local v2 = values_operator[i]
		if(    v2 ~= "quest_step_done" and v2 ~= "quest_step_not_done"
		   and v2 ~= "true_for_param"  and v2 ~= "false_for_param") then
			table.insert(operator_list, v)
		end
	end
	local text_operator_and_comparison = ""
	local explanation = ""
	local dlength = "6.5"
	if(id_prefix == "p_") then
		text_operator_and_comparison = yl_speak_up.get_fs_operator_based_comparison(
			data, id_prefix, save_button, e,
			values_operator, operator_list, "function",
			"Execute and evaluate the following function:",
			"Operator for checking result:", "Compare the return value with this value:")
		explanation =
			"Note: Functions are called with parameters which are passed on to them. "..
			"The function then calculates a result. This result can be compared to a given "..
			"value. What the function calculates and what it returns depends on its "..
			"implementation."
	else
		dlength = "15" -- we have more room for the dropdown here
		if(not(data["function"]) or data["function"]=="") then
			save_button = ""
		end
		text_operator_and_comparison =
			"label[0.2,3.3;Execute the following function:]"..
			"label[0.2,4.3;Name of function:]"..
			save_button
		explanation =
			"Note: Functions are called with parameters which are passed on to them. "..
			"Functions used as effects/results ought to change something, i.e. set a "..
			"variable to a new value."
		if(id_prefix == "a_") then
			explanation =
				"Note: Functions are called with parameters which are passed on to "..
				"them. Functions used as actions need to return a valid formspec. This "..
				"formspec is then displayed to the player. The clicks the player does in "..
				"that formspec are sent to another custom function linked to it."
		end
	end
	-- the list of available variables needs to be extended with the ones
	return formspec..
		text_operator_and_comparison..
		-- show the description of the function again (the space in the dropdown menu is a bit
		-- limited)
		"label[7.5,3.3;"..minetest.formspec_escape(add_description).."]"..
		"dropdown[0.2,4.8;"..dlength..",0.6;select_function_name;"..
			"- please select -,"..table.concat(fun_list, ",")..";"..
			tostring(func_selected + 1)..";]"..
		"hypertext[1.2,9.6;16.0,2.5;some_text;"..
			explanation..
			"]"
end
-- helper function for:
--    yl_speak_up.get_fs_edit_option_p_and_e_block
yl_speak_up.get_block_pos_info = function(pname, data, id_prefix, e, values_block, ignore_protection)
	-- are we more intrested in the inventory of the block or in the block itself?
	local looking_at_inventory = false
	if(data and data.what_type
	  and (data.what_type == "block_inv"
	    or data.what_type == "put_into_block_inv"
	    or data.what_type == "take_from_block_inv")) then
		looking_at_inventory = true
	end
	-- did the player get here through punching a block in the meantime?
	local block_pos = yl_speak_up.speak_to[pname].block_punched
	yl_speak_up.speak_to[pname].block_punched = nil
	if(e and e[id_prefix.."pos"]) then
		-- if we are not looking for the inventory of a block:
		if(looking_at_inventory) then
			data.block = math.max(1,table.indexof(values_block, e[ id_prefix.."value" ]))
		end
		data.node_data = {}
		data.node_data.data = e[ id_prefix.."node" ]
		data.node_data.param2 = e[ id_prefix.."param2" ]
		data.block_pos = {x=e[ id_prefix.."pos" ].x,
				  y=e[ id_prefix.."pos" ].y,
				  z=e[ id_prefix.."pos" ].z}
		-- the block below was punched
		if(id_prefix == "p_" and data.block == 5) then
			data.block_pos.y = data.block_pos.y - 1
		end
	end
	local block_pos_str = "- none set -"
	local node = {name = "- unknown -", param2 = "- unkown -"}
	if(not(block_pos) and data and data.block_pos) then
		block_pos = data.block_pos
	end
	local error_is_protected = ""
	if(block_pos) then
		-- store for later usage
		data.block_pos = block_pos
		local tmp_pos = {x=block_pos.x, y=block_pos.y, z=block_pos.z}
		-- "I can't punch it. The block is as the block *above* the one I punched.",
		-- (only valid for preconditions; not for effects - because the player and
		-- his NPC need to be able to build there)
		if(data.block and id_prefix == "p_" and data.block == 5) then
			tmp_pos.y = block_pos.y + 1
		end
		-- effects (and, likewise, preconditions): the player at least has to be able to
		-- build at that position - check that
		if(not(ignore_protection) and minetest.is_protected(tmp_pos, pname)) then
			error_is_protected = "label[0.2,7.8;Error: "..
				"The position you punched is protected. It cannot be used by "..
				"your NPC for checks or building. Please select a diffrent block!]"
			block_pos = nil
			data.block_pos = nil
		else
			block_pos_str = minetest.pos_to_string(tmp_pos)
			node = minetest.get_node_or_nil(tmp_pos)
			if(not(node)) then
				node = {name = "- unknown -", param2 = "- unkown -"}
			end
			-- "There shall be air instead of this block.",
			-- (only valid for preconditions)
			if(data.block and id_prefix == "p_" and data.block == 3) then
				node = {name = "air", param2 = 0}
			end
			-- cache that (in case a sapling grows or someone else changes it)
			data.node_data = node
		end
	end
	local show_save_button = true
	if(node.name == "- unknown -") then
		show_save_button = false
	end
	-- if we are dealing with the *inventory* of a block, the state of the block is of no intrest here
	if(not(looking_at_inventory) and (not(data.block) or data.block == 1)) then
		data.block = 1
		-- not enough selected yet for saving
		show_save_button = false
	end
	return {block_pos = block_pos, block_pos_str = block_pos_str, node = node,
		error_is_protected = error_is_protected,
		show_save_button = show_save_button}
end
-- "a block somewhere", -- 3
-- (block is the third offered option in both preconditions and effects list)
yl_speak_up.get_fs_edit_option_p_and_e_block = function(
			pname, dialog, formspec, data, id_prefix, save_button, e,
			text_block_position, values_block, check_block)
	local res = yl_speak_up.get_block_pos_info(pname, data, id_prefix, e, values_block, false)
	if(not(res.show_save_button)) then
		save_button = ""
	end
	return formspec..
		"label[0.2,3.3;"..text_block_position.."]"..
		"dropdown[4.0,3.5;16.0,0.6;select_block;"..
			table.concat(check_block, ",")..";"..
			tostring(data.block)..";]"..
		"label[0.2,4.8;Position of the block:]"..
		"label[4.0,4.8;"..minetest.formspec_escape(res.block_pos_str).."]"..
		"label[0.2,5.8;Name of block:]"..
		"label[4.0,5.8;"..minetest.formspec_escape(res.node.name).."]"..
		"label[0.2,6.8;Orientation (param2):]"..
		"label[4.0,6.8;"..minetest.formspec_escape(res.node.param2).."]"..
		"button_exit[10.0,5.5;4.0,0.7;select_block_pos;Set position of block]"..
		"tooltip[select_block_pos;Click on this button to select a block.\n"..
			"This menu will close and you will be asked to punch\n"..
			"the block at the position you want to check or change.\n"..
			"After punching it, you will be returned to this menu.]"..
		res.error_is_protected..
		save_button
end
-- "a trade", -- 4
-- (trade - only for preconditions; effects have something else here)
yl_speak_up.get_fs_edit_option_precondition_trade = function(
			pname, dialog, formspec, data, id_prefix, save_button, e,
			values_trade, check_trade)
	if(e) then
		data.trade = math.max(1,table.indexof(values_trade, e[ "p_value" ]))
	end
	if(not(data.trade) or data.trade == 1) then
		data.trade = 1
		-- not enough selected yet for saving
		save_button = ""
	end
	return formspec..
		"label[0.2,3.3;If the action is a trade, the following shall be true:]"..
		"dropdown[4.0,3.5;16.0,0.6;select_trade;"..
			table.concat(check_trade, ",")..";"..
			tostring(data.trade)..";]"..
		save_button
end
-- "the inventory of the player", -- 5
-- "the inventory of the NPC", -- 6
-- "the inventory of a block somewhere", -- 7
-- "put item from the NPC's inventory into a chest etc.", -- 4  (effect)
-- "take item from a chest etc. and put it into the NPC's inventory", -- 5 (effect)
-- (inventory - only for preconditions; effects have something else here)
yl_speak_up.get_fs_edit_option_precondition_inv = function(
			pname, dialog, formspec, data, id_prefix, save_button, e,
			values_inv, check_inv, values_block)
	if(e) then
		data.inv = math.max(1,table.indexof(values_inv, e["p_value"]))
		data.inv_stack_name = e[ id_prefix.."itemstack" ]
	end
	if(id_prefix == "p_" and (not(data.inv) or data.inv == 1)) then
		data.inv = 1
		-- not enough selected yet for saving
		save_button = ""
	end
	local block_selection = ""
	if(data and data.what_type
	  and (data.what_type == "block_inv"
	    or data.what_type == "put_into_block_inv"
	    or data.what_type == "take_from_block_inv")) then
		local inv_list_name = ""
		if(e) then
			-- not really relevant here but needed for getting the position
			e[ id_prefix.."value" ] = "node_is_like"
			inv_list_name = e[ id_prefix.."inv_list_name"]
		end
		-- positions of nodes in protected areas are allowed for inventory access
		local res = yl_speak_up.get_block_pos_info(pname, data, id_prefix, e, values_block, true)
		if(not(res.show_save_button)) then
			save_button = ""
		end
		-- which inventory lists are available?
		local tmp = yl_speak_up.get_node_inv_lists(res.block_pos, inv_list_name)
		block_selection = ""..
			"label[0.2,7.0;Position of the block:]"..
			"label[4.0,7.0;"..minetest.formspec_escape(res.block_pos_str).."]"..
			"label[0.2,7.5;Name of block:]"..
			"label[4.0,7.5;"..minetest.formspec_escape(res.node.name).."]"..
			"label[0.2,8.0;Orientation (param2):]"..
			"label[4.0,8.0;"..minetest.formspec_escape(res.node.param2).."]"..
			"label[0.2,8.5;Inventory list name:]"..
			"dropdown[4.0,8.2;3.8,0.6;inv_list_name;"..
				table.concat(tmp.inv_lists, ",")..";"..
				tostring(tmp.index)..";]"..
			"button_exit[0.2,9.0;4.0,0.7;select_block_pos;Set position of block]"..
			"tooltip[select_block_pos;Click on this button to select a block.\n"..
				"This menu will close and you will be asked to punch\n"..
				"the block at the position you want to check or change.\n"..
				"After punching it, you will be returned to this menu.]"
	end
	local intro = ""
	-- for preconditions: contain/does not contain item, is empty, ..
	if(id_prefix == "p_") then
		intro = "label[0.2,3.0;The following shall be true about the inventory:]"..
		        "dropdown[4.0,3.2;16.0,0.6;select_inv;"..
				table.concat(check_inv, ",")..";"..
				tostring(data.inv)..";]"
	-- for results/effects:
	elseif(data.what_type == "put_into_block_inv") then
		intro = "label[0.2,3.0;The NPC shall put the following item from his inventory "..
			"into the given block's inventory:]"
	elseif(data.what_type == "take_from_block_inv") then
		intro = "label[0.2,3.0;The NPC shall take the following item from the given block's "..
			"inventory and put it into his own inventory:]"
	end
	return formspec..
		intro..
		yl_speak_up.fs_your_inventory_select_item(pname, data)..
		block_selection..
		save_button
end
-- "an item the player offered to the NPC"
yl_speak_up.get_fs_edit_option_effect_deal_with_offered_item = function(
			pname, dialog, formspec, data, id_prefix, save_button, e)
	if(e) then
		data.select_deal_with_offered_item = table.indexof(
				yl_speak_up.dropdown_values_deal_with_offered_item,
				e[ "r_value" ])
	end
	if(not(data) or not(data.select_deal_with_offered_item)
	  or data.select_deal_with_offered_item < 2) then
		save_button = ""
		data.select_deal_with_offered_item = 1
	end
	return formspec..
		"label[0.2,3.3;The NPC shall:]"..
		"dropdown[4.0,3.0;15.0,0.7;select_deal_with_offered_item;"..
			table.concat(yl_speak_up.dropdown_list_deal_with_offered_item, ",")..";"..
			tostring(data.select_deal_with_offered_item)..";]"..
		save_button
end
-- "give item (created out of thin air) to player (requires npc_master priv)", -- 9
-- "take item from player and destroy it (requires npc_master priv)", -- 10
yl_speak_up.get_fs_edit_option_effect_give_item_or_take_item = function(
			pname, dialog, formspec, data, id_prefix, save_button, e)
	if(e) then
		data.inv_stack_name = e[ "r_value" ] or ""
	end
	local text = "The following item shall be created out of thin air and added to the "..
			"player's inventory:"
	local priv_name = "effect_give_item"
	if(data.what == 10) then
		text = "The following item shall be removed from the player's inventory and "..
			"be destroyed:"
		priv_name = "effect_take_item"
	end
	return formspec..
		"label[0.2,3.0;"..text.."]"..
		"label[0.2,3.5;Note: You can *save* this effect only if you have the "..
			"\"npc_master\" priv!]"..
		"label[0.2,8.0;"..
			"And in order to be able to execute it, this NPC\n"..
			"needs the \""..tostring(priv_name).."\" priv.\n\t"..
			"Type \"/npc_talk_privs grant "..tostring(yl_speak_up.speak_to[pname].n_id)..
			" "..tostring(priv_name).."\"\nin order to grant this.]"..
		yl_speak_up.fs_your_inventory_select_item(pname, data)..
		save_button
end
-- "move the player to a given position (requires npc_master priv)", -- 11
yl_speak_up.get_fs_edit_option_effect_move = function(
			pname, dialog, formspec, data, id_prefix, save_button, e)
	if(e) then
		if(e[ "r_value"] and type(e[ "r_value" ]) == "string") then
			local pos = minetest.string_to_pos(e[ "r_value" ])
			if(pos) then
				data.move_to_x = pos.x
				data.move_to_y = pos.y
				data.move_to_z = pos.z
			end
		end
	end
	return formspec..
		"label[0.2,3.0;Move the player to this position:]"..
		"label[0.2,3.5;Note: You can *save* this effect only if you have the "..
			"\"npc_master\" priv!\n"..
			"And in order to be able to execute it, this NPC needs the \""..
			"effect_move_player\" priv.\n\t"..
			"Type \"/npc_talk_privs grant "..tostring(yl_speak_up.speak_to[pname].n_id)..
			" effect_move_player\" in order to grant this.]"..
		"label[0.2,5.3;X:]"..
		"label[3.7,5.3;Y:]"..
		"label[7.2,5.3;Z:]"..
		"field[0.7,5.0;2.0,0.6;move_to_x;;"..(data.move_to_x or "").."]"..
		"field[4.2,5.0;2.0,0.6;move_to_y;;"..(data.move_to_y or "").."]"..
		"field[7.7,5.0;2.0,0.6;move_to_z;;"..(data.move_to_z or "").."]"..
		save_button
end
-- "execute Lua code (requires npc_master priv)", -- precondition: 8; effect: 12
yl_speak_up.get_fs_edit_option_p_and_e_function = function(
			pname, dialog, formspec, data, id_prefix, save_button, e)
	if(e) then
		if(e[ id_prefix.."value"] and e[ id_prefix.."value"] ~= "") then
			data.lua_code = e[ id_prefix.."value" ]
		end
	end
	local priv_name = "precon_exec_lua"
	if(id_prefix == "_r") then
		priv_name = "effect_exec_lua"
	end
	return formspec..
		"label[0.2,3.0;Execute the following Lua code (ought to return true or false):]"..
		"label[0.2,3.5;Note: You can *save* this effect only if you have the "..
			"\"npc_master\" priv!\n"..
			"And in order to be able to execute it, this NPC needs the \""..
			tostring(priv_name).."\" priv.\n\t"..
			"Type \"/npc_talk_privs grant "..tostring(yl_speak_up.speak_to[pname].n_id)..
			" "..tostring(priv_name).."\" in order to grant this.]"..
		"textarea[0.2,5.0;20,4.0;lua_code;;"..
			minetest.formspec_escape(tostring(data.lua_code)).."]"..
		save_button
end
-- "NPC crafts something", -- 6
-- (craft - only for effects - not for preconditions)
yl_speak_up.get_fs_edit_option_effect_craft = function(
			pname, dialog, formspec, data, id_prefix, save_button, e)
	if(e) then
		-- those items can at least be shown as background images
		data.craftresult = e[ "r_value" ]
		data.craft_grid = e[ "r_craft_grid"]
	end
	local bg_img = ""
	if(data and data.craftresult and data.craft_grid) then
		bg_img = "item_image[5.95,8.70;0.7,0.7;"..tostring(data.craftresult).."]"..
			"image[4.6,8.6;1,1;gui_furnace_arrow_bg.png^[transformR270]"
		for i, v in ipairs(data.craft_grid) do
			if(v and v ~= "") then
				bg_img = bg_img.."item_image["..
					tostring(1.15 + ((i-1)%3)*1.25)..","..
					tostring(8.15 + math.floor((i-1)/3)*0.65)..
					";0.7,0.7;"..tostring(v).."]"
			end
		end
	end
	return formspec..
		"label[8,2.6;Your invnetory:]"..
		"list[current_player;main;8,3;8,4;]"..
		"label[1,3.1;Your craft grid:]"..
		"list[current_player;craft;1,3.5;3,3;]"..
		"list[current_player;craftpreview;5.8,4.75;1,1;]"..
		"image[4.6,4.8;1,1;gui_furnace_arrow_bg.png^[transformR270]"..
		"label[1,8.0;Use your craft grid to show your NPC what to craft "..
			"and how. Click on \"Save\" to save. Currently stored:]"..
		bg_img..
		save_button
end
-- "go to other dialog if the *previous* effect failed", -- 5
-- (on_failure - only for effects - not for preconditions)
yl_speak_up.get_fs_edit_option_effect_on_failure = function(
			pname, dialog, formspec, data, id_prefix, save_button, e)
	if(e) then
		data.on_failure = e[ "r_value" ]
		data.alternate_text = e[ "alternate_text" ]
	end
	local dialog = yl_speak_up.speak_to[pname].dialog
	local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
	local nr = 1
	if(not(data) or not(data.on_failure)) then
		save_button = ""
	else
		nr = table.indexof(sorted_dialog_list, data.on_failure)
	end
	local on_failure_dialog = ""
	if(dialog and dialog.n_dialogs and dialog.n_dialogs[ data.on_failure ]) then
		on_failure_dialog =
			"label[0.2,5.5;This will switch to dialog \""..
				minetest.formspec_escape(tostring(data.on_failure)).."\""..
			yl_speak_up.show_colored_dialog_text(
				dialog,
				data,
				data.on_failure,
				"1.2,5.8;18.0,2.0;d_text",
				", but with the following *modified* text",
				":]",
				"button_edit_effect_on_failure_text_change")
	end
	return formspec..
		"label[0.2,3.3;If the *previous* effect failed,]"..
		"label[0.2,3.8;switch to the following dialog:]"..
		"dropdown[5.0,3.5;6.5,0.6;select_on_failure;"..
			table.concat(sorted_dialog_list, ",")..";"..
			tostring(nr)..";]"..
		on_failure_dialog..
		save_button
end
-- "send a chat message to all players" -- 8
yl_speak_up.get_fs_edit_option_effect_chat_all = function(
			pname, dialog, formspec, data, id_prefix, save_button, e)
	if(e) then
		data.chat_msg_text = e[ "r_value" ]
	end
	local default_text = "$NPC_NAME$ (owned by $OWNER_NAME$) announces: $PLAYER_NAME$ "..
		"- example; please enter the text -"
	return formspec..
		"label[0.2,3.3;Send the following chat message to *all* players:]"..
		"label[0.2,4.1;Message:]"..
		"field[2.0,3.8;16.0,0.6;chat_msg_text;;"..
			minetest.formspec_escape(
				data.chat_msg_text
				or default_text).."]"..
		"label[0.2,5.3;Note: Your chat message needs to contain the following placeholders,"..
			" which will be replaced automaticly like in dialog texts:"..
			"\n$NPC_NAME$, $PLAYER_NAME$ and $OWNER_NAME$.]"..
		save_button
end
-- "Normal trade - one item(stack) for another item(stack).", -- 3
yl_speak_up.get_fs_edit_option_action_trade = function(
			pname, dialog, formspec, data, id_prefix, save_button, e)
	if(e) then
		data.trade_id = e[ "a_value" ]
		-- use as background images
		if(dialog and dialog.trades and dialog.trades[ data.trade_id ]) then
			data.pay = dialog.trades[ data.trade_id ].pay[1]
			data.buy = dialog.trades[ data.trade_id ].buy[1]
		end
		local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
		data.action_failure_dialog = math.max(1,
				table.indexof(sorted_dialog_list, e[ "a_on_failure" ]))
	end
	local dialog = yl_speak_up.speak_to[pname].dialog
	local d_id = yl_speak_up.speak_to[pname].d_id
	local o_id = yl_speak_up.speak_to[pname].o_id
	if(not(data.trade_id)) then
		data.trade_id = tostring(d_id).." "..tostring(o_id)
	end
	-- show the player which trade is stored
	local bg_img = ""
	if(data and data.buy and data.pay) then
		bg_img = "item_image[1.15,4.35;0.7,0.7;"..tostring(data.pay).."]"..
			 "item_image[6.15,4.35;0.7,0.7;"..tostring(data.buy).."]"
	end
	yl_speak_up.speak_to[pname].trade_id = data.trade_id
	return formspec..
		"label[8,2.6;Your invnetory:]"..
		"list[current_player;main;8,3;8,4;]"..
		"label[0.2,3.1;Configure trade with "..minetest.formspec_escape(dialog.n_npc)..":]"..
		"label[0.5,3.8;The customer pays:]"..
		-- show the second slot of the setup inventory in the detached player's inv
		"list[detached:yl_speak_up_player_"..pname..";setup;2,4.2;1,1;]"..
		"image[3.5,4.2;1,1;gui_furnace_arrow_bg.png^[transformR270]"..
		"label[4.0,3.8;"..minetest.formspec_escape(dialog.n_npc or "?").." sells:]"..
		-- show the second slot of said inventory
		"list[detached:yl_speak_up_player_"..pname..";setup;5,4.2;1,1;1]"..
		bg_img..
		yl_speak_up.set_on_action_failure_dialog(pname, data,
			"The player shall trade at least once.")..
		save_button
end
-- "The NPC gives something to the player (i.e. a quest item).", -- 4
-- (only for actions)
yl_speak_up.get_fs_edit_option_action_npc_gives = function(
			pname, dialog, formspec, data, id_prefix, save_button, e)
	local bg_img = ""
	if(e) then
		data.item_quest_id = data.item_quest_id or e["item_quest_id"]
		data.item_desc = data.item_desc or e["item_desc"]
	end
	if(data and (data.item_node_name or data.item_string)) then
		bg_img = "item_image[1.15,3.65;0.7,0.7;"..
			 tostring(data.item_node_name or data.item_string).."]"
	end
	return formspec..
		"label[8,2.6;Your inventory:]"..
		"list[current_player;main;8,3;8,4;]"..
		"label[1,3.1;"..minetest.formspec_escape(dialog.n_npc or "?").." gives:]"..
		"list[detached:yl_speak_up_player_"..pname..";npc_gives;2,3.5;1,1;]"..
		"label[3.2,4.0;"..
				minetest.formspec_escape(
					data.item_node_name
					or "- no item set -").."]"..
		"label[0.2,5.6;Set a description to turn the item into a special\n"..
			"quest item. Set a special ID (short text) so that\n"..
			"the player cannot create a fake item. Click on \n"..
			"\"Save\" to apply the changes.\n"..
			"You can use placeholders like $PLAYER_NAME$ etc.]"..
		"label[0.2,8.3;Special ID to set:]"..
		"field[3.2,8.0;14.5,0.6;action_item_quest_id;;"..
				minetest.formspec_escape(
					data.item_quest_id
					or "- none set -").."]"..
		"tooltip[action_item_quest_id;"..
			"Set this to a text that helps *you* to remember what this\n"..
			"special quest item is for (i.e. \"quest_deliver_augusts_"..
			"letter\").\n"..
			"The ID will be extended with the ID of the NPC and the\n"..
			"name of the player who got this item from the NPC.]"..
		"label[0.2,9.0;Description to set:]"..
		"field[3.2,8.7;14.5,0.6;action_item_desc;;"..
				minetest.formspec_escape(
					data.item_desc
					or "- no item set -").."]"..
		"tooltip[action_item_desc;"..
			"Set this to a text that helps the *player* to remember what\n"..
			"this special quest item is for (i.e. \"Letter from August to\n"..
			"Frederike\" for a piece of paper).\n"..
			"This description is shown in the inventory on mouseover.]"..
		bg_img..
		yl_speak_up.set_on_action_failure_dialog(pname, data,
			"The player shall take this offered item.")..
		save_button
end
-- "The player is expected to give something to the NPC (i.e. a quest item).", -- 5
-- (only for actions)
-- "an item the player offered/gave to the NPC", (as precondition)
yl_speak_up.get_fs_edit_option_action_npc_wants_or_accepts = function(
			pname, dialog, formspec, data, id_prefix, save_button, e)
	local bg_img = ""
	local node_name = ""
	if(e) then
		data.item_quest_id = data.item_quest_id or e["item_quest_id"]
		data.item_desc = data.item_desc or e["item_desc"]
		data.item_group = data.item_group or e["item_group"]
		data.item_stack_size = data.item_stack_size or e["item_stack_size"]
		data.match_stack_size = data.match_stack_size or e["match_stack_size"]
	end
	if(data and (data.item_node_name or data.item_string)) then
		node_name = tostring(data.item_node_name or data.item_string)
		bg_img = "item_image[1.15,3.65;0.7,0.7;"..node_name.."]"
	end
	local info_text = ""
	if(id_prefix == "p_") then
		local group_list = {minetest.formspec_escape("- no, just this one item -")}
		-- get node name without amount
		local parts = node_name:split(" ")
		local nr = 1
		local count = 1
		local amount = tostring(1)
		-- prepare group_list
		if(data and parts and minetest.registered_items[ parts[1] ]) then
			for k,v in pairs(minetest.registered_items[ parts[1] ].groups) do
				table.insert(group_list, k)
				count = count + 1
				if(data.item_group and data.item_group == k) then
					nr = count
				end
			end
			amount = tostring(parts[2])
		end
		local size_list = {"any amount", "exactly "..amount,
			"less than "..amount, "more than "..amount, "another amount than "..amount}
		local match_size = 1
		for i, list_text in ipairs(size_list) do
			if(data.match_stack_size and data.match_stack_size == list_text:split(" ")[ 1 ]) then
				match_size = i
			end
		end
		if(data) then
			data.item_stack_size = amount
		end
		info_text =
			"label[1,2.6;The player offered:]"..
			"label[6.7,3.1;of:]"..
			"dropdown[2,2.8;4.5,0.6;select_match_stack_size;"..
				table.concat(size_list, ",")..";"..
				tostring(match_size or 1)..";]"..
			"label[1,4.8;...and also all other items of the group:]"..
			"dropdown[2,5.1;5.0,0.6;select_accept_group;"..
				table.concat(group_list, ",")..";"..tostring(nr)..";]"
	else
		info_text =
			"label[1,3.1;"..minetest.formspec_escape(dialog.n_npc or "?").." wants:]"..
			yl_speak_up.set_on_action_failure_dialog(pname, data,
						"The player shall give the NPC this item.")
	end
	return formspec..
		"label[8,2.6;Your inventory:]"..
		"list[current_player;main;8,3;8,4;]"..
		"list[detached:yl_speak_up_player_"..pname..";npc_wants;2,3.5;1,1;]"..
		"label[3.2,4.0;"..
			minetest.formspec_escape(
				node_name
				or "- no item set -").."]"..
		"label[0.2,6.1;If you want a special ID and description, create\n"..
			"those via the \"NPC gives something to the player\"\n"..
			"menu option first and insert that item here. Don't\n"..
			"use other placeholders than $PLAYER_NAME$ for this!]"..
		"label[0.2,8.3;Expected special ID:]"..
		"label[4.0,8.3;"..
			minetest.formspec_escape(
				data.item_quest_id
				or "- none set -").."]"..
		"label[0.2,9.0;Expected description:]"..
		"label[4.0,9.0;"..
			minetest.formspec_escape(
				 data.item_desc
				 or "- none set -").."]"..
		bg_img..
		info_text..
		save_button
end
-- "The player has to manually enter a password or passphrase or some other text.", -- 6
-- (only for actions)
yl_speak_up.get_fs_edit_option_action_text_input = function(
			pname, dialog, formspec, data, id_prefix, save_button, e)
	if(e) then
		data.quest_question = e[ "a_question" ]
		data.quest_answer = e[ "a_value" ]
		local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
		data.action_failure_dialog = math.max(1,
				table.indexof(sorted_dialog_list, e[ "a_on_failure" ]))
	end
	return formspec..
		"label[0.2,3.3;What to ask the player and which answer to expect:]"..
		"label[0.2,4.0;Question to show:]"..
		"field[4.0,3.8;10.0,0.6;quest_question;;"..
			minetest.formspec_escape(
				data.quest_question
				or "Your answer:").."]"..
		"label[0.2,5.0;Expected answer:]"..
		"field[4.0,4.8;10.0,0.6;quest_answer;;"..
			minetest.formspec_escape(
				data.quest_answer
				or "- Insert the correct answer here -").."]"..
		"tooltip[quest_question;"..
			"This is just a short text that will be shown to remind\n"..
			"the player what he is asked for. Most of the question\n"..
			"ought to be part of the normal dialog of the NPC.]"..
		"tooltip[quest_answer;"..
			"The correct answer will not be shown to the player.\n"..
			"What the player enters will be compared to this\n"..
			"correct value.]"..
		"tooltip[select_on_action_failure;"..
			"If the player gives the wrong answer, you can show him\n"..
			"a diffrent target dialog (i.e. with text \"No, that answer\n"..
			"was wrong, but please try again!\"). In such a case the\n"..
			"effects/results of the current dialog option are *not*\n"..
			"executed.]"..
		yl_speak_up.set_on_action_failure_dialog(pname, data,
			"The player shall enter the correct answer.")..
		save_button
end
-- "Call custom functions that are supposed to be overridden by the server.", -- 7
-- precondition: 9; action: 7; effect: 13
yl_speak_up.get_fs_edit_option_all_custom = function(
			pname, dialog, formspec, data, id_prefix, save_button, e)
	if(e) then
		data.custom_param = e[ id_prefix.."value" ]
		if(id_prefix == "a_") then
			local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
			data.action_failure_dialog = math.max(1,
				table.indexof(sorted_dialog_list, e[ "a_on_failure" ]))
		end
	end
	formspec = formspec..
		"label[0.2,3.3;Note: Calling a custom function will require direct support "..
			"from the server.]"..
		"label[0.2,4.0;Parameter for custom function:]"..
		"field[6.0,3.7;10.0,0.6;custom_param;;"..
			minetest.formspec_escape(
				data.custom_param
				or "- Insert a text that is passed on to your function here -").."]"..
		"tooltip[custom_param;"..
			"The custom parameter may help whoever implements the\n"..
			"custom function to more easily see what it belongs to.\n"..
			"Dialog and option ID are also passed as parameters.]"
	if(id_prefix == "a_") then
		formspec = formspec..
			"tooltip[select_on_action_failure;"..
				"If the player gives the wrong answer, you can show him\n"..
				"a diffrent target dialog (i.e. with text \"No, that answer\n"..
				"was wrong, but please try again!\"). In such a case the\n"..
				"effects/results of the current dialog option are *not*\n"..
				"executed.]"..
			yl_speak_up.set_on_action_failure_dialog(pname, data,
				"The player shall click on the right button.")
	else
		formspec = formspec..
			"label[0.3,5.0;Note: Your custom function has to return either true "..
				"or false.]"
	end
	return formspec..save_button
end
-- "The preconditions of another dialog option are fulfilled/not fulfilled.", -- 10
-- precondition: 10
yl_speak_up.get_fs_other_option_preconditions = function(
				pname, dialog, formspec, data, id_prefix, save_button, e)
	local dialog = yl_speak_up.speak_to[pname].dialog
	local d_id = yl_speak_up.speak_to[pname].d_id
	local o_id = yl_speak_up.speak_to[pname].o_id
	-- only o_id with a *lower* o_sort value are suitable (else evaluation would become
	-- difficult and loops might be created)
	local o_id_list = {}
	local options = dialog.n_dialogs[ d_id ].d_options
	if(options) then
		local this_option = options[ o_id ]
		if(not(this_option) or not(this_option.o_sort)) then
			this_option = {o_sort = 0}
		end
		for k, v in pairs(options) do
			if(k and v and v.o_sort and v.o_sort < this_option.o_sort) then
				table.insert(o_id_list, minetest.formspec_escape(k))
			end
		end
	end
	if(e) then
		data.other_o_id = e[ "p_value" ]
		data.fulfilled = e[ "p_fulfilled" ]
	end
	local nr = math.max(0, table.indexof(o_id_list, data.other_o_id))
	nr_fulfilled = 1
	if(data.fulfilled == "true") then
		nr_fulfilled = 2
	elseif(data.fulfilled == "false") then
		nr_fulfilled = 3
	end
	if(nr == 0 or nr_fulfilled == 1) then
		save_button = ""
	end
	return formspec..
		"label[0.2,3.3;Note: You can only select dialog options with a *lower* o_sort value "..
			"for this evaluation.]"..
		"label[0.2,4.0;The preconditions of dialog option:]"..
		"dropdown[6.0,3.7;3.0,0.6;select_other_o_id;-select-,"..
			table.concat(o_id_list, ",")..";"..
			tostring(nr + 1)..";]"..
		"label[9.2,4.0;..shall be:]"..
		"dropdown[11,3.7;2.0,0.6;select_fulfilled;-select-,true,false;"..
			tostring(nr_fulfilled).."]"..
		"tooltip[select_other_o_id;"..
			"Sometimes you may need the same preconditions for more than\n"..
			"one dialog option - or you may need one dialog option to be\n"..
			"available exactly when another one is *not* available.\n"..
			"This is what you can do here.]"..
		"tooltip[select_fulfilled;"..
			"If you select \"true\" here, then this precondition will be\n"..
			"fulfilled when all the preconditions of the dialog option you\n"..
			"selected here are true as well.\n"..
			"If you select \"false\", this precondition will only be\n"..
			"fulfilled if the other dialog option you selected here\n"..
			"is not true.]"..
		save_button
end
-- end of formspecs for types of preconditions, actions and effects
----------------------------------------------------------------------------
-- helper function
yl_speak_up.set_on_action_failure_dialog = function(pname, data, instruction)
	local dialog = yl_speak_up.speak_to[pname].dialog
	local nr = 1
	if(data and data.action_failure_dialog) then
		nr = data.action_failure_dialog + 1
	end
	local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
	local on_failure_dialog =
		"label[0.2,9.9;"..tostring(instruction).." If he doesn't, go to dialog:]"..
		"dropdown[11.0,9.6;2.7,0.6;select_on_action_failure;"..
			"- current one -,"..
			table.concat(sorted_dialog_list, ",")..";"..tostring(nr)..";]"
	if(nr and nr > 1) then
		return on_failure_dialog..
			yl_speak_up.show_colored_dialog_text(
				dialog,
				data,
				sorted_dialog_list[ nr - 1],
				"1.2,10.2;18.0,1.8;d_text",
				"label[13.8,9.9;and show the following *modified* text:]",
				"",
				"button_edit_action_on_failure_text_change")
	end
	return on_failure_dialog
end