From 0629d6ba2c370f3475fb4a93eebed80a31d2631a Mon Sep 17 00:00:00 2001 From: Sokomine Date: Wed, 4 Aug 2021 04:15:45 +0200 Subject: [PATCH] added new tabs 'Limit guessing:' and 'Limit repeating:' to edit options menu --- config.lua | 12 ++++ fs_edit_options_dialog.lua | 121 ++++++++++++++++++++++++++++++++++--- functions.lua | 38 ++++++++++++ quest_api.lua | 30 ++++++++- 4 files changed, 189 insertions(+), 12 deletions(-) diff --git a/config.lua b/config.lua index 6fe949d..fc80358 100644 --- a/config.lua +++ b/config.lua @@ -67,3 +67,15 @@ yl_speak_up.blacklist_action_quest_item = {} yl_speak_up.chat_all_prefix = minetest.colorize("#0000FF", "[NPC] ") -- the NPC will use this color when sending a chat message yl_speak_up.chat_all_color = "#AAAAFF" + + +-- it's possible to prevent players from trying actions (i.e. npc_gives, text_input, ..) too often; +-- if no special text is set, this one will be shown (tab "Limit guessing:" in edit options menu) +yl_speak_up.standard_text_if_action_failed_too_often = "You have tried so many times. I'm tired! ".. + "Come back when you know the answer - but not before tomorrow." +-- it's also possible to prevent players from successfully executing actions too often (after all the +-- quest items are created from the finite NPC's inventory); this is the standard text that will be +-- shown by default (tab "Limit repeating:" in edit options menu) +yl_speak_up.standard_text_if_action_repeated_too_soon = "I don't have infinite ressources. If you lost ".. + "something I gave you - come back tomorrow and we may talk again." + diff --git a/fs_edit_options_dialog.lua b/fs_edit_options_dialog.lua index ef1f862..541d38a 100644 --- a/fs_edit_options_dialog.lua +++ b/fs_edit_options_dialog.lua @@ -29,6 +29,16 @@ yl_speak_up.input_edit_option_dialog = function(player, formname, fields) {n_id = n_id, d_id = d_id, o_id = o_id, caller="show_if_action_succeeded"}) return + elseif(fields.switch_tab and fields.switch_tab == "3") then + yl_speak_up.show_fs(player, "edit_option_dialog", + {n_id = n_id, d_id = d_id, o_id = o_id, + caller="show_tab_limit_guessing"}) + return + elseif(fields.switch_tab and fields.switch_tab == "4") then + yl_speak_up.show_fs(player, "edit_option_dialog", + {n_id = n_id, d_id = d_id, o_id = o_id, + caller="show_tab_limit_repeating"}) + return end -- this menu is specific to an option for a dialog; if no dialog is selected, we really @@ -331,13 +341,15 @@ yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id, calle -- make all following coordinates relative local action_text = "container[0.2,13.0]".. "box[0.25,0.0;21.0,6.7;#555555]" + local tab_list = "tabheader[0.2,0.0;switch_tab;".. + "If the action was successful:,".. + "If the action failed:,".. + "Limit guessing:,".. + "Limit repeating:" -- show what happens if the action fails if(caller == "show_if_action_failed") then - action_text = action_text.. - -- allow to switch between successful and failed actions - "tabheader[0.2,0.0;switch_tab;".. - "If the action was successful:,".. - "If the action failed:;2;true;true]".. + -- allow to switch between successful and failed actions + action_text = action_text..tab_list..";2;true;true]".. "label[0.4,0.6;".. "If the player *failed* to complete the above action correctly,]" if(action_data and action_data.a_on_failure @@ -364,6 +376,97 @@ yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id, calle action_text = action_text.. "label[0.4,3.6;..go back to the initial dialog.]" end + -- show time-based restrictions (max guesses per time); + -- the values will be saved in function yl_speak_up.edit_mode_apply_changes + elseif( caller == "show_tab_limit_guessing") then + local timer_name = "timer_on_failure_"..tostring(d_id).."_"..tostring(o_id) + local timer_data = yl_speak_up.get_variable_metadata(timer_name, "parameter", true) + if(not(timer_data)) then + timer_data = {} + end + action_text = action_text..tab_list..";3;true;true]".. + -- allow to switch between successful and failed actions + "label[0.4,0.6;".. + "Apply the following time-based restrictions to limit wild guessing:]".. + -- timer for failed actions + "label[0.4,1.6;The player can make]".. + "field[4.9,1.0;1.5,0.9;timer_max_attempts_on_failure;;".. + tostring(timer_data[ "max_attempts" ] or 0).."]".. + "label[6.7,1.6;attempts to complete this action successfully each]".. + "field[17.5,1.0;1.5,0.9;timer_max_seconds_on_failure;;".. + tostring(timer_data[ "duration" ] or 0).."]".. + "label[19.2,1.6;seconds.]".. + "label[0.4,2.2;Hint: 3 attempts per 1200 seconds (=20 minutes or one MineTest day)".. + " may be good values to\navoid wild guessing while not making the player ".. + "having to wait too long to try again.]".. + "tooltip[timer_max_attempts_on_failure;How many tries shall the player have?".. + "\nA value of 0 disables this restriction.]".. + "tooltip[timer_max_seconds_on_failure;After which time can the player try again?".. + "\nA value of 0 disables this restriction.]".. + -- ..and what the NPC will explain in such a case + "tooltip[1.2,3.9;19.6,2.5;This is what the NPC will say next when ".. + "\nthe player has failed to complete the action too".. + "\nmany times for the NPC's patience and the player".. + "\nhas to wait some time before guessing again.]".. + "container[0.0,3.2]".. + "label[0.4,0.4;The NPC will explain his unwillingness to accept more ".. + "guesses ".. + yl_speak_up.show_colored_dialog_text( + dialog, + {alternate_text = (timer_data[ "alternate_text" ] + or yl_speak_up.standard_text_if_action_failed_too_often)}, + d_id, -- show the same dialog again + "1.2,0.7;19.6,2.5;d_text_next", + "with the following text", + ":]", + "button_edit_limit_action_failed_repeat").. + "container_end[]" + -- show time-based restrictions (time between repeating this action successfully) + elseif( caller == "show_tab_limit_repeating") then + local timer_name = "timer_on_success_"..tostring(d_id).."_"..tostring(o_id) + local timer_data = yl_speak_up.get_variable_metadata(timer_name, "parameter", true) + if(not(timer_data)) then + timer_data = {} + end + action_text = action_text..tab_list..";4;true;true]".. + "label[0.4,0.6;".. + "Apply the following time-based restrictions to limit too quick repeating:]".. + -- timer for successful actions + "label[0.4,1.6;If the player completed the action successfully, he shall have to".. + " wait]".. + "field[15.0,1.0;1.5,0.9;timer_max_seconds_on_success;;".. + tostring(timer_data[ "duration" ] or 0).."]".. + "label[16.7,1.6;seconds until he]".. + "label[0.4,2.1;can repeat the action. Hint: 1200 seconds (=20 minutes or one ".. + "MineTest day) may be a good value.]".. + "tooltip[timer_max_seconds_on_success;"..minetest.formspec_escape( + "If you hand out a quest item, you may not want the player".. + "\nto immediately repeat the action countless times, thus".. + "\nemptying the NPC's storage and using the quest item for".. + "\nother purposes. On the other hand, quest items may get ".. + "\nlost, so the player needs a way to repeat each step.".. + "\n1200 seconds may be a good value here as well.").."]".. + -- ..and what the NPC will explain in such a case + "tooltip[1.2,3.9;19.6,2.5;"..minetest.formspec_escape( + "This is what the NPC will say next when the player".. + "\nwants to repeat the action too soon for the NPC's".. + "\ntaste - after all the NPC does not have infinite ".. + "\ninventory ressources, and the player may abuse the ".. + "\nquest item for entirely diffrent purposes..").."]".. + "container[0.0,3.2]".. + -- this will lead back to the same dialog + "label[0.4,0.4;The NPC will explain his unwillingness to repeat the ".. + "action so soon ".. + yl_speak_up.show_colored_dialog_text( + dialog, + {alternate_text = (timer_data[ "alternate_text" ] + or yl_speak_up.standard_text_if_action_repeated_too_soon)}, + d_id, -- show the same dialog again + "1.2,0.7;19.6,2.5;d_text_next", + "with the following text", + ":]", + "button_edit_limit_action_success_repeat").. + "container_end[]" -- show what happens if the action was successful else -- no action defined @@ -373,11 +476,8 @@ yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id, calle "label[0.4,0.6;".. "There is no (A)ction defined. Directly apply the following (Ef)fects:]" else - action_text = action_text.. - -- allow to switch between successful and failed actions - "tabheader[0.2,0.0;switch_tab;".. - "If the action was successful:,".. - "If the action failed:;1;true;true]".. + -- allow to switch between successful and failed actions + action_text = action_text..tab_list..";1;true;true]".. "label[0.4,0.6;".. "If the player completed the above action successfully, ".. "apply the following (Ef)fects:]" @@ -418,6 +518,7 @@ yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id, calle "", "button_edit_action_success_dialog").. "container_end[]" + end action_text = action_text.."container_end[]" diff --git a/functions.lua b/functions.lua index a40a288..f6cad82 100644 --- a/functions.lua +++ b/functions.lua @@ -1423,6 +1423,44 @@ yl_speak_up.edit_mode_apply_changes = function(pname, fields) end end + -- how many times can the player fail to execute the action successfully? + if(fields[ "timer_max_attempts_on_failure"]) then + local field_name = "timer_max_attempts_on_failure" + local timer_name = "timer_on_failure_"..tostring(d_id).."_"..tostring(o_id) + if(not(tonumber(fields[ field_name ]))) then + fields[ field_name ] = 0 + end + -- make sure the variable exists + if(yl_speak_up.add_time_based_variable(timer_name)) then + yl_speak_up.set_variable_metadata(timer_name, nil, "parameter", "max_attempts", + fields[ field_name ]) + end + end + -- ..and how long has the player to wait in order to try again? + if(fields[ "timer_max_seconds_on_failure"]) then + local field_name = "timer_max_seconds_on_failure" + local timer_name = "timer_on_failure_"..tostring(d_id).."_"..tostring(o_id) + if(not(tonumber(fields[ field_name ]))) then + fields[ field_name ] = 0 + end + -- make sure the variable exists + if(yl_speak_up.add_time_based_variable(timer_name)) then + yl_speak_up.set_variable_metadata(timer_name, nil, "parameter", "duration", + fields[ field_name ]) + end + end + if(fields[ "timer_max_seconds_on_success"]) then + local field_name = "timer_max_seconds_on_success" + local timer_name = "timer_on_success_"..tostring(d_id).."_"..tostring(o_id) + if(not(tonumber(fields[ field_name ]))) then + fields[ field_name ] = 0 + end + -- make sure the variable exists + if(yl_speak_up.add_time_based_variable(timer_name)) then + yl_speak_up.set_variable_metadata(timer_name, nil, "parameter", "duration", + fields[ field_name ]) + end + end -- currently only contains result["show_new_option"] (which is needed for options edit menu) return result end diff --git a/quest_api.lua b/quest_api.lua index b3bd485..5bb85ad 100644 --- a/quest_api.lua +++ b/quest_api.lua @@ -70,6 +70,26 @@ yl_speak_up.add_quest_variable = function(owner_name, variable_name) end +-- time based variables are used for "Limit guessing:" and "Limit repeating:"; they ensure that +-- options with actions cannot be repeated indefintely; +-- returns false if the variable could not be created; else it returns the variable metadata +yl_speak_up.add_time_based_variable = function(variable_name) + if(not(yl_speak_up.player_vars[ variable_name ])) then + yl_speak_up.player_vars[ variable_name ] = {} + yl_speak_up.player_vars[ variable_name ][ "$META$" ] = {} + yl_speak_up.player_vars[ variable_name ][ "$META$"][ "var_type" ] = "time_based" + yl_speak_up.save_quest_variables(true) + return yl_speak_up.player_vars[ variable_name ][ "$META$"] + elseif(yl_speak_up.player_vars[ variable_name ] + and yl_speak_up.player_vars[ variable_name ][ "$META$"] + and type(yl_speak_up.player_vars[ variable_name ][ "$META$"]) == "table" + and yl_speak_up.player_vars[ variable_name ][ "$META$"][ "var_type" ] == "time_based") then + return yl_speak_up.player_vars[ variable_name ][ "$META$"] + end + return false +end + + -- accidentally created or no longer needed variables need to be deleted somehow -- force_delete if set, the variable will be deleted no matter what; this is for -- manual maintenance and not used in this mod @@ -305,7 +325,9 @@ end -- in general, meta_name is a table containing entries entry_name (usually players or npc_ids) -- with assigned values (usually true) for quick lookup yl_speak_up.set_variable_metadata = function(k, pname, meta_name, entry_name, new_value) - k = yl_speak_up.add_pname_to_var(k, pname) + if(pname) then + k = yl_speak_up.add_pname_to_var(k, pname) + end -- delete/unset if(not(new_value)) then new_value = nil @@ -451,7 +473,7 @@ end -- which NPC do use this variable? -yl_speak_up.get_variable_metadata = function(var_name, meta_name) +yl_speak_up.get_variable_metadata = function(var_name, meta_name, get_as_is) -- var_type (the type of the variable) is a single string if(meta_name and var_name and meta_name == "var_type") then if( not(yl_speak_up.player_vars[ var_name ]) @@ -469,6 +491,10 @@ yl_speak_up.get_variable_metadata = function(var_name, meta_name) or type(yl_speak_up.player_vars[ var_name ][ "$META$"][ meta_name ]) ~= "table") then return {} end + -- do not transform into a list; get the table + if(get_as_is) then + return yl_speak_up.player_vars[ var_name ][ "$META$"][ meta_name ] + end local meta_list = {} for k, v in pairs(yl_speak_up.player_vars[ var_name ][ "$META$"][ meta_name ]) do table.insert(meta_list, k)