moved code from calculate_displayable_options to yl_speak_up.eval_precondition_function; added yl_speak_up.eval_all_preconditions; added yl_speak_up.eval_precondition

This commit is contained in:
Sokomine 2021-06-07 19:54:46 +02:00
parent 80c25fb9fa
commit b0f141b09d
2 changed files with 212 additions and 103 deletions

View File

@ -109,7 +109,17 @@ local check_variable = {
-- 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(p.p_type == "state") then
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
@ -158,6 +168,141 @@ yl_speak_up.show_precondition = function(p)
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

View File

@ -349,6 +349,70 @@ local function delete_option(n_id, d_id, o_id)
end
-- evaluate those preconditions of type "function" (set by the staff)
-- WARNING: This is extremly powerful!
-- The code is taken out of the function
-- calculate_displayable_options(..)
-- (written by AliasAlreadyTaken).
-- It requires the npc_master priv to add or edit this prereq.
-- The function is called by yl_speak_up.eval_precondition
yl_speak_up.eval_precondition_function = function(player, p_v)
local pname = player:get_player_name()
--minetest.chat_send_all("this is in a single prereq: "..dump(p_v))
local p_id = p_v.p_id
if p_v.p_type ~= "function" then
return true
end
local code = p_v.p_value
if code:byte(1) == 27 then
local obj = yl_speak_up.speak_to[pname].obj
local n_id = yl_speak_up.speak_to[pname].n_id
local npc = get_number_from_id(n_id)
if obj:get_luaentity() and tonumber(npc) then
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not compile the content of "..p_id.." :"..dump(code) .. " because of illegal bytecode for player "..pname)
else
minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not compile the content of "..p_id.." :"..dump(code) .. " for player unknown because of illegal bytecode")
end
end
local f, msg = loadstring("return function(playername) " .. code .. " end")
if not f then
local obj = yl_speak_up.speak_to[pname].obj
local n_id = yl_speak_up.speak_to[pname].n_id
local npc = get_number_from_id(n_id)
if obj:get_luaentity() and tonumber(npc) then
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not compile the content of "..p_id.." :"..dump(code) .. " for player "..pname)
else
minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not compile the content of "..p_id.." :"..dump(code) .. " for player unknown")
end
else
local func = f()
local ok, ret = pcall(func,pname)
if not ok then
local obj = yl_speak_up.speak_to[pname].obj
local n_id = yl_speak_up.speak_to[pname].n_id
local npc = get_number_from_id(n_id)
if obj:get_luaentity() and tonumber(npc) then
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not execute the content of "..p_id.." :"..dump(code) .. " for player "..pname)
else
minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not execute the content of "..p_id.." :"..dump(code) .. " for player unknown")
end
end
if type(ret) == "boolean" then
return ret
end
end
-- fallback
return false
end
local function calculate_displayable_options(pname, d_options)
-- Let's go through all the options and see if we need to display them to the user
@ -361,113 +425,13 @@ local function calculate_displayable_options(pname, d_options)
end
for o_k, o_v in pairs(d_options) do
--minetest.chat_send_all("#####"..o_k.."#####")
local o_p_met = true
if o_v.o_prerequisites == nil then
--minetest.chat_send_all("display this because no prereq:"..dump(o_v))
else
--minetest.chat_send_all("prereqs exists")
if next(o_v.o_prerequisites) == nil then
--minetest.chat_send_all("prereqs exist, but empty")
else
--minetest.chat_send_all("prereqs exists and not empty")
--minetest.chat_send_all("if all prereqs are met, then we can display the option")
local p_met = {}
for p_k, p_v in pairs(o_v.o_prerequisites) do
--minetest.chat_send_all("this is in a single prereq: "..dump(p_v))
p_met[p_k] = false
local p_id = p_v.p_id
if p_v.p_type == "item" then
--minetest.chat_send_all("item! Does the player have this thing?")
if player:get_inventory():contains_item("main", p_v.p_value) then
--minetest.chat_send_all("found item:"..p_v.p_value)
p_met[p_k] = true
end
end
if p_v.p_type == "quest" then
--minetest.chat_send_all("quest! let's call the quest api?")
p_met[p_k] = false
end
if p_v.p_type == "function" then
local code = p_v.p_value
if code:byte(1) == 27 then
local obj = yl_speak_up.speak_to[pname].obj
local n_id = yl_speak_up.speak_to[pname].n_id
local npc = get_number_from_id(n_id)
if obj:get_luaentity() and tonumber(npc) then
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not compile the content of "..p_id.." :"..dump(code) .. " because of illegal bytecode for player "..pname)
else
minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not compile the content of "..p_id.." :"..dump(code) .. " for player unknown because of illegal bytecode")
end
end
local f, msg = loadstring("return function(playername) " .. code .. " end")
if not f then
local obj = yl_speak_up.speak_to[pname].obj
local n_id = yl_speak_up.speak_to[pname].n_id
local npc = get_number_from_id(n_id)
if obj:get_luaentity() and tonumber(npc) then
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not compile the content of "..p_id.." :"..dump(code) .. " for player "..pname)
else
minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not compile the content of "..p_id.." :"..dump(code) .. " for player unknown")
end
else
local func = f()
local ok, ret = pcall(func,pname)
if not ok then
local obj = yl_speak_up.speak_to[pname].obj
local n_id = yl_speak_up.speak_to[pname].n_id
local npc = get_number_from_id(n_id)
if obj:get_luaentity() and tonumber(npc) then
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not execute the content of "..p_id.." :"..dump(code) .. " for player "..pname)
else
minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not execute the content of "..p_id.." :"..dump(code) .. " for player unknown")
end
end
if type(ret) == "boolean" then
p_met[p_k] = ret
end
end
end
if p_v.p_type == "auto" then
--minetest.chat_send_all("auto! what shall we do now?")
p_met[p_k] = true
end
end
-- is there a "false" in the p_met ?
for m_k, m_v in pairs(p_met) do
--minetest.chat_send_all(o_k..",m_v="..dump(m_v))
if m_v == false then
o_p_met = false
end
end
end
end
-- Can we display this option?
retval[o_k] = o_p_met
--[[
if o_p_met == true then
minetest.chat_send_all("Prereqs say YES")
retval[o_k] = o_p_met
else
minetest.chat_send_all("Prereqs say NOO")
end
]]--
retval[o_k] = yl_speak_up.eval_all_preconditions(player, o_v.o_prerequisites, o_k)
end
return retval
end
local function calculate_portrait(pname, n_id)
local tex = yl_speak_up.speak_to[pname].textures