yl_speak_up/fs_edit_general.lua

585 lines
22 KiB
Lua

-- 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
-- delete precondition
if(fields.delete_element) then
local dialog = yl_speak_up.speak_to[pname].dialog
if(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
-- 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
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
-- 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 ~= ""
and id_prefix == "p_"
and data and data.what and data.what >= 5 and data.what <= 6) 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
-- does the item exist?
if(minetest.registered_items[ parts[1] ]) then
data.inv_stack_name = parts[1].." "..tostring(size)
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(fields.inv_stack_name)..
"\" 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) then
data.var_cmp_value = fields.var_cmp_value
end
-- the save button was pressed
if(fields.save_element and data and data.what and values_what[ data.what ]) then
-- 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
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) 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" ] = data.variable_name
-- "a block somewhere", -- 3
elseif(data.what == 3) 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")) then
v[ id_prefix.."node" ] = data.node_data.name
v[ id_prefix.."param2" ] = data.node_data.param2
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
-- we have checked this value earlier on
v[ "p_itemstack" ] = data.inv_stack_name
end
-- "NPC crafts soemthing", -- 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 action (i.e. trade) 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
end
-- 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)..".")
-- 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
end
end
-- selections in a dropdown menu (they trigger sending the formspec)
-- select a general direction/type first
if(fields.select_what) then
local nr = table.indexof(check_what, fields.select_what)
yl_speak_up.speak_to[pname][ tmp_data_cache ] = { what = nr }
-- select a subtype for the "a trade" selection
elseif(fields.select_trade) then
local nr = table.indexof(check_trade, fields.select_trade)
yl_speak_up.speak_to[pname][ tmp_data_cache ].trade = nr
-- select a subtype for the inventory selection (player or NPC)
elseif(fields.select_inv) then
local nr = table.indexof(check_inv, fields.select_inv)
yl_speak_up.speak_to[pname][ tmp_data_cache ].inv = nr
-- select data regarding a block
elseif(fields.select_block) then
local nr = table.indexof(check_block, fields.select_block)
yl_speak_up.speak_to[pname][ tmp_data_cache ].block = nr
-- select data regarding a variable
elseif(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)
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
-- select data regarding an operator
elseif(fields.select_operator) then
local nr = table.indexof(check_operator, fields.select_operator)
yl_speak_up.speak_to[pname][ tmp_data_cache ].operator = nr
elseif(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
-- the player wants to change/edit a precondition
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.back_from_error_msg)) 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,
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
-- store which element we are talking about
yl_speak_up.speak_to[pname][ id_prefix.."id" ] = x_id
-- 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 ]))..";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;1,0.9;back;Back]"
end
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
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 save_button = "button[5.0,9.0;1,0.7;save_element;Save]"
local formspec =
"formspec_version[3]"..
"size[20,10]"..
"label[5,0.5;Edit "..element_desc.." \""..minetest.formspec_escape(x_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,9.0;1,0.7;back;Abort]"
-- "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) then
if(not(data.variable) or data.variable == 1) then
data.variable = 1
-- 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.2,4.8;7.0,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))
or (id_prefix == "r_" and (data.operator == 3))) then
field_for_value = "label[11.2,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)
local var_list = get_sorted_player_var_list_function(pname)
formspec = 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;"..
table.concat(var_list, ",")..";"..
tostring(data.variable)..";]"..
"label[7.0,4.3;"..text_select_operator.."]"..
"dropdown[7.0,4.8;4.0,0.6;select_operator;"..
table.concat(check_operator, ",")..";"..
tostring(data.operator)..";]"..
"label[11.2,4.3;"..text_select_value.."]"..
field_for_value
-- "a block somewhere", -- 3
-- (block is the third offered option in both preconditions and effects list)
elseif(data.what and data.what == 3) then
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
formspec = 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
-- "a trade", -- 4
-- (trade - only for preconditions; effects have something else here)
elseif(data.what and id_prefix == "p_" and data.what == 4) then
if(not(data.trade) or data.trade == 1) then
data.trade = 1
-- not enough selected yet for saving
save_button = ""
end
formspec = 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)..";]"
-- "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
if(not(data.inv) or data.inv == 1) then
data.inv = 1
-- not enough selected yet for saving
save_button = ""
end
formspec = formspec..
"label[0.2,3.3;The following shall be true about the inventory:]"..
"dropdown[4.0,3.5;16.0,0.6;select_inv;"..
table.concat(check_inv, ",")..";"..
tostring(data.inv)..";]"..
"label[0.2,4.5;Name of the item(stack):]"..
"field[4.0,4.3;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.]"
-- "NPC crafts soemthing", -- 4
-- (craft - only for effects - not for preconditions)
elseif(data.what and id_prefix == "r_" and data.what == 4) then
formspec = 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.]"
-- "go to other dialog if the action (i.e. trade) failed", -- 5
-- (on_failure - only for effects - not for preconditions)
elseif(data.what and id_prefix == "r_" and data.what == 5) then
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
formspec = formspec..
"label[0.2,3.3;If the action *failed* (i.e. the player didn't trade),]"..
"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)..";]"
end
return formspec..save_button
end