2
0
Fork 0
yl_speak_up/fs/fs_edit_preconditions.lua

363 lines
15 KiB
Lua

-- This file contains what is necessary to add/edit a precondition.
--
-- 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
--
-- the value of a property of the NPC (for generic NPC) ("property"):
-- p_value name of the property that shall be checked
-- p_operator operator for cheking the property against p_expected_val
-- p_var_cmp_value the expected value of the property
--
-- something that has to be calculated or evaluated (=call a function) ("evaluate"):
-- p_value the name of the function that is to be called
-- p_param1 the first paramter (optional; depends on function)
-- ..
-- p_param9 the 9th parameter (optional; depends on function)
-- p_operator operator for checking the result
-- p_var_cmp_value compare the result of the function with this value
--
-- 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
--
-- 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
"the value of a property of the NPC (for generic NPC)",
"something that has to be calculated or evaluated (=call a function)",
"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
"The preconditions of another dialog option are fulfilled/not fulfilled.", -- 9 -> 11
"nothing - always true (useful for generic dialogs)",
"nothing - always false (useful for temporally deactivating an option)",
}
-- how to store these as p_type in the precondition:
local values_what = {"", "state", "property", "evaluate", "block", "trade",
"player_inv", "npc_inv", "block_inv",
"player_offered_item",
-- "function" requires npc_master priv:
"function",
-- depends on the preconditions of another option
"other",
"true", "false"}
-- 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"}
-- 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 that are server-specific
for i, v in ipairs(yl_speak_up.custom_server_functions.precondition_descriptions) 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 == "true") then
return "true: Always true."
elseif(p.p_type == "false") then
return "false: Always false."
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 == "property") then
local i = math.max(1,table.indexof(values_operator, p.p_operator))
return tostring(p.p_value)..
" "..tostring(check_operator[i])..
" "..tostring(p.p_var_cmp_value)
elseif(p.p_type == "evaluate") then
local str = ""
for i = 1, 9 do
str = str..tostring(p["p_param"..tostring(i)])
if(i < 9) then
str = str..","
end
end
local i_op = math.max(1,table.indexof(values_operator, p.p_operator))
return "FUNCTION["..tostring(p.p_value).."]"..
"("..str..") "..tostring(check_operator[i_op])..
" "..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 == "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
-- 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.handle_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.build_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 (in some cases parameter):",
"The following shall be true about the block:"
)
end