From 8a3daab3c0781474b5f5eb134f7dbc1cf6f5923a Mon Sep 17 00:00:00 2001 From: Sokomine Date: Tue, 8 Jun 2021 02:09:16 +0200 Subject: [PATCH] added quest_api.lua; added variable handling in fs_edit_preconditions.lua --- fs_edit_preconditions.lua | 136 ++++++++++++++++++++++++++++++-------- init.lua | 2 + quest_api.lua | 22 ++++++ 3 files changed, 131 insertions(+), 29 deletions(-) create mode 100644 quest_api.lua diff --git a/fs_edit_preconditions.lua b/fs_edit_preconditions.lua index e79323a..02763f6 100644 --- a/fs_edit_preconditions.lua +++ b/fs_edit_preconditions.lua @@ -1,6 +1,5 @@ -- 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: @@ -11,7 +10,8 @@ -- p_value used to store the subtype of p_type -- -- a state/variable: --- p_variable TODO +-- 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 -- @@ -101,11 +101,29 @@ local values_operator = {"", "==", "~=", ">=", ">", "<=", "<", "not", "is_set", -- some internal ones... local check_variable = { "- please select -", - "(internal) hour of ingame day", - "(internal) player's health points", + "(internal) hour of ingame day", -- 2 + "(internal) player's health points", -- 3 } +-- get the list of variables the player has read access to +local get_sorted_player_var_list = 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) @@ -120,14 +138,19 @@ yl_speak_up.show_precondition = function(p) 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).." )" + if(not(p.p_operator)) then + return "Error: Operator not defined." + elseif(p.p_operator == "not") then + return "not( VALUE_OF[ "..tostring(p.p_variable).." ] )" elseif(p.p_operator == "is_set") then - return tostring(p.p_variable).." ~= nil (is_set)" + return "VALUE_OF[ "..tostring(p.p_variable).." ] ~= nil (is_set)" elseif(p.p_operator == "is_unset") then - return tostring(p.p_variable).." == nil (is_unset)" + return "VALUE_OF[ "..tostring(p.p_variable).." ] == nil (is_unset)" end - return tostring(p.p_variable).." "..tostring(p.p_operator).." ".. + if(p.p_var_cmp_value == "") then + return "VALUE_OF[ "..tostring(p.p_variable).." ] "..tostring(p.p_operator).." \"\"" + end + return "VALUE_OF[ "..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" @@ -216,18 +239,65 @@ yl_speak_up.eval_precondition = function(player, n_id, p) -- 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)" + 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 + var_val = yl_speak_up.get_quest_variable_value(owner, pname, p.p_variable) end - return tostring(p.p_variable).." "..tostring(p.p_operator).." ".. - tostring(p.p_var_cmp_value) ---]] + + 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 + -- 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 + -- 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(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(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(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 @@ -386,7 +456,7 @@ yl_speak_up.input_fs_edit_preconditions = function(player, formname, fields) -- comparison value for a variable elseif(fields.var_cmp_value - and data and data.what and data.what >= 5 and data.what <= 6) then + and data and data.what and data.what == 2) then data.var_cmp_value = fields.var_cmp_value end @@ -420,9 +490,10 @@ yl_speak_up.input_fs_edit_preconditions = function(player, formname, fields) -- "an internal state (i.e. of a quest)", -- 2 if(data.what == 2) then - pq.p_value = values_operator[ data.operator ] + pq.p_value = "expression" + pq.p_operator = values_operator[ data.operator ] pq.p_var_cmp_value = (data.var_cmp_value or "") - -- TODO: p_variable + pq.p_variable = data.variable_name -- "a block somewhere", -- 3 elseif(data.what == 3) then @@ -509,9 +580,14 @@ yl_speak_up.input_fs_edit_preconditions = function(player, formname, fields) -- 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 + -- get the list of available variables (with the same elements + -- and the same sort order as when the dropdown was displayed) + local var_list = get_sorted_player_var_list(pname) + local nr = table.indexof(var_list, fields.select_variable) + if(nr) then + yl_speak_up.speak_to[pname].tmp_prereq.variable = nr + yl_speak_up.speak_to[pname].tmp_prereq.variable_name = var_list[ nr ] + end -- select data regarding an operator elseif(fields.select_operator) then @@ -649,13 +725,15 @@ yl_speak_up.get_fs_edit_preconditions = function(player, table_click_result) 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 + -- the list of available variables needs to be extended with the ones + -- the player has read access to, and the order has to be constant + -- (because dropdown just returns an index) + local var_list = get_sorted_player_var_list(pname) 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, ",")..";".. + table.concat(var_list, ",")..";".. tostring(data.variable)..";]".. "label[7.0,4.3;Operator:]".. "dropdown[7.0,4.8;4.0,0.6;select_operator;".. diff --git a/init.lua b/init.lua index 2e9025d..dcd4ceb 100644 --- a/init.lua +++ b/init.lua @@ -33,6 +33,8 @@ dofile(modpath .. "inventory.lua") dofile(modpath .. "trade_simple.lua") -- easily accessible list of all trades the NPC offers dofile(modpath .. "trade_list.lua") +-- handle variables for quests for player-owned NPC +dofile(modpath .. "quest_api.lua") -- the main functionality of the mod dofile(modpath .. "functions.lua") -- a way of determining a node position diff --git a/quest_api.lua b/quest_api.lua new file mode 100644 index 0000000..fca2717 --- /dev/null +++ b/quest_api.lua @@ -0,0 +1,22 @@ + +-- just some handling of variables + +yl_speak_up.player_vars = {} + +-- get the value of a variable used by a player in an NPC +yl_speak_up.get_quest_variable_value = function(owner_name, current_player_name, variable_name) + -- TODO: actually implement + return tostring(owner_name).." "..tostring(current_player_name).." "..tostring(variable_name) +end + +-- which variables can player pname read and use in preconditions? +yl_speak_up.get_quest_variables_with_read_access = function(pname) + -- TODO: actually implement + return {"var_1","var_2","var_3", "var_4"} +end + +-- which variables can player pname write and use in effects/results? +yl_speak_up.get_quest_variables_with_write_access = function(pname) + -- TODO: actually implement + return {"var_1","var_2","var_3"} +end