yl_speak_up/fs_edit_preconditions.lua
2021-06-07 21:34:36 +02:00

754 lines
28 KiB
Lua

-- TODO: check inscription of a sign?
-- TODO: invlist as dropdown of inventory lists at detected position
-- TODO: variable as dropdown of allowed variables
-- Which diffrent types of preconditions are available?
-- -> The following fields are part of a precondition:
-- p_id the ID/key of the precondition/prerequirement
-- p_type selected from values_what; the staffs allow to use other
-- types like "function" or "has_item" - but that is not
-- supported here (cannot be edited or created; only be shown)
-- p_value used to store the subtype of p_type
--
-- a state/variable:
-- p_variable TODO
-- p_operator selected from values_operator
-- p_var_cmp_value can be set freely by the player
--
-- a block in the world:
-- p_pos a position in the world; determined by asking the player
-- to punch the block
-- p_node (follows from p_pos)
-- p_param2 (follows from p_pos)
--
-- a trade defined as an action: no variables needed (buy and pay stack follow
-- from the trade set as action)
--
-- an inventory:
-- p_itemstack an itemstack; needs to be a minetest.registered_item[..];
-- size/count is also checked
-- some helper lists for creating the formspecs and evaulating
-- the player's answers:
-- general direction of what a prerequirement may be about
local check_what = {
"- please select -",
"an internal state (i.e. of a quest)", -- 2
"a block somewhere", -- 3
"a trade", -- 4
"the inventory of the player", -- 5
"the inventory of the NPC", -- 6
}
-- how to store these as p_type in the precondition:
local values_what = {"", "state", "block", "trade", "player_inv", "npc_inv"}
-- options for "a trade"
local check_trade = {
"- please select -",
"The NPC has the item(s) he wants to sell in his inventory.", -- 2
"The player has the item(s) needed to pay the price.", -- 3
"The NPC ran out of stock.", -- 4
"The player cannot afford the price.", -- 5
}
-- how to store these as p_value:
local values_trade = {"", "npc_can_sell", "player_can_buy", "npc_is_out_of_stock", "player_has_not_enough"}
-- options for "the inventory of " (either player or NPC; perhaps blocks later on)
local check_inv = {
"- please select -",
"The inventory contains the following item:",
"The inventory *does not* contain the following item:",
"There is room for the following item in the inventory:",
"The inventory is empty.",
}
-- how to store these as p_value (the actual itemstack gets stored as p_itemstack):
local values_inv = {"", "inv_contains", "inv_does_not_contain", "has_room_for", "inv_is_empty"}
local check_block = {
"- please select -",
"The block is as it is now.",
"There shall be air instead of this block.",
"The block is diffrent from how it is now.",
"I can't punch it. The block is as the block *above* the one I punched.",
}
-- how to store these as p_value (the actual node data gets stored as p_node, p_param2 and p_pos):
-- Note: "node_is_like" occours twice because it is used to cover blocks that
-- cannot be punched as well as normal blocks.
local values_block = {"", "node_is_like", "node_is_air", "node_is_diffrent_from", "node_is_like"}
-- comparison operators for variables
local check_operator = {
"- please select -", -- 1
"== (is equal)", -- 2
"~= (is not equal)", -- 3
">= (is greater or equal)", -- 4
"> (is greater)", -- 5
"<= (is smaller or equal)", -- 6
"< (is smaller)", -- 7
"not (logically invert)", -- 8
"is_set (has a value)", -- 9
"is_unset (has no value)" -- 10
}
-- how to store these as p_value (the actual variable is stored in p_variable, and the value in p_cmp_value):
local values_operator = {"", "==", "~=", ">=", ">", "<=", "<", "not", "is_set", "is_unset"}
-- some internal ones...
local check_variable = {
"- please select -",
"(internal) hour of ingame day",
"(internal) player's health points",
}
-- returns a human-readable text as description of the precondition
-- (as shown in the edit options dialog and in the edit precondition formspec)
yl_speak_up.show_precondition = function(p)
if(not(p.p_type) or p.p_type == "") then
return "(nothing): Always true."
elseif(p.p_type == "item") then
return "item: The player has "..tostring(p.p_value).." in his inventory."
elseif(p.p_type == "quest") then
return "quest: Always false."
elseif(p.p_type == "auto") then
return "auto: Always true."
elseif(p.p_type == "function") then
return "function: evaluate "..tostring(p_value)
elseif(p.p_type == "state") then
if(p.p_operator == "not") then
return "not( "..tostring(p.p_variable).." )"
elseif(p.p_operator == "is_set") then
return tostring(p.p_variable).." ~= nil (is_set)"
elseif(p.p_operator == "is_unset") then
return tostring(p.p_variable).." == nil (is_unset)"
end
return tostring(p.p_variable).." "..tostring(p.p_operator).." "..
tostring(p.p_var_cmp_value)
elseif(p.p_type == "block") then
if(not(p.p_pos) or type(p.p_pos) ~= "table"
or not(p.p_pos.x) or not(p.p_pos.y) or not(p.p_pos.z)) then
return "ERROR: p.p_pos is "..minetest.serialize(p.p_pos)
elseif(p.p_value == "node_is_like") then
return "The block at "..minetest.pos_to_string(p.p_pos).." is \""..
tostring(p.p_node).."\" with param2: "..tostring(p.p_param2).."."
elseif(p.p_value == "node_is_air") then
return "There is no block at "..minetest.pos_to_string(p.p_pos).."."
elseif(p.p_value == "node_is_diffrent_from") then
return "There is another block than \""..tostring(p.p_node).."\" at "..
minetest.pos_to_string(p.p_pos)..", or it is at least "..
"rotated diffrently (param2 is not "..tostring(p.p_param2)..")."
end
elseif(p.p_type == "trade") then
local nr = table.indexof(values_trade, p.p_value)
if(nr and check_trade[ nr ]) then
return check_trade[ nr ]
end
elseif(p.p_type == "player_inv" or p.p_type == "npc_inv") then
local who = "The player"
if(p.p_type == "npc_inv") then
who = "The NPC"
end
if(p.p_value == "inv_contains") then
return who.." has \""..tostring(p.p_itemstack).."\" in his inventory."
elseif(p.p_value == "inv_does_not_contain") then
return who.." does not have \""..tostring(p.p_itemstack).."\" in his inventory."
elseif(p.p_value == "has_room_for") then
return who.." has room for \""..tostring(p.p_itemstack).."\" in his inventory."
elseif(p.p_value == "inv_is_empty") then
return who.." has an empty inventory."
end
end
-- fallback
return tostring(p.p_value)
end
-- called by calculate_displayable_options(..);
-- returns false if a single precondition is false
-- Important: If something cannot be determined (i.e. the node is nil),
-- *both* the condition and its inverse condition may be
-- true (or false).
yl_speak_up.eval_all_preconditions = function(player, prereq, o_id)
if(not(prereq)) then
minetest.chat_send_player(player:get_player_name(), "No preconditions given for "..tostring(o_id)..".")
-- no prerequirements? then they are automaticly fulfilled
return true
end
local pname = player:get_player_name()
minetest.chat_send_player(pname, "Checking preconditions for "..tostring(o_id).."..") -- TODO
local n_id = yl_speak_up.speak_to[pname].n_id
for k, p in pairs(prereq) do
minetest.chat_send_player(pname, "..checking "..tostring(p.p_id)..": "..yl_speak_up.show_precondition(p)) -- TODO
if(not(yl_speak_up.eval_precondition(player, n_id, p))) then
-- no need to look any further - once we hit a false, it'll stay false
minetest.chat_send_player(pname, " -> is false! Aborting.") -- TODO
return false
end
end
-- all preconditions are true
minetest.chat_send_player(pname, "All preconditions true for "..tostring(o_id)..".") -- TODO
return true
end
-- checks if precondition p is true for the player and npc n_id
yl_speak_up.eval_precondition = function(player, n_id, p)
if(not(p.p_type) or p.p_type == "") then
-- empty prerequirement: automaticly true (fallback)
return true
elseif(p.p_type == "item") then
-- a precondition set by using the staff;
-- aequivalent to p.p_type == "player_inv" and p.p_itemstack == "inv_contains"
return player:get_inventory():contains_item("main", p.p_value)
elseif(p.p_type == "quest") then
-- a precondition set by using the staff; intended as future quest interface?
return false
elseif(p.p_type == "auto") then
-- a precondition set by using the staff; kept for compatibility
return true
elseif(p.p_type == "function") then
-- a precondition set by using the staff;
-- extremly powerful (executes any lua code)
return yl_speak_up.eval_precondition_function(player, p)
elseif(p.p_type == "state") then
-- TODO: implement
--[[
if(p.p_operator == "not") then
return "not( "..tostring(p.p_variable).." )"
elseif(p.p_operator == "is_set") then
return tostring(p.p_variable).." ~= nil (is_set)"
elseif(p.p_operator == "is_unset") then
return tostring(p.p_variable).." == nil (is_unset)"
end
return tostring(p.p_variable).." "..tostring(p.p_operator).." "..
tostring(p.p_var_cmp_value)
--]]
elseif(p.p_type == "block") then
if(not(p.p_pos) or type(p.p_pos) ~= "table"
or not(p.p_pos.x) or not(p.p_pos.y) or not(p.p_pos.z)) then
return false
elseif(p.p_value == "node_is_like") then
local node = minetest.get_node_or_nil(p.p_pos)
return (node and node.name and node.name == p.p_node and node.param2 == p.p_param2)
elseif(p.p_value == "node_is_air") then
local node = minetest.get_node_or_nil(p.p_pos)
return (node and node.name and node.name == "air")
elseif(p.p_value == "node_is_diffrent_from") then
local node = minetest.get_node_or_nil(p.p_pos)
return (node and node.name and (node.name ~= p.p_node or node.param2 ~= p.p_param2))
end
-- fallback - unsupported option
return false
elseif(p.p_type == "trade") then
local pname = player:get_player_name()
local dialog = yl_speak_up.speak_to[pname].dialog
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
-- if there is no trade, then this condition is true
if(not(dialog) or not(dialog.trades) or not(d_id) or not(o_id)) then
return true
end
local trade = dialog.trades[ tostring(d_id).." "..tostring(o_id) ]
-- something is wrong with the trade
if(not(trade)
or not(trade.pay) or not(trade.pay[1]) or not(trade.buy) or not(trade.buy[1])) then
return false
end
if( p.p_value == "npc_can_sell") then
local npc_inv = minetest.get_inventory({type="detached",
name="yl_speak_up_npc_"..tostring(n_id)})
return npc_inv:contains_item("npc_main", trade.buy[1])
elseif(p.p_value == "npc_is_out_of_stock") then
local npc_inv = minetest.get_inventory({type="detached",
name="yl_speak_up_npc_"..tostring(n_id)})
return not(npc_inv:contains_item("npc_main", trade.buy[1]))
elseif(p.p_value == "player_can_buy") then
local player_inv = player:get_inventory()
return player_inv:contains_item("main", trade.pay[1])
elseif(p.p_value == "player_has_not_enough") then
local player_inv = player:get_inventory()
return not(player_inv:contains_item("main", trade.pay[1]))
end
return false
elseif(p.p_type == "player_inv" or p.p_type == "npc_inv") then
local inv = nil
local inv_name = "main"
-- determine the right inventory
if(p.p_type == "player_inv") then
inv = player:get_inventory()
else
inv = minetest.get_inventory({type="detached",
name="yl_speak_up_npc_"..tostring(n_id)})
inv_name = "npc_main"
end
if( p.p_itemstack and p.p_value == "inv_contains") then
return inv:contains_item(inv_name, p.p_itemstack)
elseif(p.p_itemstack and p.p_value == "inv_does_not_contain") then
return not(inv:contains_item(inv_name, p.p_itemstack))
elseif(p.p_itemstack and p.p_value == "has_room_for") then
return inv:room_for_item(inv_name, p.p_itemstack)
elseif(p.p_value == "inv_is_empty") then
return inv:is_empty(inv_name)
end
return false
end
-- fallback - unknown type
return false
end
yl_speak_up.input_fs_edit_preconditions = function(player, formname, fields)
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 p_id = yl_speak_up.speak_to[pname].p_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_prereq) then
local dialog = yl_speak_up.speak_to[pname].dialog
if(dialog and dialog.n_dialogs
and p_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 prerequirement
dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites[ p_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)..": ".."Pre(C)ondition "..tostring(p_id)..
" deleted for option "..tostring(o_id)..".")
-- TODO: when trying to save: save to disk as well?
-- show the new/changed precondition
-- 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:edit_preconditions",
formspec = "size[6,2]"..
"label[0.2,0.5;Pre(C)ondition \""..
minetest.formspec_escape(tostring(p_id))..
"\" has been deleted.]"..
"button[1.5,1.5;2,0.9;back_from_delete_precondition;Back]"})
return
end
end
if(fields.select_block_pos) then
minetest.chat_send_player(pname,
"Please punch the block you want to check in your precondition!")
-- this formspec expects the block punch:
yl_speak_up.speak_to[pname].expect_block_punch = "edit_preconditions"
return
end
-- field inputs: those do not trigger a sending of the formspec on their own
-- are we talking about an inventory?
local data = yl_speak_up.speak_to[pname].tmp_prereq
if(fields.inv_stack_name and fields.inv_stack_name ~= ""
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:edit_preconditions",
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
elseif(fields.var_cmp_value
and data and data.what and data.what >= 5 and data.what <= 6) then
data.var_cmp_value = fields.var_cmp_value
end
-- the save button was pressed
if(fields.save_prereq 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 pq = {}
-- determine p_type
pq.p_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 prereq = dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites
-- set p_id appropriately
if(not(p_id) or p_id == "new") then
p_id = "p_"..yl_speak_up.find_next_id(prereq)
if(not(prereq)) then
dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites = {}
prereq = dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites
end
end
pq.p_id = p_id
-- "an internal state (i.e. of a quest)", -- 2
if(data.what == 2) then
pq.p_value = values_operator[ data.operator ]
pq.p_var_cmp_value = (data.var_cmp_value or "")
-- TODO: p_variable
-- "a block somewhere", -- 3
elseif(data.what == 3) then
pq.p_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:edit_preconditions",
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(pq.p_value
and (pq.p_value == "node_is_like"
or pq.p_value == "node_is_diffrent_from")) then
pq.p_node = data.node_data.name
pq.p_param2 = data.node_data.param2
end
-- we also need to store the position of the node
pq.p_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(data.block == 5) then
pq.p_pos.y = pq.p_pos.y + 1
end
-- "a trade", -- 4
elseif(data.what == 4) then
-- this depends on the trade associated with that option; therefore,
-- it does not need any more parameters (they come dynamicly from the
-- trade)
pq.p_value = values_trade[ data.trade ]
-- "the inventory of the player", -- 5
-- "the inventory of the NPC", -- 6
elseif(data.what == 5 or data.what == 6) then
pq.p_value = values_inv[ data.inv ]
if(pq.p_value and pq.p_value ~= "inv_is_empty") then
-- we have checked this value earlier on
pq[ "p_itemstack" ] = data.inv_stack_name
end
end
-- only save if something was actually selected
if(pq.p_value) then
-- store the change in the dialog
dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites[ p_id ] = pq
-- clear up data
yl_speak_up.speak_to[pname].p_id = nil
yl_speak_up.speak_to[pname].tmp_prereq = 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)..": ".."Pre(C)ondition "..tostring(p_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, "edit_preconditions", p_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_prereq = { 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_prereq.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_prereq.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_prereq.block = nr
-- select data regarding a variable
elseif(fields.select_variable) then
-- TODO: this needs to include player-specific variables
local nr = table.indexof(check_variable, fields.select_variable)
yl_speak_up.speak_to[pname].tmp_prereq.variable = nr
-- 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_prereq.operator = nr
end
-- the player wants to change/edit a precondition
if(not(fields.back)
and (fields.change_prereq 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.back_from_error_msg)) then
yl_speak_up.show_fs(player, "edit_preconditions")
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="edit_precondition"})
end
yl_speak_up.get_fs_edit_preconditions = function(player, table_click_result)
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 p_id = yl_speak_up.speak_to[pname].p_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 prereq = dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites
if(not(prereq)) then
prereq = {}
end
-- did we arrive here through clicking on a prereq in the dialog edit options menu?
if(table_click_result or prereq[ table_click_result ]) then
if(not(prereq[ table_click_result ])) then
-- which precondition has the player selected?
local sorted_key_list = yl_speak_up.sort_keys(prereq)
local selected = minetest.explode_table_event(table_click_result)
-- use "new" if nothing fits
p_id = "new"
if((selected.type == "CHG" or selected.type == "DLC")
and selected.row <= #sorted_key_list) then
p_id = sorted_key_list[ selected.row ]
end
if( p_id == "new" and #sorted_key_list >= yl_speak_up.max_prerequirements) then
return "size[9,1.5]"..
"label[0.2,0.0;There are only up to "..
minetest.formspec_escape(yl_speak_up.max_prerequirements)..
" pre(C)onditions allowed per dialog option.]"..
"button[2.0,0.8;1.0,0.9;back;Back]"
end
else
-- allow to directly specify a p_id to show
p_id = table_click_result
end
-- store which prereq we are talking about
yl_speak_up.speak_to[pname].p_id = p_id
-- display the selected prereq
if(p_id ~= "new") then
return "formspec_version[3]"..
"size[20,3]"..
"bgcolor[#00000000;false]"..
"label[0.2,0.5;Selected pre(C)ondition:]"..
"tablecolumns[text;color,span=1;text;text]"..
"table[0.2,0.8;19.6,0.7;table_of_preconditions;"..
minetest.formspec_escape(prereq[ p_id ].p_id)..",#FFFF00,"..
minetest.formspec_escape(prereq[ p_id ].p_type)..","..
minetest.formspec_escape(
yl_speak_up.show_precondition(prereq[ p_id ]))..";0]"..
"button[2.0,1.8;1.5,0.9;delete_prereq;Delete]"..
"button[4.0,1.8;1.5,0.9;change_prereq;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_prereq
if(not(data) or not(data.what)) then
data = { what = 1}
end
-- fallback
if(not(p_id)) then
p_id = "new"
end
local save_button = "button[5.0,9.0;1,0.7;save_prereq;Save]"
local formspec =
"formspec_version[3]"..
"size[20,10]"..
"label[5,0.5;Edit pre(C)ondition \""..minetest.formspec_escape(p_id).."\"]"..
"label[0.2,1.5;What do you want to check in this precondition?]"..
"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
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
if(not(data.operator) or data.operator == 1 or data.operator >= 8) then
field_for_value = "label[11.2,5.1;- not used for this operator -]"
end
-- TODO: the list of available variables needs to be extended
-- with the ones the player has read access to
formspec = formspec..
"label[0.2,3.3;The following expression shall be true:]"..
"label[0.2,4.3;Name of variable:]"..
"dropdown[0.2,4.8;6.5,0.6;select_variable;"..
table.concat(check_variable, ",")..";"..
tostring(data.variable)..";]"..
"label[7.0,4.3;Operator:]"..
"dropdown[7.0,4.8;4.0,0.6;select_operator;"..
table.concat(check_operator, ",")..";"..
tostring(data.operator)..";]"..
"label[11.2,4.3;Value to compare with:]"..
field_for_value
-- "a trade", -- 4
elseif(data.what 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
elseif(data.what 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.]"
-- "a block somewhere", -- 3
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
if(block_pos) then
-- store for later usage
data.block_pos = block_pos
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.",
if(data.block and data.block == 5) then
tmp_pos.y = block_pos.y + 1
end
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.",
if(data.block 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
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;The following shall be true about the block:]"..
"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.\n"..
"After punching it, you will be returned to this menu.]"
end
return formspec..save_button
end