mirror of
https://gitea.your-land.de/Sokomine/yl_speak_up.git
synced 2025-06-20 14:18:02 +02:00
split up fs_edit_preconditions into fs_edit and exec_eval_
This commit is contained in:
parent
ce1e191eaf
commit
9039fe08b9
354
exec_eval_preconditions.lua
Normal file
354
exec_eval_preconditions.lua
Normal file
@ -0,0 +1,354 @@
|
||||
-- This file contains what is necessary to execute/evaluate a precondition.
|
||||
|
||||
-- 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
|
@ -1,6 +1,8 @@
|
||||
-- TODO: check inscription of a sign?
|
||||
-- TODO: check mesecons related things?
|
||||
|
||||
--
|
||||
-- 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
|
||||
@ -303,360 +305,6 @@ yl_speak_up.show_precondition = function(p, pname)
|
||||
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)
|
||||
|
2
init.lua
2
init.lua
@ -27,6 +27,8 @@ dofile(modpath .. "fs_save_or_discard_or_back.lua")
|
||||
-- of course you can change them only if you have access to
|
||||
-- the server's file system or can execute lua code.
|
||||
dofile(modpath .. "custrom_functions_you_can_override.lua")
|
||||
-- execute preconditions, actions and effects
|
||||
dofile(modpath .. "exec_eval_preconditions.lua")
|
||||
-- some helper functions for formatting text for a formspec talbe
|
||||
dofile(modpath .. "print_as_table.lua")
|
||||
-- create i.e. a dropdown list of player names
|
||||
|
Loading…
Reference in New Issue
Block a user