-- 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: -- $ (makes it easier to grant read access) -- the values are of the form: -- : 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 yl_speak_up.del_quest_variable = function(owner_name, variable_name) local k = yl_speak_up.restore_complete_var_name(variable_name, owner_name) if(not(owner_name) or not(variable_name)) then return false end -- actually delete the variable yl_speak_up.player_vars[ k ] = nil -- a variable was deleted - that deserves a forced save yl_speak_up.save_quest_variables(true) return true 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 end -- TODO: insert those vars owned by other players where this one has read access to 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 ] -- revoke read or write access to a variable if(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 -- TODO: delete variable -- 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) 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 local k = var_list[ yl_speak_up.speak_to[pname].tmp_index_variable ] 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 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")..".]" end return "size[14,6.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,6.0;1.0,0.6;back;Back]" end -- variables are personalized; they are prefixed by "$ " -- 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 -- (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(), } -- 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" ])) 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 -- TODO: make delete_unused_variable work 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