allowed to refer to other options' preconditions as a new precondition

This commit is contained in:
Sokomine 2021-07-12 00:22:49 +02:00
parent 586a18073e
commit bcc171dff2
3 changed files with 143 additions and 27 deletions

View File

@ -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
----------------------------------------------------------------------------

View File

@ -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

View File

@ -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)