quest api variables are now properly saved and restored

This commit is contained in:
Sokomine 2021-07-03 21:25:38 +02:00
parent 522179cf28
commit 5cf3e412c5
6 changed files with 137 additions and 43 deletions

View File

@ -9,6 +9,13 @@ yl_speak_up.path = "yl_speak_up_dialogs"
-- What shall we call the folder all the inventories of the NPC will reside in?
yl_speak_up.inventory_path = "yl_speak_up_inventories"
-- Where shall player-specific varialbes (usually quest states) be stored?
yl_speak_up.player_vars_save_file = "yl_speak_up_player_vars"
-- amount of time in seconds that has to have passed before the above file will be saved again
-- (more time can pass if no variable is changed)
yl_speak_up.player_vars_min_save_time = 60
-- Texts
yl_speak_up.message_button_option_exit = "Farewell!"

View File

@ -122,7 +122,7 @@ end
-- returns a human-readable text as description of the effects
-- (as shown in the edit options dialog and in the edit effect formspec)
yl_speak_up.show_effect = function(r)
yl_speak_up.show_effect = function(r, pname)
if(not(r.r_type) or r.r_type == "") then
return "(nothing): Nothing to do. No effect."
elseif(r.r_type == "give_item") then
@ -138,18 +138,22 @@ yl_speak_up.show_effect = function(r)
elseif(r.r_type == "dialog") then
return "Switch to dialog \""..tostring(r.r_value).."\"."
elseif(r.r_type == "state") then
local var_name = "VARIABLE[ - ? - ]"
if(r.r_variable) then
var_name = "VARIABLE[ "..tostring(
yl_speak_up.strip_pname_from_var(r.r_variable, pname)).." ]"
end
if(not(r.r_operator)) then
return "Error: Operator not defined."
elseif(r.r_operator == "set_to") then
return "set VARIABLE[ "..tostring(r.r_variable).." ] to value \""..
return "set "..var_name.." to value \""..
tostring(r.r_var_cmp_value).."\""
elseif(r.r_operator == "unset") then
return "discard VARIABLE[ "..tostring(r.r_variable).." ] (unset)"
return "discard "..var_name.." (unset)"
elseif(r.r_operator == "set_to_current_time") then
return "set VARIABLE[ "..tostring(r.r_variable).." ] to the current time"
return "set "..var_name.." to the current time"
else
return "ERROR: Wrong operator \""..tostring(r.r_operator).."\" for "..
"VARIABLE[ "..tostring(r.r_variable).." ]"
return "ERROR: Wrong operator \""..tostring(r.r_operator).."\" for "..var_name
end
elseif(r.r_type == "block") then
if(not(r.r_pos) or type(r.r_pos) ~= "table"
@ -251,7 +255,7 @@ yl_speak_up.execute_all_relevant_effects = function(player, effects, o_id, actio
for i, k in ipairs(sorted_key_list) do
local r = effects[ k ]
yl_speak_up.debug_msg(player, n_id, o_id, "..executing "..
tostring(r.r_id)..": "..yl_speak_up.show_effect(r))
tostring(r.r_id)..": "..yl_speak_up.show_effect(r, pname))
-- do not execute effects in edit mode
if(not(edit_mode)) then
yl_speak_up.debug_msg(player, n_id, o_id,

View File

@ -242,7 +242,7 @@ yl_speak_up.save_element_p_or_a_or_e = function(
v[ id_prefix.."value" ] = "expression"
v[ id_prefix.."operator" ] = values_operator[ data.operator ]
v[ id_prefix.."var_cmp_value" ] = (data.var_cmp_value or "")
v[ id_prefix.."variable" ] = data.variable_name
v[ id_prefix.."variable" ] = yl_speak_up.add_pname_to_var(data.variable_name, pname)
-- "a block somewhere", -- 3
elseif(data.what == 3 and id_prefix ~= "a_") then
@ -899,6 +899,7 @@ yl_speak_up.input_fs_edit_option_related = function(player, formname, fields,
-- 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_function(pname)
yl_speak_up.strip_pname_from_varlist(var_list, pname)
local nr = table.indexof(var_list, fields.select_variable)
if(nr) then
yl_speak_up.speak_to[pname][ tmp_data_cache ].variable = nr
@ -1125,7 +1126,7 @@ yl_speak_up.get_fs_edit_option_related = function(player, table_click_result,
minetest.formspec_escape(elements[ x_id ][ id_prefix.."type"])..
","..
minetest.formspec_escape(
show_element_function(elements[ x_id ]))..";0]"..
show_element_function(elements[ x_id ], pname))..";0]"..
"button[2.0,1.8;1.5,0.9;delete_element;Delete]"..
"button[4.0,1.8;1.5,0.9;change_element;Change]"..
"button[6.0,1.8;5.5,0.9;back;Back to edit dialog option \""..
@ -1327,11 +1328,12 @@ yl_speak_up.get_fs_edit_option_p_and_e_state = function(
if(e) then
data.operator = math.max(1,table.indexof(values_operator, e[ id_prefix.."operator" ]))
data.var_cmp_value = e[ id_prefix.."var_cmp_value" ]
data.variable_name = e[ id_prefix.."variable" ]
data.variable = math.max(1, table.indexof(var_list, e[ id_prefix.."variable"])+1)
data.variable_name = yl_speak_up.strip_pname_from_var(e[ id_prefix.."variable" ], pname)
data.variable = table.indexof(var_list, e[ id_prefix.."variable"])
end
local var_list_stripped = yl_speak_up.strip_pname_from_varlist(var_list, pname)
if(not(data.variable) or data.variable < 1) then
data.variable = 1
data.variable = 0
-- not enough selected yet for saving
save_button = ""
elseif(not(data.operator) or data.operator == 1) then
@ -1351,22 +1353,12 @@ yl_speak_up.get_fs_edit_option_p_and_e_state = function(
-- 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_text = "- please select -"
for i, v in ipairs(var_list) do
local parts = string.split(v, " ")
local var_name = v
if(parts and parts[1] and parts[1] == pname) then
table.remove(parts, 1)
var_name = table.concat(parts, " ")
end
var_list_text = var_list_text..","..minetest.formspec_escape(tostring(var_name))
end
return formspec..
"label[0.2,3.3;"..text_variable.."]"..
"label[0.2,4.3;Name of variable:]"..
"dropdown[0.2,4.8;6.5,0.6;select_variable;"..
var_list_text..";"..
tostring(data.variable)..";]"..
"- please select -"..var_list_stripped..";"..
tostring(data.variable + 1)..";]"..
"label[7.0,4.3;"..text_select_operator.."]"..
"dropdown[7.0,4.8;4.0,0.6;select_operator;"..
table.concat(check_operator, ",")..";"..

View File

@ -236,7 +236,7 @@ yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id, calle
minetest.formspec_escape(v.p_id)..",#FFFF00,"..
minetest.formspec_escape(v.p_type)..","..
minetest.formspec_escape(
yl_speak_up.show_precondition(v))..","
yl_speak_up.show_precondition(v, pname))..","
count_prereq = count_prereq + 1
end
end
@ -299,7 +299,7 @@ yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id, calle
minetest.formspec_escape(v.r_id)..",#999999,"..
minetest.formspec_escape(v.r_type)..","..
minetest.formspec_escape(
yl_speak_up.show_effect(v))..","
yl_speak_up.show_effect(v, pname))..","
-- there may be more than one in the data structure
target_dialog = v.r_value
target_effect = v
@ -308,7 +308,7 @@ yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id, calle
minetest.formspec_escape(v.r_id)..",#FFFF00,"..
minetest.formspec_escape(v.r_type)..","..
minetest.formspec_escape(
yl_speak_up.show_effect(v))..","
yl_speak_up.show_effect(v, pname))..","
end
count_effects = count_effects + 1
end

View File

@ -135,7 +135,7 @@ 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)
yl_speak_up.show_precondition = function(p, pname)
if(not(p.p_type) or p.p_type == "") then
return "(nothing): Always true."
elseif(p.p_type == "item") then
@ -147,25 +147,30 @@ yl_speak_up.show_precondition = function(p)
elseif(p.p_type == "function") then
return "function: evaluate "..tostring(p.p_value)
elseif(p.p_type == "state") then
local var_name = "VALUE_OF[ - ? - ]"
if(p.p_variable) then
var_name = "VALUE_OF[ "..tostring(
yl_speak_up.strip_pname_from_var(p.p_variable, pname)).." ]"
end
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).." ] )"
return "not( "..var_name.." )"
elseif(p.p_operator == "is_set") then
return "VALUE_OF[ "..tostring(p.p_variable).." ] ~= nil (is_set)"
return var_name.." ~= nil (is_set)"
elseif(p.p_operator == "is_unset") then
return "VALUE_OF[ "..tostring(p.p_variable).." ] == nil (is_unset)"
return var_name.." == nil (is_unset)"
elseif(p.p_operator == "more_than_x_seconds_ago") then
return "VALUE_OF[ "..tostring(p.p_variable).." ] was set to current time "..
return var_name.." was set to current time "..
"*more* than "..tostring(p.p_var_cmp_value).." seconds ago"
elseif(p.p_operator == "less_than_x_seconds_ago") then
return "VALUE_OF[ "..tostring(p.p_variable).." ] was set to current time "..
return var_name.." was set to current time "..
"*less* than "..tostring(p.p_var_cmp_value).." seconds ago"
end
if(p.p_var_cmp_value == "") then
return "VALUE_OF[ "..tostring(p.p_variable).." ] "..tostring(p.p_operator).." \"\""
return var_name.." "..tostring(p.p_operator).." \"\""
end
return "VALUE_OF[ "..tostring(p.p_variable).." ] "..tostring(p.p_operator).." "..
return var_name.." "..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"
@ -224,7 +229,7 @@ yl_speak_up.eval_all_preconditions = function(player, prereq, o_id)
yl_speak_up.debug_msg(player, n_id, o_id, "Checking preconditions..")
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))
tostring(p.p_id)..": "..yl_speak_up.show_precondition(p, pname))
if(not(yl_speak_up.eval_precondition(player, n_id, p))) then
yl_speak_up.debug_msg(player, n_id, o_id, tostring(p.p_id)..
" -> is false. Aborting.")

View File

@ -4,34 +4,79 @@
-- TODO: handle read (and write?) access for other players
-- TODO: add a function to check if the player has read/write access
-- TODO: mark some vars as "need to be saved" while others are less important (i.e. timestamps)
-- TODO: save state whenever a new variable is added - or else if the last change was more than x minutes ago?
-- the keys are of the form:
-- <player name> <blank> <variable name> (makes it easier to grant read access)
-- $ <blank> <player name> <blank> <variable name> (makes it easier to grant read access)
-- the values are of the form:
-- <current player name as key> : <value of variable for that player as value>
yl_speak_up.player_vars = {}
-- store when player_vars was last saved to disc
yl_speak_up.player_vars_last_save_time = 0
-- save the data to disc; either if force_save is set or enough time has passed
yl_speak_up.save_quest_variables = function(force_save)
if(not(force_save)
and (yl_speak_up.player_vars_last_save_time + yl_speak_up.player_vars_min_save_time >
math.floor(minetest.get_us_time()/1000000))) then
return
end
local json = minetest.write_json( yl_speak_up.player_vars )
-- actually store it on disk
minetest.safe_file_write(yl_speak_up.worldpath..yl_speak_up.player_vars_save_file..".json", json)
end
-- load the data from disc
yl_speak_up.load_quest_variables = function()
-- load the data from the file
local file, err = io.open(yl_speak_up.worldpath..yl_speak_up.player_vars_save_file..".json", "r")
if err then
return
end
io.input(file)
local text = io.read()
-- all values saved in the tables as such are strings
local data = minetest.parse_json(text, -1)
io.close(file)
if(type(data) ~= "table") then
return
end
for k,v in pairs(data) do
if(v == -1) then
data[ k ] = {}
end
end
yl_speak_up.player_vars = data
end
-- do so when this file is parsed
yl_speak_up.load_quest_variables()
-- new variables have to be added somehow
yl_speak_up.add_quest_variable = function(owner_name, variable_name)
local k = tostring(owner_name).." "..tostring(variable_name)
local k = "$ "..tostring(owner_name).." "..tostring(variable_name)
if(not(owner_name) or not(variable_name)) then
return false
end
-- create a new empty table;
-- keys will be the names of players for which values are set
yl_speak_up.player_vars[ k ] = {}
-- a new variable was created - that deserves a forced save
yl_speak_up.save_quest_variables(true)
return true
end
-- accidentally created or no longer needed variables need to be deleted somehow
yl_speak_up.del_quest_variable = function(owner_name, variable_name)
local k = tostring(owner_name).." "..tostring(variable_name)
local k = "$ "..tostring(owner_name).." "..tostring(variable_name)
if(not(owner_name) or not(variable_name)) then
return false
end
-- a variable was deleted - that deserves a forced save
yl_speak_up.save_quest_variables(true)
yl_speak_up.player_vars[ k ] = nil
end
@ -44,7 +89,9 @@ yl_speak_up.set_quest_variable_value = function(player_name, variable_name, new_
if(not(variable_name) or not(player_name) or not(yl_speak_up.player_vars[ k ])) then
return false
end
yl_speak_up.player_vars[ k ][ player_name ] = new_value
yl_speak_up.player_vars[ k ][ player_name ] = tostring(new_value)
-- a quest variable was changed - save that to disc (but no need to force it)
yl_speak_up.save_quest_variables(false)
return true
end
@ -70,7 +117,7 @@ yl_speak_up.get_quest_variables_with_read_access = function(pname)
local liste = {}
for k, v in pairs(yl_speak_up.player_vars) do
local parts = string.split(k, " ")
if(parts and parts[1] and parts[1] == pname) then
if(parts and parts[1] and parts[1] == "$" and parts[2] and parts[2] == pname) then
table.insert(liste, k)
end
end
@ -88,7 +135,7 @@ yl_speak_up.get_quest_variables_with_write_access = function(pname)
local liste = {}
for k, v in pairs(yl_speak_up.player_vars) do
local parts = string.split(k, " ")
if(parts and parts[1] and parts[1] == pname) then
if(parts and parts[1] and parts[1] == "$" and parts[2] and parts[2] == pname) then
table.insert(liste, k)
end
end
@ -151,3 +198,42 @@ yl_speak_up.get_fs_manage_variables = function(player, param)
-- TODO: delete variable
"button[2.0,3.5;1.0,0.6;back;Back]"
end
-- variables are personalized; they are prefixed by "$ <PLAYER_NAME> <VAR_NAME>"
-- helper function;
-- strip "$ PNAME " from variable names (but only for those owned by player with name pname)
yl_speak_up.strip_pname_from_var = function(var_name, pname)
local parts = string.split(var_name, " ")
if(parts and parts[1] and parts[1] == "$" and parts[2] and parts[2] == pname) then
table.remove(parts, 1) -- remove "$"
table.remove(parts, 1) -- remove pname
return table.concat(parts, " ")
end
return var_name
end
-- does the opposite of the function above; adds "$ PNAME " if needed
yl_speak_up.add_pname_to_var = function(var_name, pname)
local parts = string.split(var_name, " ")
if(parts and parts[1] and parts[1] ~= "$") then
return "$ "..tostring(pname).." "..tostring(var_name)
end
return var_name
end
-- helper function for yl_speak_up.input_fs_edit_option_related
-- and yl_speak_up.get_fs_edit_option_p_and_e_state
yl_speak_up.strip_pname_from_varlist = function(var_list, pname)
local var_list_text = ""
-- strip pname from the variable names
for i, v in ipairs(var_list) do
var_list[i] = yl_speak_up.strip_pname_from_var(v, pname)
-- useful for presenting a list
var_list_text = var_list_text..","..minetest.formspec_escape(tostring(var_list[i]))
end
return var_list_text
end