diff --git a/fs_edit_general.lua b/fs_edit_general.lua index 678585a..9267244 100644 --- a/fs_edit_general.lua +++ b/fs_edit_general.lua @@ -629,6 +629,16 @@ yl_speak_up.save_element_p_or_a_or_e = function( local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs) v[ "a_on_failure" ] = sorted_dialog_list[ data.action_failure_dialog ] end + + -- "The preconditions of another dialog option are fulfilled/not fulfilled.", -- 9 + -- precondition: 9 + elseif(data.what and id_prefix == "p_" and data.what == 9) then + if(data.other_o_id and data.other_o_id ~= "-select-") then + v[ "p_value" ] = data.other_o_id + end + if(data.fulfilled and data.fulfilled ~= "-select-") then + v[ "p_fulfilled" ] = data.fulfilled + end end v[ "alternate_text" ] = data.alternate_text @@ -911,6 +921,15 @@ yl_speak_up.input_fs_edit_option_related = function(player, formname, fields, local nr = table.indexof(check_operator, fields.select_operator) yl_speak_up.speak_to[pname][ tmp_data_cache ].operator = nr end + -- another dialog option is true or false + -- Note: "-select-" can be choosen here as well + if(fields.select_other_o_id and fields.select_other_o_id ~= "") then + yl_speak_up.speak_to[pname][ tmp_data_cache ].other_o_id = fields.select_other_o_id + end + -- Note: "-select-" can be choosen here as well + if(fields.select_fulfilled and fields.select_fulfilled ~= "") then + yl_speak_up.speak_to[pname][ tmp_data_cache ].fulfilled = fields.select_fulfilled + end if(fields.select_on_failure) then -- in this case we really want the name of the target dialog yl_speak_up.speak_to[pname][ tmp_data_cache ].on_failure = fields.select_on_failure @@ -987,6 +1006,8 @@ yl_speak_up.input_fs_edit_option_related = function(player, formname, fields, or fields.select_on_action_failure or fields.back_from_error_msg or fields.store_item_name + or fields.select_other_o_id + or fields.select_fulfilled or was_changed or fields.key_enter or fields.quit @@ -1350,6 +1371,12 @@ yl_speak_up.get_fs_edit_option_related = function(player, table_click_result, or (id_prefix == "r_" and data.what == 11))) then return yl_speak_up.get_fs_edit_option_all_custom( pname, dialog, formspec, data, id_prefix, save_button, e) + + -- "The preconditions of another dialog option are fulfilled/not fulfilled.", -- 9 + -- precondition: 9 + elseif(data.what and id_prefix == "p_" and data.what == 9) then + return yl_speak_up.get_fs_other_option_preconditions( + pname, dialog, formspec, data, id_prefix, save_button, e) end -- create a new precondition, action or effect return formspec..save_button @@ -1959,6 +1986,69 @@ yl_speak_up.get_fs_edit_option_all_custom = function( return formspec..save_button end + +-- "The preconditions of another dialog option are fulfilled/not fulfilled.", -- 9 +-- precondition: 9 +yl_speak_up.get_fs_other_option_preconditions = function( + pname, dialog, formspec, data, id_prefix, save_button, e) + local dialog = yl_speak_up.speak_to[pname].dialog + local d_id = yl_speak_up.speak_to[pname].d_id + local o_id = yl_speak_up.speak_to[pname].o_id + -- only o_id with a *lower* o_sort value are suitable (else evaluation would become + -- difficult and loops might be created) + local o_id_list = {} + local options = dialog.n_dialogs[ d_id ].d_options + if(options) then + local this_option = options[ o_id ] + if(not(this_option) or not(this_option.o_sort)) then + this_option = {o_sort = 0} + end + for k, v in pairs(options) do + if(k and v and v.o_sort and v.o_sort < this_option.o_sort) then + table.insert(o_id_list, minetest.formspec_escape(k)) + end + end + end + if(e) then + data.other_o_id = e[ "p_value" ] + data.fulfilled = e[ "p_fulfilled" ] + end + local nr = math.max(0, table.indexof(o_id_list, data.other_o_id)) + nr_fulfilled = 1 + if(data.fulfilled == "true") then + nr_fulfilled = 2 + elseif(data.fulfilled == "false") then + nr_fulfilled = 3 + end + if(nr == 0 or nr_fulfilled == 1) then + save_button = "" + end + return formspec.. + "label[0.2,3.3;Note: You can only select dialog options with a *lower* o_sort value ".. + "for this evaluation.]".. + "label[0.2,4.0;The preconditions of dialog option:]".. + "dropdown[6.0,3.7;3.0,0.6;select_other_o_id;-select-,".. + table.concat(o_id_list, ",")..";".. + tostring(nr + 1)..";]".. + "label[9.2,4.0;..shall be:]".. + "dropdown[11,3.7;2.0,0.6;select_fulfilled;-select-,true,false;".. + tostring(nr_fulfilled).."]".. + "tooltip[select_other_o_id;".. + "Sometimes you may need the same preconditions for more than\n".. + "one dialog option - or you may need one dialog option to be\n".. + "available exactly when another one is *not* available.\n".. + "This is what you can do here.]".. + "tooltip[select_fulfilled;".. + "If you select \"true\" here, then this precondition will be\n".. + "fulfilled when all the preconditions of the dialog option you\n".. + "selected here are true as well.\n".. + "If you select \"false\", this precondition will only be\n".. + "fulfilled if the other dialog option you selected here\n".. + "is not true.]".. + save_button +end + + -- end of formspecs for types of preconditions, actions and effects ---------------------------------------------------------------------------- diff --git a/fs_edit_preconditions.lua b/fs_edit_preconditions.lua index 5d02c8e..b721a29 100644 --- a/fs_edit_preconditions.lua +++ b/fs_edit_preconditions.lua @@ -41,6 +41,7 @@ local check_what = { "the inventory of the NPC", -- 6 "execute Lua code (requires npc_master priv)", -- 7 "Call custom functions that are supposed to be overridden by the server.", -- 8 + "The preconditions of another dialog option are fulfilled/not fulfilled.", -- 9 } -- how to store these as p_type in the precondition: @@ -48,7 +49,9 @@ local values_what = {"", "state", "block", "trade", "player_inv", "npc_inv", -- requires npc_master priv "function", -- custom function (does not require npc_master priv) - "custom"} + "custom", + -- depends on the preconditions of another option + "other"} -- options for "a trade" local check_trade = { @@ -216,18 +219,51 @@ yl_speak_up.show_precondition = function(p, pname) 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 +yl_speak_up.calculate_displayable_options = function(pname, d_options) + -- 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 + 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) + minetest.chat_send_player("singleplayer","retval "..tostring(o_k)..": "..tostring(retval[o_k])) -- TODO + 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) +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 @@ -239,7 +275,7 @@ yl_speak_up.eval_all_preconditions = function(player, prereq, o_id) 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))) then + 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 @@ -253,7 +289,7 @@ end -- checks if precondition p is true for the player and npc n_id -yl_speak_up.eval_precondition = function(player, n_id, p) +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 @@ -365,7 +401,9 @@ yl_speak_up.eval_precondition = function(player, n_id, p) -- 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(p.p_var_cmp_value == nil) 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 @@ -377,10 +415,10 @@ yl_speak_up.eval_precondition = function(player, n_id, p) 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 + 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) then + 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 @@ -463,6 +501,12 @@ yl_speak_up.eval_precondition = function(player, n_id, p) 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 diff --git a/functions.lua b/functions.lua index ccb05ca..0b77695 100644 --- a/functions.lua +++ b/functions.lua @@ -351,25 +351,6 @@ local function delete_option(n_id, d_id, o_id) 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 - - local retval = {} - - local player = minetest.get_player_by_name(pname) - - if d_options == nil then - return {} - end - - for o_k, o_v in pairs(d_options) do - -- Can we display this option? - 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 @@ -800,7 +781,8 @@ yl_speak_up.get_fs_talkdialog = function(player, n_id, d_id, alternate_text) return "size[6,2]".. "label[0.2,0.5;Ups! Something went wrong. Please try again.]" end - local allowed = calculate_displayable_options(pname, active_dialog.d_options) + -- evaluate the preconditions of each option and check if the option can be offered + local allowed = yl_speak_up.calculate_displayable_options(pname, active_dialog.d_options) yl_speak_up.speak_to[pname].allowed = allowed local portrait = calculate_portrait(pname, n_id)