yl_speak_up/fs_edit_general.lua
2021-07-15 04:23:06 +02:00

2203 lines
87 KiB
Lua

-- store which player is monitoring the NPC (for preconditions and
-- effects)
yl_speak_up.debug_mode_set_by_player = {}
-- for sending debug information about preconditions and effects to
-- the player who is monitoring the NPC
-- (sometimes it is not easy/obvious to see why something failed)
yl_speak_up.debug_msg = function(player, n_id, o_id, text)
local dname = yl_speak_up.debug_mode_set_by_player[ n_id ]
-- nobody cares
if(not(dname)) then
return
end
local pname = player:get_player_name()
local d_id = yl_speak_up.speak_to[pname].d_id
minetest.chat_send_player(dname, "[NPC "..tostring(n_id)..": "..
tostring(pname).."] <"..tostring(d_id).." "..tostring(o_id)..
"> "..tostring(text))
end
-- a chat command for entering and leaving debug mode; needs to be a chat command
-- because the player may have wandered off from his NPC and get too many messages
-- without a quick way to get rid of them otherwise
minetest.register_chatcommand( 'npc_talk_debug', {
description = "Sets you as debugger for the yl_speak_up-NPC with the ID <n_id>.\n"..
" <list> lists the NPC you are currently debugging.\n"..
" <off> turns debug mode off again.",
privs = {npc_talk_owner = true},
func = function(pname, param)
if(param and param == "off") then
local count = 0
for k, v in pairs(yl_speak_up.debug_mode_set_by_player) do
if(v and v == pname) then
yl_speak_up.debug_mode_set_by_player[ k ] = nil
count = count + 1
minetest.chat_send_player(pname, "You are no longer "..
"debugging the NPC with the ID "..tostring(k)..".")
end
end
minetest.chat_send_player(pname, "Removed you as debugger of "..
tostring(count).." NPCs.")
return
elseif(not(param) or param == "" or param == "list") then
local count = 0
local text = "You are currently debugging the NPCs with the following IDs:\n"
for k, v in pairs(yl_speak_up.debug_mode_set_by_player) do
if(v and v == pname) then
count = count + 1
text = text.." "..tostring(k)
end
end
if(count == 0) then
text = text.." - none -"
else
text = text.."\nTo turn debugging off, call this command with the "..
"parameter <off>."
end
minetest.chat_send_player(pname, text)
return
elseif(not(yl_speak_up.may_edit_npc(minetest.get_player_by_name(pname), param))) then
minetest.chat_send_player(pname, "You do not have the necessary privs to "..
"edit that NPC.")
return
else
yl_speak_up.debug_mode_set_by_player[ param ] = pname
minetest.chat_send_player(pname, "You are now receiving debug information "..
"for NPC "..tostring(param)..".\nTo turn that off, type "..
"\"/npc_talk_debug off\".")
end
end
});
-- evaluate those preconditions of type "function" (set by the staff)
-- and execute those effects/results of type "function" (set by the staff)
-- WARNING: This is extremly powerful!
-- The code is taken out of the function
-- calculate_displayable_options(..)
-- (written by AliasAlreadyTaken).
-- It requires the npc_master priv to add or edit this prereq.
-- The function is called by
-- * yl_speak_up.eval_precondition and
-- * yl_speak_up.eval_effect.
yl_speak_up.eval_and_execute_function = function(player, x_v, id_prefix)
local pname = player:get_player_name()
--minetest.chat_send_all("this is in a single prereq or effect: "..dump(x_v))
local x_id = x_v[ id_prefix.. "id" ]
if x_v[ id_prefix.."type" ] ~= "function" then
return true
end
local code = x_v[ id_prefix.."value" ]
if code:byte(1) == 27 then
local obj = yl_speak_up.speak_to[pname].obj
local n_id = yl_speak_up.speak_to[pname].n_id
local npc = yl_speak_up.get_number_from_id(n_id)
if obj:get_luaentity() and tonumber(npc) then
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not compile the content of "..x_id.." :"..dump(code) .. " because of illegal bytecode for player "..pname)
else
minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not compile the content of "..x_id.." :"..dump(code) .. " for player unknown because of illegal bytecode")
end
end
local param = "playername"
if( id_prefix == "r_") then
param = "player"
end
local f, msg = loadstring("return function("..param..") " .. code .. " end")
if not f then
local obj = yl_speak_up.speak_to[pname].obj
local n_id = yl_speak_up.speak_to[pname].n_id
local npc = yl_speak_up.get_number_from_id(n_id)
if obj:get_luaentity() and tonumber(npc) then
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not compile the content of "..x_id.." :"..dump(code) .. " for player "..pname)
else
minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not compile the content of "..x_id.." :"..dump(code) .. " for player unknown")
end
else
local func = f()
local ok, ret = pcall(func,pname)
if not ok then
local obj = yl_speak_up.speak_to[pname].obj
local n_id = yl_speak_up.speak_to[pname].n_id
local npc = yl_speak_up.get_number_from_id(n_id)
if obj:get_luaentity() and tonumber(npc) then
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not execute the content of "..x_id.." :"..dump(code) .. " for player "..pname)
else
minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not execute the content of "..x_id.." :"..dump(code) .. " for player unknown")
end
end
if type(ret) == "boolean" then
return ret
end
end
-- fallback
return false
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 ]
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(data.what == 2 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 "")
v[ id_prefix.."variable" ] = yl_speak_up.add_pname_to_var(data.variable_name, pname)
-- "a block somewhere", -- 3
elseif(data.what == 3 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(data.what == 4 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
-- (only for preconditions; not for effects)
elseif((data.what == 5 or data.what == 6) and id_prefix == "p_") then
v.p_value = values_inv[ data.inv ]
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[ "p_itemstack" ] = data.inv_stack_name
end
-- "give item (created out of thin air) to player (requires npc_master priv)", -- 7
-- "take item from player and destroy it (requires npc_master priv)", -- 8
elseif(data.what and id_prefix == "r_" and (data.what == 7 or data.what == 8)) 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)", -- 9
elseif(data.what and id_prefix == "r_" and data.what == 9) 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: 7; effect: 10
elseif((data.what and id_prefix == "p_" and data.what == 7)
or (data.what and id_prefix == "r_" and data.what == 10)) 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", -- 4
-- (only for effects; not for preconditions)
elseif(data.what and id_prefix == "r_" and data.what == 4) 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", -- 5
-- (only for effects; not for preconditions)
elseif(data.what and id_prefix == "r_" and data.what == 5) then
v[ "r_value" ] = data.on_failure
-- "send a chat message to all players", -- 6
-- (only for effects; not for preconditions)
elseif(data.what and id_prefix == "r_" and data.what == 6) 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(data.what and id_prefix == "a_" and data.what == 3) 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(data.what and id_prefix == "a_" and (data.what == 4 or data.what == 5)) then
local trade_inv_list = "npc_gives"
if(data.what == 5) 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
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 give to 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?
if(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[ "a_value" ] = stack:get_name().." "..stack:get_count()
-- for displaying as a background image
data.item_string = v[ "a_value" ]
if(data.what == 5) 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(data.what == 4) then
meta:set_string("description", data.item_desc)
end
v[ "a_item_desc" ] = data.item_desc
end
if(data.what == 5) 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(data.what == 4) 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[ "a_item_quest_id" ] = data.item_quest_id
end
if( v["a_item_quest_id"] and not(v[ "a_item_desc"]) and data.what == 4) 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
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(data.what and id_prefix == "a_" and data.what == 6) 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 ]
-- "Call custom functions that are supposed to be overridden by the server.", --
-- precondition: 8; action: 7; effect: 11
elseif(data.what
and ((id_prefix == "a_" and data.what == 7)
or (id_prefix == "p_" and data.what == 8)
or (id_prefix == "r_" and data.what == 11))) 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.", -- 9
-- precondition: 9
elseif(data.what and id_prefix == "p_" and data.what == 9) 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
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
-- 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 ]
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 data.what >= 5 and data.what <= 6)
-- "give item (created out of thin air) to player (requires npc_master priv)", -- 7
-- "take item from player and destroy it (requires npc_master priv)", -- 8
or (id_prefix == "r_" and data.what >= 7 and data.what <= 8))) 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
-- comparison value for a variable (same for both preconditions and effects)
elseif(fields.var_cmp_value
and data and data.what and data.what == 2 and id_prefix ~= "a_") 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 data.what == 6 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 data.what == 7)
or (id_prefix == "p_" and data.what == 8)
or (id_prefix == "r_" and data.what == 11))) 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 data.what == 4 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 data.what == 4 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 data.what == 6 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 data.what == 6 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)", -- 9
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
-- 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 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
-- 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
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.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)) 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.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))
})
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 = dialog.n_dialogs[d_id].d_options[o_id][ element_list_name ][ x_id ]
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.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))
})
return
end
-- allow to delete unused variables
elseif(fields.delete_unused_variable) then
-- try to delete the variable
local res = yl_speak_up.del_quest_variable(pname, data.variable_name)
local text = "Deleted successfully."
if(not(res)) then
text = "Error: Variable could not be deleted."
end
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:"..formspec_input_to,
formspec = "size[8,2]"..
"label[0.2,0.5;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.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 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
-- helper function for yl_speak_up.input_fs_edit_option_related
-- (handle editing of alternate texts that are shown instead of the normal dialog)
yl_speak_up.handle_edit_actions_alternate_text = function(
player, pname, n_id, d_id, o_id, x_id, id_prefix,
formspec_input_to, data, fields, tmp_data_cache)
-- edit alternate text for an action
if(fields.button_edit_action_on_failure_text_change) then
local dialog = yl_speak_up.speak_to[pname].dialog
local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
local failure_id = ""
if(data and data.action_failure_dialog) then
failure_id = sorted_dialog_list[ data.action_failure_dialog ]
end
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:"..formspec_input_to,
formspec = yl_speak_up.get_fs_edit_dialog_modification(
dialog, failure_id, data.alternate_text,
"if the action \""..tostring(x_id)..
"\" of option \""..tostring(o_id)..
"\" of dialog \""..tostring(d_id)..
"\" failed because the player did something wrong")
})
return
-- edit alternate text for an on_failure effect
elseif(fields.button_edit_effect_on_failure_text_change) then
local dialog = yl_speak_up.speak_to[pname].dialog
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:"..formspec_input_to,
formspec = yl_speak_up.get_fs_edit_dialog_modification(
dialog, data.on_failure, data.alternate_text,
"if the effect \""..tostring(x_id)..
"\" of option \""..tostring(o_id)..
"\" of dialog \""..tostring(d_id)..
"\" failed to execute correctly")
})
return
-- save the changes
elseif(fields.save_dialog_modification) then
local dialog = yl_speak_up.speak_to[pname].dialog
local old_text = data.alternate_text
if(data and fields.d_text_new and fields.d_text_new ~= "$TEXT$"
and fields.d_text_new ~= data.alternate_text) then
-- store modification
data.alternate_text = fields.d_text_new
yl_speak_up.speak_to[pname][ tmp_data_cache ] = data
if(id_prefix == "r_") then
-- record the change
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": The text displayed for dialog "..
tostring(data.on_failure).." when selecting option "..
tostring(o_id).." in dialog "..tostring( d_id )..
" and effect "..tostring(x_id).." failed "..
" was changed from "..
"["..tostring(old_text).."] to ["..tostring(fields.d_text_new).."].")
elseif(id_prefix == "a_") then
local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
local failure_id = ""
if(data and data.action_failure_dialog) then
failure_id = sorted_dialog_list[ data.action_failure_dialog ]
end
-- record the change
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": The text displayed for dialog "..
tostring(failure_id).." when the action "..
tostring(x_id).." of option "..
tostring( o_id ).." in dialog "..tostring( d_id )..
" failed, was changed from "..
"["..tostring(old_text).."] to ["..tostring(fields.d_text_new).."].")
end
end
end
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 "formspec_version[3]"..
"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" ]
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
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 =
"formspec_version[3]"..
"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
-- "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 data.what == 2 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 )
-- "a block somewhere", -- 3
-- (block is the third offered option in both preconditions and effects list)
elseif(data.what and data.what == 3 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 data.what == 4) 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
-- (inventory - only for preconditions; effects have something else here)
elseif(data.what and id_prefix == "p_" and data.what >= 5 and data.what <= 6) then
return yl_speak_up.get_fs_edit_option_precondition_inv(
pname, dialog, formspec, data, id_prefix, save_button, e,
values_inv, check_inv)
-- "give item (created out of thin air) to player (requires npc_master priv)", -- 7
-- "take item from player and destroy it (requires npc_master priv)", -- 8
elseif(data.what and id_prefix == "r_" and (data.what == 7 or data.what == 8)) 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)", -- 9
elseif(data.what and id_prefix == "r_" and data.what == 9) 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: 7; effect: 10
elseif((data.what and id_prefix == "p_" and data.what == 7)
or (data.what and id_prefix == "r_" and data.what == 10)) 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", -- 4
-- (craft - only for effects - not for preconditions)
elseif(data.what and id_prefix == "r_" and data.what == 4) 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 data.what == 5) 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" -- 6
elseif(data.what and id_prefix == "r_" and data.what == 6) 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 data.what == 3) 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 data.what == 4) 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)
elseif(data.what and id_prefix == "a_" and data.what == 5) then
return yl_speak_up.get_fs_edit_option_action_npc_wants(
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 data.what == 6) 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: 8; action: 7; effect: 11
elseif(data.what
and ((id_prefix == "a_" and data.what == 7)
or (id_prefix == "p_" and data.what == 8)
or (id_prefix == "r_" and data.what == 11))) 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.", -- 9
-- precondition: 9
elseif(data.what and id_prefix == "p_" and data.what == 9) 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
-- "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 )
local var_list = get_sorted_player_var_list_function(pname)
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" ]
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
local var_list_stripped = yl_speak_up.strip_pname_from_varlist(var_list, pname)
if(not(data.variable) or data.variable < 1) then
data.variable = 0
-- 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 formspec..
"label[0.2,3.3;"..text_variable.."]"..
"label[0.2,4.3;Name of variable:]"..
"dropdown[0.2,4.8;6.5,0.6;select_variable;"..
"- please select -"..var_list_stripped..";"..
tostring(data.variable + 1)..";]"..
"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..
"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;<normal>"..
"<b>Note:</b> Each variable is player-specific and will be set and "..
"checked for the player that currently talks to your NPC.\n"..
"<b>Note:</b> 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</normal>]"..
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)
-- 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) then
data.block = math.max(1,table.indexof(values_block, e[ id_prefix.."value" ]))
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(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
if(node.name == "- unknown -") then
save_button = ""
end
if(not(data.block) or data.block == 1) then
data.block = 1
-- not enough selected yet for saving
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(block_pos_str).."]"..
"label[0.2,5.8;Name of block:]"..
"label[4.0,5.8;"..minetest.formspec_escape(node.name).."]"..
"label[0.2,6.8;Orientation (param2):]"..
"label[4.0,6.8;"..minetest.formspec_escape(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.]"..
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
-- (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)
if(e) then
data.inv = math.max(1,table.indexof(values_inv, e["p_value"]))
data.inv_stack_name = e[ "p_itemstack" ]
end
if(not(data.inv) or data.inv == 1) then
data.inv = 1
-- not enough selected yet for saving
save_button = ""
end
return formspec..
"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)..";]"..
"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 \"Store\":]"..
"button[5.5,5.5;1.5,0.9;store_item_name;Store]"..
"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;]"..
save_button
end
-- "give item (created out of thin air) to player (requires npc_master priv)", -- 7
-- "take item from player and destroy it (requires npc_master priv)", -- 8
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:"
if(data.what == 8) then
text = "The following item shall be removed from the player's inventory and "..
"be destroyed:"
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,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 \"Store\":]"..
"button[5.5,5.5;1.5,0.9;store_item_name;Store]"..
"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;]"..
save_button
end
-- "move the player to a given position (requires npc_master priv)", -- 9
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!]"..
"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: 7; effect: 10
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
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!]"..
"textarea[0.2,4.5;20,4.0;lua_code;;"..
minetest.formspec_escape(tostring(data.lua_code)).."]"..
save_button
end
-- "NPC crafts something", -- 4
-- (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,4.90;0.7,0.7;"..tostring(data.craftresult).."]"
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(3.65 + math.floor((i-1)/3)*1.30)..
";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.]"..
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" ]
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" -- 6
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[2.15,4.35;0.7,0.7;"..tostring(data.buy).."]"..
"item_image[5.15,4.35;0.7,0.7;"..tostring(data.pay).."]"
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(data and data.item_string) then
bg_img = "item_image[2.15,3.65;0.7,0.7;"..tostring(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)
yl_speak_up.get_fs_edit_option_action_npc_wants = function(
pname, dialog, formspec, data, id_prefix, save_button, e)
local bg_img = ""
if(data and data.item_string) then
bg_img = "item_image[2.15,3.65;0.7,0.7;"..tostring(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 "?").." wants:]"..
"list[detached:yl_speak_up_player_"..pname..";npc_wants;2,3.5;1,1;]"..
"label[3.2,4.0;"..
minetest.formspec_escape(
data.item_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..
yl_speak_up.set_on_action_failure_dialog(pname, data,
"The player shall give the NPC this item.")..
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: 8; action: 7; effect: 11
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.", -- 9
-- precondition: 9
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
yl_speak_up.show_colored_dialog_text = function(dialog, data, d_id, hypertext_pos,
alternate_label_text, postfix, button_name)
if(not(data)) then
return ""
end
-- if(math.random(1,2)==1) then data.alternate_text = "This is an alternate text.\n$TEXT$" end
-- slightly red in order to indicate that this is an on_failure dialog
local color = "776666"
-- ..except for normal redirecting to the next dialog with the dialog effect
-- (slightly yellow there)
if(data.r_id and data.r_type and data.r_type == "dialog") then
color = "777766"
end
local add_info_alternate_text = ""
local text = ""
if(dialog and dialog.n_dialogs and dialog.n_dialogs[ d_id ]) then
text = dialog.n_dialogs[ d_id ].d_text
end
if(data and data.alternate_text and data.alternate_text ~= "") then
add_info_alternate_text = alternate_label_text
-- replace $TEXT$ with the normal dialog text and make the new text yellow
text = "<style color=#FFFF00>"..
string.gsub(
data.alternate_text,
"%$TEXT%$",
"<style color=#FFFFFF>"..text.."</style>")..
"</style>"
-- slightly blue in order to indicate that this is a modified text
color = "333366"
end
-- display the variables in orange
text = yl_speak_up.replace_vars_in_text(text,
-- fake dialog; just adds the colors
-- also MY_NAME..but we can easily replace just one
{ n_npc = "<style color=#FF8800>$NPC_NAME$</style>",
npc_owner = "<style color=#FF8800>$OWNER_NAME$</style>"},
-- pname
"<style color=#FF8800>$PLAYER_NAME$</style>")
local edit_button = ""
-- if there is the possibility that an alternate text may be displayed: allow to edit it
-- and calculate the position of the button from the hypertext_pos position and size
if(button_name and button_name ~= "") then
local parts = string.split(hypertext_pos, ";")
local start = string.split(parts[1], ",")
local size = string.split(parts[2], ",")
edit_button = "button_exit["..
tostring(tonumber(start[1]) + tonumber(size[1]) - 3.5)..","..
tostring(tonumber(start[2]) + tonumber(size[2]) - 0.9)..";"..
"3.0,0.7;"..button_name..";Edit this text]"
end
return add_info_alternate_text..
postfix..
"hypertext["..hypertext_pos..";<global background=#"..color.."><normal>"..
minetest.formspec_escape(text or "?")..
"\n</normal>]"..
-- display the edit button *inside*/on top of the hypertext field
edit_button
end
-- this allows to edit modifications of a dialog that are applied when a given option
-- is choosen - i.e. when the NPC wants to answer some questions - but those answers
-- do not warrant their own dialog
yl_speak_up.get_fs_edit_dialog_modification = function(dialog, d_id, alternate_dialog_text, explanation)
return "formspec_version[3]"..
"size[20,13.5]"..
"label[6.0,0.5;Edit alternate text]"..
"label[0.2,1.0;The alternate text which you can edit here will be shown instead of "..
"the normal text of the dialog \""..tostring(d_id).."\" - but *only*\n"..
tostring(explanation or "- missing explanation -")..".]"..
"label[0.2,2.3;This is the normal text of dialog \""..
minetest.formspec_escape(tostring(d_id)).."\", shown for reference:]"..
yl_speak_up.show_colored_dialog_text(
dialog,
{r_id = "", r_type = "dialog"},
d_id,
"1.2,2.6;18.0,4.0;d_text_orig",
"", -- no modifications possible at this step
"",
"").. -- no edit button here as this text cannot be changed here
"label[0.2,7.3;Enter the alternate text here. $TEXT$ will be replaced with the normal "..
"dialog text above:]"..
"textarea[1.2,7.6;18.0,4.5;d_text_new;;"..
minetest.formspec_escape(alternate_dialog_text or "$TEXT$").."]"..
"button[3.0,12.3;1,0.7;back_from_edit_dialog_modification;Abort]"..
"button[6.0,12.3;1,0.7;save_dialog_modification;Save]"
end