yl_speak_up/quest_api.lua
2021-07-31 19:01:09 +02:00

945 lines
35 KiB
Lua

-- just some handling of variables
-- 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)
-- the keys are of the form:
-- $ <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
if(not(yl_speak_up.player_vars.meta)) then
yl_speak_up.player_vars["meta"] = {}
end
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)
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
-- force_delete if set, the variable will be deleted no matter what; this is for
-- manual maintenance and not used in this mod
yl_speak_up.del_quest_variable = function(owner_name, variable_name, force_delete)
if(not(owner_name) or not(variable_name)) then
return " could not be deleted. Parameters mismatch."
end
local var_name = yl_speak_up.restore_complete_var_name(variable_name, owner_name)
if(not(var_name) or not(yl_speak_up.player_vars[ var_name ])) then
return text.." does not exist."
end
local text = "Variable \""..minetest.formspec_escape(var_name).."\""
-- forcefully delete - even if the variable is still beeing used
if(force_delete) then
yl_speak_up.player_vars[ k ] = nil
yl_speak_up.save_quest_variables(true)
return text.." deleted by force."
end
-- check if the player really owns the variable: not that important because only unused
-- variables can be deleted;
-- check if the variable is used by an NPC
local var_data = yl_speak_up.player_vars[ var_name ]
if(var_data["$META$"] and var_data["$META$"]["used_by_npc" ]
and type(var_data["$META$"]["used_by_npc" ]) == "table") then
for k, v in pairs(var_data["$META$"]["used_by_npc" ]) do
if(k) then
return text.." could not be deleted.\nIt is used by at least one NPC."
end
end
end
-- check if the variable is used by a node position (for quests)
if(var_data["$META$"] and var_data["$META$"]["used_by_node_pos" ]
and type(var_data["$META$"]["used_by_node_pos" ]) == "table") then
for k, v in pairs(var_data["$META$"]["used_by_node_pos" ]) do
if(k) then
return text.." could not be deleted.\nIt is used by at least one node pos."
end
end
end
-- check if the variable has any values stored
for k, v in pairs(var_data) do
if(k and k ~= "$META$") then
return text.." could not be deleted.\nIt contains at least one stored value."
end
end
-- actually delete the variable
yl_speak_up.player_vars[ var_name ] = nil
-- a variable was deleted - that deserves a forced save
yl_speak_up.save_quest_variables(true)
return text.." deleted successfully."
end
-- set the value of a variable used by a player in an NPC;
-- returns false if the variable cannot be set (i.e. does not exist)
yl_speak_up.set_quest_variable_value = function(player_name, variable_name, new_value)
-- the owner name is alrady encoded in the variable name
local k = tostring(variable_name)
if(not(variable_name) or not(player_name) or not(yl_speak_up.player_vars[ k ])) then
return false
end
if(new_value ~= nil) then
new_value = tostring(new_value)
end
yl_speak_up.player_vars[ k ][ player_name ] = 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
-- get the value of a variable used by a player in an NPC;
-- returns nil if the variable does not exist
yl_speak_up.get_quest_variable_value = function(player_name, variable_name)
-- the owner name is alrady encoded in the variable name
local k = tostring(variable_name)
if(not(variable_name) or not(player_name) or not(yl_speak_up.player_vars[ k ])) then
return nil
end
return yl_speak_up.player_vars[ k ][ player_name ]
end
yl_speak_up.get_quest_variables = function(pname, has_write_access)
if(not(pname)) then
return {}
end
local liste = {}
-- first: list the variables owned by the player
for k, v in pairs(yl_speak_up.player_vars) do
local parts = string.split(k, " ")
if(parts and parts[1] and parts[1] == "$" and parts[2] and parts[2] == pname) then
table.insert(liste, k)
end
end
-- if the player has the right privs: allow to access all other variables as well
if( minetest.check_player_privs(pname, {npc_master=true})
or minetest.check_player_privs(pname, {npc_talk_master=true})) then
for k, v in pairs(yl_speak_up.player_vars) do
local parts = string.split(k, " ")
-- variables owned by *other* players
if(parts and parts[1] and parts[1] == "$" and parts[2] and parts[2] ~= pname) then
table.insert(liste, k)
end
end
else
local right = "read_access"
if(has_write_access) then
right = "write_access"
end
-- insert those vars owned by other players where this one has access
for k, v in pairs(yl_speak_up.player_vars) do
if( k[ "$META$"]
and k[ "$META$"][ right ]
and k[ "$META$"][ right ][ pname ]) then
table.insert(liste, k)
end
end
end
table.sort(liste)
return liste
end
-- which variables can player pname read and use in preconditions?
-- returns a sorted list
yl_speak_up.get_quest_variables_with_read_access = function(pname)
return yl_speak_up.get_quest_variables(pname, false)
end
-- which variables can player pname write and use in effects/results?
yl_speak_up.get_quest_variables_with_write_access = function(pname)
return yl_speak_up.get_quest_variables(pname, true)
end
yl_speak_up.input_fs_manage_variables = function(player, formname, fields)
local pname = player:get_player_name()
if(fields and fields.back_from_msg) then
yl_speak_up.show_fs(player, "manage_variables")
return
end
-- leave this formspec
if(fields and (fields.quit or fields.exit or fields.back)) then
local last_fs = yl_speak_up.speak_to[pname][ "working_at" ]
yl_speak_up.show_fs(player, last_fs)
return
-- add a new variable?
elseif(fields and fields.add_variable) then
local error_msg = ""
if(not(fields.add_variable_name) or fields.add_variable_name == ""
or fields.add_variable_name:trim() == "") then
error_msg = "Please enter the name of your variable!"
-- limit names to something more sensible
elseif(string.len(fields.add_variable_name) > 30) then
error_msg = "The name of your variable name is too long.\n"..
"Only up to 30 characters are allowed."
elseif(string.len(fields.add_variable_name:trim()) < 2) then
error_msg = "The name of your variable name is too short.\n"..
"It has to be at least 2 characters long."
else
fields.add_variable_name = fields.add_variable_name:trim()
local res = yl_speak_up.add_quest_variable(pname, fields.add_variable_name)
-- not really an error msg here - but fascilitates output
error_msg = "A new variable named\n \""..
minetest.formspec_escape(fields.add_variable_name)..
"\"\nhas been created."
if(not(res)) then
error_msg = "Failed to create variable named\n \""..
minetest.formspec_escape(fields.add_variable_name).."\"."
else
-- slect this new variable
local var_list = yl_speak_up.get_quest_variables(pname, true)
-- make names of own variables shorter
yl_speak_up.strip_pname_from_varlist(var_list, pname)
table.sort(var_list)
local index = table.indexof(var_list, fields.add_variable_name)
if(index and index > -1) then
yl_speak_up.speak_to[pname].tmp_index_variable = index + 1
end
end
end
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:manage_variables",
formspec = "size[6,2]"..
"label[0.2,0.0;"..error_msg.."]"..
"button[1.5,1.5;2,0.9;back_from_msg;Back]"})
return
-- show where this variable is used
elseif(fields and fields.show_var_usage and fields.show_var_usage ~= "") then
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:manage_variables",
formspec = yl_speak_up.get_list_of_usage_of_variable(
fields.list_var_names, pname, true,
"back_from_msg",
"Back to manage variables",
-- not an internal variable
false)
})
return
end
-- which variable is currently selected?
local var_list = yl_speak_up.get_quest_variables(pname, true)
-- make names of own variables shorter
yl_speak_up.strip_pname_from_varlist(var_list, pname)
table.sort(var_list)
-- a var name was selected in the dropdown list
if(fields and fields.list_var_names and fields.list_var_names ~= "") then
local index = table.indexof(var_list, fields.list_var_names)
if(fields.list_var_names == "Add variable:") then
index = 0
end
if(index and index > -1) then
yl_speak_up.speak_to[pname].tmp_index_variable = index + 1
end
end
local var_name = var_list[ yl_speak_up.speak_to[pname].tmp_index_variable - 1]
if(not(var_name)) then
var_name = ""
end
-- delete (empty) variable
if(fields
and ((fields.delete_variable and fields.delete_variable ~= "")
or (fields.delete_unused_variable and fields.delete_unused_variable ~= ""))
and var_name) then
local text = yl_speak_up.del_quest_variable(pname, var_name, nil)
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:manage_variables",
formspec = "size[10,2]"..
"label[0.2,0.0;Trying to delete variable \""..
minetest.formspec_escape(tostring(var_name))..
"\":\n"..text.."]"..
"button[1.5,1.5;2,0.9;back_from_msg;Back]"})
return
-- revoke read or write access to a variable
elseif(fields
and ((fields.revoke_player_var_read_access and fields.revoke_player_var_read_access ~= "")
or (fields.revoke_player_var_write_access and fields.revoke_player_var_write_access ~= ""))
and var_name) then
local right = "read"
if(fields.revoke_player_var_write_access and fields.revoke_player_var_write_access ~= "") then
right = "write"
end
-- which player are we talking about?
local selected = yl_speak_up.speak_to[pname]["tmp_index_var_"..right.."_access"]
local pl_with_access = yl_speak_up.get_access_list_for_var(var_name, pname, right.."_access")
local tmp_list = {}
for k, v in pairs(pl_with_access) do
table.insert(tmp_list, k)
end
table.sort(tmp_list)
local grant_to = ""
if(selected > 1) then
grant_to = tmp_list[ selected-1 ]
end
local error_msg = ""
local pl_with_access = yl_speak_up.get_access_list_for_var(var_name, pname, right.."_access")
if(not(grant_to) or grant_to == "") then
error_msg = "For which player do you want to revoke "..right.." access?"
elseif(pname ~= yl_speak_up.npc_owner[ n_id ]
and not(minetest.check_player_privs(pname, {npc_talk_master=true}))) then
error_msg = "Only the owner of the NPC or players with\n"..
"the npc_talk_master priv can change this."
elseif(not(pl_with_access[ grant_to ])) then
error_msg = minetest.formspec_escape(grant_to).." doesn't have "..right..
" access\nto this variable. Nothing changed."
-- try to revoke access
elseif(not(yl_speak_up.manage_access_to_quest_variable(var_name, pname, grant_to,
right.."_access", nil))) then
error_msg = "An internal error occoured."
else
-- not really an error message here...rather a success message
error_msg = "Revoked "..right.." access to variable\n\""..
minetest.formspec_escape(var_name)..
"\"\nfor player "..minetest.formspec_escape(grant_to)..".\n"..
"Note: This will *not* affect existing preconditions/effects!"
end
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:manage_variables",
formspec = "size[6,2]"..
"label[0.2,0.0;"..error_msg.."]"..
"button[1.5,1.5;2,0.9;back_from_msg;Back]"})
return
-- grant read or write access to a variable
elseif(fields
and ((fields.grant_player_var_read_access and fields.grant_player_var_read_access ~= "")
or (fields.grant_player_var_write_access and fields.grant_player_var_write_access ~= ""))
and var_name) then
local right = "read"
if(fields.grant_player_var_write_access and fields.grant_player_var_write_access ~= "") then
right = "write"
end
local grant_to = fields[ "grant_player_var_"..right.."_access"]
local error_msg = ""
local pl_with_access = yl_speak_up.get_access_list_for_var(var_name, pname, right.."_access")
if(pname ~= yl_speak_up.npc_owner[ n_id ]
and not(minetest.check_player_privs(pname, {npc_talk_master=true}))) then
error_msg = "Only the owner of the NPC or players with\n"..
"the npc_talk_master priv can change this."
elseif(grant_to == pname) then
error_msg = "You already have "..right.." access to this variable."
elseif(pl_with_access[ grant_to ]) then
error_msg = minetest.formspec_escape(grant_to).." already has "..right..
" access\nto this variable."
elseif(not(minetest.check_player_privs(grant_to, {interact=true}))) then
error_msg = "Player \""..minetest.formspec_escape(grant_to).."\" not found."
-- try to grant access
elseif(not(yl_speak_up.manage_access_to_quest_variable(var_name, pname, grant_to,
right.."_access", true))) then
error_msg = "An internal error occoured."
else
-- not really an error message here...rather a success message
error_msg = "Granted "..right.." access to variable\n\""..
minetest.formspec_escape(var_name)..
"\"\nto player "..minetest.formspec_escape(grant_to).."."
end
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:manage_variables",
formspec = "size[6,2]"..
"label[0.2,0.0;"..error_msg.."]"..
"button[1.5,1.5;2,0.9;back_from_msg;Back]"})
return
-- the player clicked on a name in the list of players with read or write access
elseif(fields
and ((fields.list_var_read_access and fields.list_var_read_access ~= "")
or (fields.list_var_write_access and fields.list_var_write_access ~= ""))
and var_name) then
local right = "read"
if(fields.list_var_write_access and fields.list_var_write_access ~= "") then
right = "write"
end
local selected = fields[ "list_var_"..right.."_access"]
local pl_with_access = yl_speak_up.get_access_list_for_var(var_name, pname, right.."_access")
local tmp_list = {}
for k, v in pairs(pl_with_access) do
table.insert(tmp_list, k)
end
table.sort(tmp_list)
local index = table.indexof(tmp_list, selected)
if(selected == "Add player:") then
index = 0
end
if(index and index > -1) then
yl_speak_up.speak_to[pname]["tmp_index_var_"..right.."_access"] = index + 1
end
yl_speak_up.show_fs(player, "manage_variables")
return
-- a var name was selected in the dropdown list
elseif(fields and fields.list_var_names and fields.list_var_names ~= "") then
-- show the same formspec again, with a diffrent variable selected
yl_speak_up.show_fs(player, "manage_variables")
return
end
-- try to go back to the last formspec shown before this one
if(not(yl_speak_up.speak_to[pname])) then
return
end
local last_fs = yl_speak_up.speak_to[pname][ "working_at" ]
yl_speak_up.show_fs(player, last_fs)
end
yl_speak_up.get_fs_manage_variables = function(player, param)
local pname = player:get_player_name()
-- variables owned by the player - including those with write access
local var_list = yl_speak_up.get_quest_variables(pname, true)
-- make names of own variables shorter
yl_speak_up.strip_pname_from_varlist(var_list, pname)
-- the yl_speak_up.create_dropdown_playerlist function needs a table - not a list
local table_of_vars = {}
for i, k in ipairs(var_list) do
table_of_vars[ k ] = true
end
-- "Add variable:" is currently selected
local additional_buttons = ""
if(not(yl_speak_up.speak_to[pname].tmp_index_variable)
or yl_speak_up.speak_to[pname].tmp_index_variable == 1
or not(var_list[ yl_speak_up.speak_to[pname].tmp_index_variable - 1])) then
yl_speak_up.speak_to[pname].tmp_index_variable = 1
additional_buttons = "button[11.4,1.9;2.5,0.9;add_variable;Create variable]"..
"tooltip[add_variable;Create a new varialbe with the name\n"..
"you entered in the field to the left.]"
else
-- index 1 is "Add variable:"
local k = var_list[ yl_speak_up.speak_to[pname].tmp_index_variable - 1]
local pl_with_read_access = yl_speak_up.get_access_list_for_var(k, pname, "read_access")
local pl_with_write_access = yl_speak_up.get_access_list_for_var(k, pname, "write_access")
local add_read_button = ""
local add_write_button = ""
if(not(yl_speak_up.speak_to[pname].tmp_index_var_read_access)
or yl_speak_up.speak_to[pname].tmp_index_var_read_access == 1) then
yl_speak_up.speak_to[pname].tmp_index_var_read_access = 1
add_read_button = "button[12.9,2.9;1.0,0.9;add_read_access;Add]"..
"tooltip[add_read_access;Grant the player whose name you entered\n"..
"you entered in the field to the left read access\n"..
"to your variable.]"
end
if(not(yl_speak_up.speak_to[pname].tmp_index_var_write_access)
or yl_speak_up.speak_to[pname].tmp_index_var_write_access == 1) then
yl_speak_up.speak_to[pname].tmp_index_var_write_access = 1
add_write_button = "button[12.9,3.9;1.0,0.9;add_write_access;Add]"..
"tooltip[add_write_access;Grant the player whose name you entered\n"..
"you entered in the field to the left *write* access\n"..
"to your variable.]"
end
local list_of_npc_users = ""
local list_of_node_pos_users = ""
-- expand name of variable k again
local k_long = yl_speak_up.add_pname_to_var(k, pname)
-- which npc and which node_pos use this variable? create a list for display
local c1 = 0
local c2 = 0
if(k_long
and yl_speak_up.player_vars[ k_long ]
and yl_speak_up.player_vars[ k_long ][ "$META$"]) then
local t = yl_speak_up.player_vars[ k_long ][ "$META$"]["used_by_npc" ]
if(type(t) ~= "table") then
t = {}
end
for key, v in pairs(t or {}) do
if(c1==0) then
list_of_npc_users = minetest.formspec_escape(key)
else
list_of_npc_users = list_of_npc_users..", "..
minetest.formspec_escape(key)
end
c1 = c1 + 1
end
local t = yl_speak_up.player_vars[ k_long ][ "$META$"]["used_by_node_pos" ]
if(type(t) ~= "table") then
t = {}
end
for key, v in pairs(t or {}) do
if(c2==0) then
list_of_node_pos_users = minetest.formspec_escape(key)
else
list_of_node_pos_users = list_of_node_pos_users..", "..
minetest.formspec_escape(key)
end
c2 = c2 + 1
end
end
if(list_of_npc_users == "") then
list_of_npc_users = "- none -"
end
if(list_of_node_pos_users == "") then
list_of_node_pos_users = "- none -"
end
additional_buttons = "button[11.4,1.9;2.5,0.9;show_var_usage;Where is it used?]"..
"tooltip[show_var_usage;Show which NPC use this variable in which context.]"..
-- offer a dropdown list and a text input field for new varialbe names for adding
"label[0.2,3.05;Players with read access to this variable:]"..
yl_speak_up.create_dropdown_playerlist(player, pname,
pl_with_read_access,
yl_speak_up.speak_to[pname].tmp_index_var_read_access,
5.5, 2.9, 0.0, "list_var_read_access", "player", "Remove player from list",
"grant_player_var_read_access",
"Enter the name of the player that shall\n"..
"have read access to this variable.",
"revoke_player_var_read_access",
"If you click here, the selected player\n"..
"will no longer be able to add new\n"..
"pre(C)onditions which read your variable."
)..add_read_button..
"label[0.2,4.05;Players with *write* access to this variable:]"..
yl_speak_up.create_dropdown_playerlist(player, pname,
pl_with_write_access,
yl_speak_up.speak_to[pname].tmp_index_var_write_access,
5.5, 3.9, 0.0, "list_var_write_access", "player", "Remove player from list",
"grant_player_var_write_access",
"Enter the name of the player that shall\n"..
"have *write* access to this variable.",
"revoke_player_var_write_access",
"If you click here, the selected player\n"..
"will no longer be able to *write* new\n"..
"values into this variable."
)..add_write_button..
"label[0.2,5.05;Type of variable: "..
-- TODO: show actual type
minetest.colorize("#FFFF00","String/text or numerical value, depending "..
"on how you use it")..".]"..
"label[0.2,6.05;This variable is used by the following "..
minetest.colorize("#FFFF00", tostring(c1)).." NPC:\n\t"..
-- those are only n_id - no need to formspec_escape that
minetest.colorize("#FFFF00", list_of_npc_users)..".]"..
"label[0.2,7.05;This variable is used by the following "..
minetest.colorize("#FFFF00", tostring(c2)).." node positions:\n\t"..
-- those are only pos_to_string(..) - no need to formspec_escape that
minetest.colorize("#FFFF00", list_of_node_pos_users)..".]"
end
return "size[16,8.5]"..
"label[5.0,0.0;* Manage your variables *]"..
"label[0.2,0.8;Note: Each variable will store a diffrent value for each player who "..
"interacts with the NPC.\n"..
"You can grant read and write access to other players for your "..
"variables so that they can also use them as well.]"..
"label[0.2,2.05;Your variables:]"..
-- offer a dropdown list and a text input field for new varialbe names for adding
yl_speak_up.create_dropdown_playerlist(player, pname,
table_of_vars, yl_speak_up.speak_to[pname].tmp_index_variable,
2.2, 1.9, 1.0, "list_var_names", "variable", "Delete selected variable",
"add_variable_name",
"Enter the name of the new variable you\n"..
"want to create.",
"delete_variable",
"If you click here, the selected variable\n"..
"will be deleted."
)..
additional_buttons..
"button[0.0,0.2;1.0,0.6;back;Back]"..
"button[6.0,8.0;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)
if(not(var_name)) then
return ""
end
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
-- (partly) the opposite of the function above - add the name of the player to a variable
-- name again if needed
yl_speak_up.restore_complete_var_name = function(var_name, pname)
local vparts = string.split(var_name or "", " ")
-- has the player name been stripped from the variable name for better readability?
if(vparts and #vparts > 0 and vparts[1] ~= "$") then
return "$ "..tostring(pname).." "..table.concat(vparts, " ")
end
return var_name
end
-- helper function for saving NPC data;
-- this only works if *someone* is currently talking to that NPC
yl_speak_up.get_pname_for_n_id = function(n_id)
for k, v in pairs(yl_speak_up.speak_to) do
if(v and v.n_id and v.n_id == n_id) then
return k
end
end
end
-- add or revoke read or write access to a variable
--
-- k: name of the variable
-- pname: the name of the player trying to grant or revoke the right
-- grant_to_pname: the name of the player who shall have that access right
-- grant_write_access:
-- if false: grant read access
-- if true: grant write access
-- do_grant:
-- if false: revoke acces
-- if true: grant access
-- returns true if the variable was found
yl_speak_up.manage_access_to_quest_variable = function(k, pname, grant_to_pname, what_to_grant, do_grant)
k = yl_speak_up.add_pname_to_var(k, pname)
-- grant or revoke priv
if(not(do_grant)) then
do_grant = nil
end
-- only read and write access can be granted
if(not(what_to_grant) or (what_to_grant ~= "read_access" and what_to_grant ~= "write_access")) then
return false
end
-- the variable needs to exist
if(not(yl_speak_up.player_vars[ k ])) then
return false
end
-- make sure all the necessary tables exist
if(not(yl_speak_up.player_vars[ k ][ "$META$" ])) then
yl_speak_up.player_vars[ k ][ "$META$"] = { what_to_grant = {} }
end
if(not(yl_speak_up.player_vars[ k ][ "$META$" ][ what_to_grant ])) then
yl_speak_up.player_vars[ k ][ "$META$" ][ what_to_grant ] = {}
end
yl_speak_up.player_vars[ k ][ "$META$"][ what_to_grant ][ grant_to_pname ] = do_grant
yl_speak_up.save_quest_variables(true)
return true
end
-- get a list of all players who have read or write access to variable k (belonging to pname)
-- (technically a table and not a list)
yl_speak_up.get_access_list_for_var = function(k, pname, access_what)
k = yl_speak_up.add_pname_to_var(k, pname)
if(not(k)
or not(yl_speak_up.player_vars[ k ])
or not(yl_speak_up.player_vars[ k ][ "$META$"])
or not(yl_speak_up.player_vars[ k ][ "$META$"][ access_what ])) then
return {}
end
return yl_speak_up.player_vars[ k ][ "$META$"][ access_what ]
end
-- the dialog data of an NPC is saved - use this to save some statistical data
-- plus store which variables are used by this NPC
-- TODO: show this data in a formspec to admins for maintenance
yl_speak_up.update_stored_npc_data = function(n_id, dialog)
-- in order to determine the position of the NPC, we need its object
local pname = yl_speak_up.get_pname_for_n_id(n_id)
local npc_pos = ""
if(pname) then
local obj = yl_speak_up.speak_to[pname].obj
if(obj and obj:get_pos()) then
npc_pos = minetest.pos_to_string(obj:get_pos())
end
end
-- gather statistical data about the NPC and find out which variables it uses
local anz_dialogs = 0
local anz_options = 0
local anz_preconditions = 0
local anz_actions = 0
local anz_effects = 0
local anz_trades = 0
local variables_p = {}
local variables_e = {}
if(dialog and dialog.n_dialogs) then
for d_id, d in pairs(dialog.n_dialogs) do
anz_dialogs = anz_dialogs + 1
if(d and d.d_options) then
for o_id, o in pairs(d.d_options) do
anz_options = anz_options + 1
if(o and o.o_prerequisites) then
for p_id, p in pairs(o.o_prerequisites) do
anz_preconditions = anz_preconditions + 1
if(p and p.p_type and p.p_type == "state"
and p.p_variable and p.p_variable ~= "") then
variables_p[ p.p_variable ] = true
end
end
end
if(o and o.actions) then
for a_id, a_data in pairs(o.actions) do
anz_actions = anz_actions + 1
end
end
if(o and o.o_results) then
for r_id, r in pairs(o.o_results) do
anz_effects = anz_effects + 1
if(r and r.r_type and r.r_type == "state"
and r.r_variable and r.r_variable ~= "") then
variables_e[ r.r_variable ] = true
end
end
end
end
end
end
end
if(dialog and dialog.trades) then
for trade_id, t_data in pairs(dialog.trades) do
-- not a trade that is the action of a dialog option; only trade list trades count
if(not(t_data.d_id)) then
anz_trades = anz_trades + 1
end
end
end
-- add a special variable (if needed) for saving the NPC meta data
if(not(yl_speak_up.player_vars[ "$NPC_META_DATA$" ])) then
yl_speak_up.player_vars[ "$NPC_META_DATA$" ] = {}
end
yl_speak_up.player_vars[ "$NPC_META_DATA$" ][ n_id ] = {
n_id = n_id,
name = tostring(dialog.n_npc),
owner = tostring(yl_speak_up.npc_owner[ n_id ]),
may_edit = dialog.n_may_edit or {},
pos = tostring(npc_pos),
anz_dialogs = anz_dialogs,
anz_options = anz_options,
anz_preconditions = anz_preconditions,
anz_actions = anz_actions,
anz_effects = anz_effects,
anz_trades = anz_trades,
last_modified = os.date(),
}
-- make sure all variables have a metadata field
for k, v in pairs(yl_speak_up.player_vars) do
if( yl_speak_up.player_vars[ k ][ "$META$" ]
and yl_speak_up.player_vars[ k ][ "$META$" ].used_by_npc) then
if(type(yl_speak_up.player_vars[ k ][ "$META$" ].used_by_npc) ~= "table") then
yl_speak_up.player_vars[ k ][ "$META$" ].used_by_npc = {}
end
-- delete old entries (the correct new ones will be set shortly after this)
yl_speak_up.player_vars[ k ][ "$META$"].used_by_npc[ n_id ] = nil
end
end
-- save in the variables' metadata which NPC uses it
-- (this is what we're mostly after - know which variable is used in which NPC)
for k, v in pairs(variables_p) do
-- if the variable does not exist: create it
if(not(yl_speak_up.player_vars[ k ])) then
yl_speak_up.player_vars[ k ] = {}
end
if(not(yl_speak_up.player_vars[ k ][ "$META$" ])) then
yl_speak_up.player_vars[ k ][ "$META$"] = { used_by_npc = {} }
end
yl_speak_up.player_vars[ k ][ "$META$"][ "used_by_npc" ][ n_id ] = true
end
for k, v in pairs(variables_e) do
-- if the variable does not exist: create it
if(not(yl_speak_up.player_vars[ k ])) then
yl_speak_up.player_vars[ k ] = {}
end
if(not(yl_speak_up.player_vars[ k ][ "$META$" ])) then
yl_speak_up.player_vars[ k ][ "$META$"] = { used_by_npc = {} }
end
yl_speak_up.player_vars[ k ][ "$META$"][ "used_by_npc" ][ n_id ] = true
end
-- force writing the data
yl_speak_up.save_quest_variables(true)
end
-- which NPC do use this variable?
yl_speak_up.get_npc_users_of_variable = function(var_name)
-- no variable, or nothing stored? then it's not used by any NPC either
if(not(var_name)
or not(yl_speak_up.player_vars[ var_name ])
or not(yl_speak_up.player_vars[ var_name ][ "$META$"])
or not(yl_speak_up.player_vars[ var_name ][ "$META$"][ "used_by_npc" ])
or type(yl_speak_up.player_vars[ var_name ][ "$META$"][ "used_by_npc" ]) ~= "table") then
return {}
end
local npc_list = {}
for n_id, v in pairs(yl_speak_up.player_vars[ var_name ][ "$META$"][ "used_by_npc" ]) do
table.insert(npc_list, n_id)
end
table.sort(npc_list)
return npc_list
end
-- find out where this variable is used in NPCs
yl_speak_up.get_list_of_usage_of_variable = function(var_name, pname, check_preconditions,
back_button_name, back_button_text, is_internal_var)
-- TODO: check if the player really has read access to this variable
if(not(is_internal_var)) then
var_name = yl_speak_up.restore_complete_var_name(var_name, pname)
end
-- which NPC (might be several) is using this variable?
-- TODO: ..or if the player at least is owner of these NPC or has extended privs
local npc_list = yl_speak_up.get_npc_users_of_variable(var_name)
-- list of all relevant preconditions, actions and effects
local res = {}
local count_read = 0
local count_changed = 0
for i, n_id in ipairs(npc_list) do
-- the NPC may not even be loaded
local dialog = yl_speak_up.load_dialog(n_id)
if(dialog and dialog.n_dialogs) then
for d_id, d in pairs(dialog.n_dialogs) do
if(d and d.d_options) then
for o_id, o in pairs(d.d_options) do
local p_text = ""
local r_text = ""
local sort_value = 0
if(o and o.o_prerequisites and check_preconditions) then
for p_id, p in pairs(o.o_prerequisites) do
if(p and p.p_type and p.p_type == "state"
and p.p_variable and p.p_variable == var_name) then
p_text = p_text..yl_speak_up.print_as_table_precon(p,pname)
sort_value = (p.p_var_cmp_value or 0)
count_read = count_read + 1
end
end
end
if(o and o.o_results) then
for r_id, r in pairs(o.o_results) do
if(r and r.r_type and r.r_type == "state"
and r.r_variable and r.r_variable == var_name) then
r_text = r_text..yl_speak_up.print_as_table_effect(r,pname)
-- values set in the results are more important than
-- those set in preconditions
sort_value = (r.r_var_cmp_value or 0)
count_changed = count_changed + 1
end
end
end
-- if preconditions or effects apply: show the action as well
if(o and o.actions and (p_text ~= "" or r_text ~= "")) then
for a_id, a in pairs(o.actions) do
-- no need to introduce an a_text; this will follow
-- directly after p_text, and p_text is finished
p_text = p_text..yl_speak_up.print_as_table_action(a, pname)
end
end
yl_speak_up.print_as_table_dialog(p_text, r_text, dialog,
n_id, d_id, o_id, res, o, sort_value)
end
end
end
end
end
local formspec = yl_speak_up.print_as_table_prepare_formspec(res, "table_of_variable_uses",
back_button_name, back_button_text)
table.insert(formspec,
"label[20.0,1.8;"..
minetest.formspec_escape("Variable \""..
minetest.colorize("#FFFF00", tostring(var_name or "- ? -"))..
"\" is used here:").."]")
if(count_read > 0 or count_changed > 0) then
table.insert(formspec,
"label[16.0,31.0;The variable is accessed in "..
minetest.colorize("#FFFF00", tostring(count_read).." pre(C)onditions")..
" and changed in "..
minetest.colorize("#55FF55", tostring(count_changed).." (Ef)fects")..
".]")
elseif(not(is_internal_var)) then
table.insert(formspec,
"button[0.2,30.6;56.6,1.2;delete_unused_variable;"..
minetest.formspec_escape("Delete this unused variable \""..
tostring(var_name or "- ? -")).."\".]")
else
table.insert(formspec,
"label[16.0,31.0;This is an internal variable and cannot be deleted.]")
end
return table.concat(formspec, "\n")
end