-- TODO: check inscription of a sign? -- TODO: check mesecons related things? -- 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 -- p_value used to store the subtype of p_type -- -- a state/variable ("state"): -- p_variable name of a variable the player has read access to; -- dropdown list with allowed options -- p_operator selected from values_operator -- p_var_cmp_value can be set freely by the player -- -- a block in the world ("block"): -- 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 ("trade"): no variables needed (buy and pay stack -- follow from the trade set as action) -- -- an inventory: ("player_inv", "npc_inv" or "block_inv") -- p_itemstack an itemstack; needs to be a minetest.registered_item[..]; -- size/count is also checked -- -- the inventory of a block on the map: ("block_inv", in addition to the ones above) -- p_pos a position in the world; determined by asking the player -- to punch the block -- p_inv_list_name name of the inventory list of the block -- -- the player offered/gave the NPC an item: ("player_offered_item"): -- p_value an itemstack; needs to be a minetest.registered_item[..]; -- size/count is checked for some subtypes -- p_match_stack_size does the NPC expect exactly one stack size - or is -- more or less etc. also ok? -- p_item_group are items of this group instead of the exact item name -- also acceptable? -- p_item_desc the description of the itemstack (set by another quest NPC -- so that the player can distinguish it from other itemstacks -- with the same item name; see action "npc_gives") -- p_item_quest_id Special ID to make sure that it is really the *right* -- item and not just something the player faked with an -- engraving table or something similar -- -- a function ("function"): requires npc_master to create and edit -- p_value the lua code to execute and evaulate -- -- a custom function ("custon"): -- p_value parameter for the custom function -- -- depends on another option: -- p_value name of the other option of this dialog that is considered -- p_fulfilled shall option p_value be true or false? -- 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 "the inventory of a block somewhere", -- 7 "an item the player offered/gave to the NPC", -- 8 "execute Lua code (requires npc_master priv)", -- 7 -> 9 "Call custom functions that are supposed to be overridden by the server.", -- 8 -> 10 "The preconditions of another dialog option are fulfilled/not fulfilled.", -- 9 -> 11 } -- how to store these as p_type in the precondition: local values_what = {"", "state", "block", "trade", "player_inv", "npc_inv", "block_inv", "player_offered_item", -- "function" requires npc_master priv: "function", -- custom function (does not require npc_master priv) "custom", -- depends on the preconditions of another option "other"} -- 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 "more than x seconds ago", -- 11 "less than x seconds ago", -- 12 "has completed quest step", -- 13 "quest step *not* completed", -- 14 } -- 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", "more_than_x_seconds_ago","less_than_x_seconds_ago", "quest_step_done", "quest_step_not_done"} -- some internal ones... local check_variable = { -- "- please select -", -- this is automaticly added to the var list "(internal) hour of ingame day", -- 2 "(internal) player's health points", -- 3 } -- get the list of variables the player has read access to yl_speak_up.get_sorted_player_var_list_read_access = function(pname) local var_list = {} -- copy the values from check_variable for i, v in ipairs(check_variable) do table.insert(var_list, v) end -- get the list of variables the player can read local tmp = yl_speak_up.get_quest_variables_with_read_access(pname) -- sort that list (the dropdown formspec element returns just an index) table.sort(tmp) for i, v in ipairs(tmp) do table.insert(var_list, v) end return var_list end -- 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, pname) 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.p_value) elseif(p.p_type == "state") then local var_name = "VALUE_OF[ - ? - ]" if(p.p_variable) then var_name = "VALUE_OF[ "..tostring( yl_speak_up.strip_pname_from_var(p.p_variable, pname)).." ]" end if(not(p.p_operator)) then return "Error: Operator not defined." elseif(p.p_operator == "not") then return "not( "..var_name.." )" elseif(p.p_operator == "is_set") then return var_name.." ~= nil (is_set)" elseif(p.p_operator == "is_unset") then return var_name.." == nil (is_unset)" elseif(p.p_operator == "more_than_x_seconds_ago") then return var_name.." was set to current time ".. "*more* than "..tostring(p.p_var_cmp_value).." seconds ago" elseif(p.p_operator == "less_than_x_seconds_ago") then return var_name.." was set to current time ".. "*less* than "..tostring(p.p_var_cmp_value).." seconds ago" elseif(p.p_operator == "quest_step_done") then return var_name.." shows: player completed quest step \"".. tostring(p.p_var_cmp_value).."\" successfully" elseif(p.p_operator == "quest_step_not_done") then return var_name.." shows: player has not yet completed quest step \"".. tostring(p.p_var_cmp_value).."\"" end if(p.p_var_cmp_value == "") then return var_name.." "..tostring(p.p_operator).." \"\"" end return var_name.." "..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" or p.p_type == "block_inv") then local who = "The player" local what = "\""..tostring(p.p_itemstack).."\" in his inventory." if(p.p_type == "npc_inv") then who = "The NPC" elseif(p.p_type == "block_inv") 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) end who = "The block at "..minetest.pos_to_string(p.p_pos) what = "\""..tostring(p.p_itemstack).."\" in inventory list \"".. tostring(p.p_inv_list_name).."\"." end if(p.p_value == "inv_contains") then return who.." has "..what elseif(p.p_value == "inv_does_not_contain") then return who.." does not have "..what elseif(p.p_value == "has_room_for") then return who.." has room for "..what elseif(p.p_value == "inv_is_empty") then if(p.p_type == "block_inv") then return who.." has an empty inventory list \"".. tostring(p.p_inv_list_name).."\"." end return who.." has an empty inventory." end elseif(p.p_type == "player_offered_item") then local item = tostring(p.p_value:split(" ")[1]) local amount = tostring(p.p_value:split(" ")[2]) local match = "any amount" if(p.p_match_stack_size == "any") then match = "any amount" elseif(p.p_match_stack_size == "exactly") then match = "exactly "..tostring(amount) elseif(p.p_match_stack_size == "less" or p.p_match_stack_size == "more") then match = p.p_match_stack_size.." than "..tostring(amount) elseif(p.p_match_stack_size == "another") then match = "another amount than " ..tostring(amount) end if(p.p_item_group and p.p_item_group ~= "") then return "The player offered "..tostring(match).." item(s) of the group \"".. tostring(item).."\"." elseif((p.p_item_quest_id and p.p_item_quest_id ~= "") or (p.p_item_desc and p.p_item_desc ~= "")) then return "The player offered "..tostring(match).." of \"".. tostring(p.p_item_desc or "- default description -").. "\" (\""..tostring(item or "- ? -").."\") ".. "with ID \""..tostring(p.p_item_quest_id or "- no special ID -").."\"." else return "The player offered "..tostring(match).." of \""..tostring(item).."\"." end elseif(p.p_type == "custom") then return "Call custom function with param: \""..tostring(p.p_value).."\"." elseif(p.p_type == "other") then local fulfilled = "fulfilled" if(not(p.p_fulfilled) or p.p_fulfilled ~= "true") then fulfilled = "*not* fulfilled" end return "The preconditions for dialog option \""..tostring(p.p_value).."\" are ".. fulfilled.."." end -- fallback return tostring(p.p_value) end -- this is called directly in yl_speak_up.get_fs_talkdialog -- it returns a list of options whose preconditions are fulfilled -- allow_recursion may be false - we need to avoid infinite loops yl_speak_up.calculate_displayable_options = function(pname, d_options, in_edit_mode, allow_recursion) -- Let's go through all the options and see if we need to display them to the user local retval = {} local player = minetest.get_player_by_name(pname) if d_options == nil then return {} end -- sort the entries by o_sort so that preconditions referencing options earlier in the -- list can work without causing loops or the like local sorted_list = yl_speak_up.get_sorted_options(d_options, "o_sort") for i, o_k in ipairs(sorted_list) do if(not(in_edit_mode)) then local o_v = d_options[ o_k ] -- Can we display this option? retval[o_k] = yl_speak_up.eval_all_preconditions(player, o_v.o_prerequisites, o_k, retval) -- do we need to take care of an automatic autoanswer? if(retval[o_k] and retval[o_k] == true and o_v.o_autoanswer and o_v.o_autoanswer == 1 and allow_recursion) then -- abort here - because we already know which option needs to be selected next retval["autoanswer"] = o_k return retval end -- if in edit mode: no need to evaluate preconditions else retval[o_k ] = true end end return retval 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, other_options_true_or_false) local pname = player:get_player_name() local n_id = yl_speak_up.speak_to[pname].n_id if(not(prereq)) then yl_speak_up.debug_msg(player, n_id, o_id, "No preconditions given.") -- no prerequirements? then they are automaticly fulfilled return true end yl_speak_up.debug_msg(player, n_id, o_id, "Checking preconditions..") for k, p in pairs(prereq) do yl_speak_up.debug_msg(player, n_id, o_id, "..checking ".. tostring(p.p_id)..": "..yl_speak_up.show_precondition(p, pname)) if(not(yl_speak_up.eval_precondition(player, n_id, p, other_options_true_or_false))) then yl_speak_up.debug_msg(player, n_id, o_id, tostring(p.p_id).. " -> is false. Aborting.") -- no need to look any further - once we hit a false, it'll stay false return false end end -- all preconditions are true yl_speak_up.debug_msg(player, n_id, o_id, "OK. All preconditions true.") 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, other_options_true_or_false) 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_and_execute_function(player, p, "p_") elseif(p.p_type == "state") then local var_val = false if(not(p.p_variable) or p.p_variable == "") then -- broken precondition return false -- "(internal) hour of ingame day", -- 2 elseif(p.p_variable == check_variable[2]) then -- timeofday is between 0..1; translate to 24 hours var_val = math.floor((minetest.get_timeofday() * 24)+0.5) -- "(internal) player's health points", -- 3 elseif(p.p_variable == check_variable[3]) then var_val = player:get_hp() else local pname = player:get_player_name() local owner = yl_speak_up.npc_owner[ n_id ] -- get the value of the variable -- the owner is alrady encoded in the variable name var_val = yl_speak_up.get_quest_variable_value(pname, p.p_variable) end if(p.p_operator == "not") then return not(var_val) elseif(p.p_operator == "is_set") then return var_val ~= nil elseif(p.p_operator == "is_unset") then return var_val == nil -- for security reasons: do this manually instead of just evaluating a term elseif(p.p_operator == "==") then if(p.p_var_cmp_value == nil) then return false end -- best do these comparisons in string form to make sure both are of same type return tostring(var_val) == tostring(p.p_var_cmp_value) elseif(p.p_operator == "~=") then return tostring(var_val) ~= tostring(p.p_var_cmp_value) elseif(p.p_operator == ">=") then if(p.p_var_cmp_value == nil) then return false end -- compare numeric if possible if(tonumber(var_val) and tonumber(p.p_var_cmp_value)) then return tonumber(var_val) >= tonumber(p.p_var_cmp_value) -- fallback: compare as strings else return tostring(var_val) >= tostring(p.p_var_cmp_value) end elseif(p.p_operator == ">") then if(p.p_var_cmp_value == nil) then return false end if(tonumber(var_val) and tonumber(p.p_var_cmp_value)) then return tonumber(var_val) > tonumber(p.p_var_cmp_value) else return tostring(var_val) > tostring(p.p_var_cmp_value) end elseif(p.p_operator == "<=") then if(p.p_var_cmp_value == nil) then return false end if(tonumber(var_val) and tonumber(p.p_var_cmp_value)) then return tonumber(var_val) <= tonumber(p.p_var_cmp_value) else return tostring(var_val) <= tostring(p.p_var_cmp_value) end elseif(p.p_operator == "<") then if(p.p_var_cmp_value == nil) then return false end if(tonumber(var_val) and tonumber(p.p_var_cmp_value)) then return tonumber(var_val) < tonumber(p.p_var_cmp_value) else return tostring(var_val) < tostring(p.p_var_cmp_value) end elseif(p.p_operator == "more_than_x_seconds_ago") then if(p.p_var_cmp_value == nil) then return false end if(not(tonumber(var_val)) or not(tonumber(p.p_var_cmp_value))) then return true end return (tonumber(var_val) + tonumber(p.p_var_cmp_value)) < math.floor(minetest.get_us_time()/1000000) elseif(p.p_operator == "less_than_x_seconds_ago") then if(p.p_var_cmp_value == nil) then return false end if(not(tonumber(var_val)) or not(tonumber(p.p_var_cmp_value))) then return false end return (tonumber(var_val) + tonumber(p.p_var_cmp_value)) > minetest.get_us_time()/1000000 -- this is currently equivalent to >= but may change in the future -- TODO: quest steps may be strings in the future elseif(p.p_operator == "quest_step_done") then -- if the variable is not set at all, then the quest step definitely -- has not been reached yet if((p.p_var_cmp_value == nil) or (var_val == nil)) then return false end -- compare numeric if possible if(tonumber(var_val) and tonumber(p.p_var_cmp_value)) then return tonumber(var_val) >= tonumber(p.p_var_cmp_value) -- fallback: compare as strings else return tostring(var_val) >= tostring(p.p_var_cmp_value) end -- this is currently equivalent to < but may change in the future -- TODO: quest steps may be strings in the future elseif(p.p_operator == "quest_step_not_done") then -- if the variable is not set at all, then the quest step definitely -- has not been reached yet if((p.p_var_cmp_value == nil) or (var_val == nil)) then return true end if(tonumber(var_val) and tonumber(p.p_var_cmp_value)) then return tonumber(var_val) < tonumber(p.p_var_cmp_value) else return tostring(var_val) < tostring(p.p_var_cmp_value) end end -- unsupported operator return false 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" or p.p_type == "block_inv") then local inv = nil local inv_name = "main" -- determine the right inventory if(p.p_type == "player_inv") then inv = player:get_inventory() elseif(p.p_type == "npc_inv") then inv = minetest.get_inventory({type="detached", name="yl_speak_up_npc_"..tostring(n_id)}) inv_name = "npc_main" elseif(p.p_type == "block_inv") 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 end local meta = minetest.get_meta(p.p_pos) if(not(meta)) then return false end inv = meta:get_inventory() if(not(inv)) then return false end inv_name = p.p_inv_list_name 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 elseif(p.p_type == "player_offered_item") then local pname = player:get_player_name() local inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname}) local stack_got = inv:get_stack("npc_wants",1) if(stack_got:is_empty()) then return false -- empty stack end local stack_wanted = ItemStack(p.p_value) -- check group if(p.p_item_group and p.p_item_group ~= "") then local g = minetest.get_item_group(stack_got:get_name(), p.p_item_group) if(not(g) or g == 0) then return false -- wrong group end -- or: check item name elseif(stack_got:get_name() ~= stack_wanted:get_name()) then return false -- wrong item end -- independent of that: check stack size if(p.p_match_stack_size and p.p_match_stack_size ~= "") then local c_got = stack_got:get_count() local c_wanted = stack_wanted:get_count() if( p.p_match_stack_size == "exactly" and c_got ~= c_wanted) then return false -- not exactly the same amount as the wanted one elseif(p.p_match_stack_size == "less" and c_got >= c_wanted) then return false -- didn't get less than the given number elseif(p.p_match_stack_size == "more" and c_got <= c_wanted) then return false -- didn't get more than the given number elseif(p.p_match_stack_size == "another" and c_got == c_wanted) then return false -- got the same than the given number end end -- check quest_id if(p.p_item_quest_id and p.p_item_quest_id ~= "") then local meta = stack_got:get_meta() -- we don't check here if the item was given by the right NPC; -- only the quest id has to fit if(meta:get_string("yl_speak_up:quest_id") ~= p.p_item_quest_id) then return false -- wrong quest_id end -- was this quest item given to another player? if(meta:get_string("yl_speak_up:quest_item_for") ~= pname) then return false -- wrong player end end -- all ok return true elseif(p.p_type == "custom") then -- execute the custom function return yl_speak_up.precondition_custom(player, p.p_value) elseif(p.p_type == "other") then -- are the preconditions of another option fulfilled? return (p.p_value and other_options_true_or_false and other_options_true_or_false[ p.p_value ] ~= nil and tostring(other_options_true_or_false[ p.p_value ]) == tostring(p.p_fulfilled)) end -- fallback - unknown type return false end -- these are only wrapper functions for those in fs_edit_general.lua yl_speak_up.input_fs_edit_preconditions = function(player, formname, fields) return yl_speak_up.input_fs_edit_option_related(player, formname, fields, "p_", "o_prerequisites", yl_speak_up.max_prerequirements, "pre(C)ondition", "tmp_prereq", "Please punch the block you want to check in your precondition!", values_what, values_operator, values_block, values_trade, values_inv, check_what, check_operator, check_block, check_trade, check_inv, -- player variables with read access yl_speak_up.get_sorted_player_var_list_read_access, "edit_preconditions" ) end yl_speak_up.get_fs_edit_preconditions = function(player, table_click_result) return yl_speak_up.get_fs_edit_option_related(player, table_click_result, "p_", "o_prerequisites", yl_speak_up.max_prerequirements, "pre(C)ondition", "tmp_prereq", "What do you want to check in this precondition?", values_what, values_operator, values_block, values_trade, values_inv, check_what, check_operator, check_block, check_trade, check_inv, -- player variables with read access yl_speak_up.get_sorted_player_var_list_read_access, -- show one precondition element yl_speak_up.show_precondition, "table_of_preconditions", "The following expression shall be true:", "Operator:", "Value to compare with:", "The following shall be true about the block:" ) end