1111 lines
41 KiB
Lua
1111 lines
41 KiB
Lua
|
|
-- general helper functions
|
|
-- TODO: not yet used
|
|
yl_speak_up.count_table_elements = function(table)
|
|
if(not(table) or type(table) ~= "table") then
|
|
return -1
|
|
end
|
|
local c = 0
|
|
for k, v in pairs(table) do
|
|
c = c + 1
|
|
end
|
|
return c
|
|
end
|
|
|
|
|
|
-- just some handling of variables
|
|
|
|
-- 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
|
|
|
|
|
|
yl_speak_up.handle_json_nil_values = function(data)
|
|
if(data and type(data) == "table") then
|
|
for k,v in pairs(data) do
|
|
if( type(v) == "string" and v == "$NIL_VALUE$") then
|
|
data[ k ] = {}
|
|
elseif(type(v) == "table") then
|
|
data[ k ] = yl_speak_up.handle_json_nil_values(v)
|
|
end
|
|
end
|
|
end
|
|
return data
|
|
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, "$NIL_VALUE$")
|
|
io.close(file)
|
|
|
|
if(type(data) ~= "table") then
|
|
return
|
|
end
|
|
yl_speak_up.player_vars = yl_speak_up.handle_json_nil_values(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
|
|
|
|
|
|
-- 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
|
|
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 ]
|
|
local npc_users = yl_speak_up.get_variable_metadata(var_name, "used_by_npc")
|
|
if(npc_users and #npc_users > 0) then
|
|
return text.." could not be deleted.\nIt is used by "..tostring(#npc_users).." NPC."
|
|
end
|
|
-- check if the variable is used by a node position (for quests)
|
|
local node_pos_users = yl_speak_up.get_variable_metadata(var_name, "used_by_node_pos")
|
|
if(node_pos_users and #node_pos_users > 0) then
|
|
return text.." could not be deleted.\nIt is used by "..tostring(#node_pos_users)..
|
|
" node positions (quest)."
|
|
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(yl_speak_up.player_vars[ k ]["$META$"]
|
|
and yl_speak_up.player_vars[ k ]["$META$"][ "debug" ]
|
|
and type(yl_speak_up.player_vars[ k ]["$META$"][ "debug" ]) == "table") then
|
|
for p, _ in pairs(yl_speak_up.player_vars[ k ]["$META$"][ "debug" ]) do
|
|
minetest.chat_send_player(p, "[Variable "..
|
|
minetest.colorize("#FFFF00", tostring(k))..
|
|
", player "..
|
|
minetest.colorize("#FFFF00", tostring(player_name))..
|
|
"] old value: \""..
|
|
minetest.colorize("#FFFF00",
|
|
tostring(yl_speak_up.player_vars[ k ][ player_name ]))..
|
|
"\" new value: \""..
|
|
minetest.colorize("#FFFF00", tostring(new_value)).."\".")
|
|
end
|
|
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})
|
|
or minetest.check_player_privs(pname, {npc_talk_admin=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
|
|
|
|
|
|
-- 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.handle_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)
|
|
-- 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
|
|
return yl_speak_up.set_variable_metadata(k, pname, what_to_grant, grant_to_pname, do_grant)
|
|
end
|
|
|
|
|
|
-- a more general way of setting metadata for variables
|
|
-- 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)
|
|
if(pname) then
|
|
k = yl_speak_up.add_pname_to_var(k, pname)
|
|
end
|
|
-- delete/unset
|
|
if(not(new_value)) then
|
|
new_value = nil
|
|
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$" ] = { meta_name = {} }
|
|
end
|
|
|
|
-- var_type (the type of the variable) is a single string
|
|
if(meta_name == "var_type") then
|
|
yl_speak_up.player_vars[ k ][ "$META$"][ meta_name ] = new_value
|
|
else
|
|
if( not(yl_speak_up.player_vars[ k ][ "$META$" ][ meta_name ])
|
|
or type(yl_speak_up.player_vars[ k ][ "$META$" ][ meta_name ]) ~= "table") then
|
|
yl_speak_up.player_vars[ k ][ "$META$" ][ meta_name ] = {}
|
|
end
|
|
yl_speak_up.player_vars[ k ][ "$META$"][ meta_name ][ entry_name ] = new_value
|
|
end
|
|
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
|
|
|
|
|
|
-- helper function that searces for variables that will be replaced with their
|
|
-- values in text when displayed; helper function for yl_speak_up.update_stored_npc_data
|
|
-- (for keeping track of which NPC uses which variables)
|
|
-- changes table vars_used
|
|
yl_speak_up.find_player_vars_in_text = function(vars_used, text)
|
|
if(not(text) or text == "") then
|
|
return vars_used
|
|
end
|
|
for v in string.gmatch(text, "%$VAR ([%w%s_%-%.]+)%$") do
|
|
-- add the $ prefix again
|
|
vars_used["$ "..tostring(v)] = true
|
|
end
|
|
return vars_used
|
|
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
|
|
-- used in d.d_text dialog texts,
|
|
-- o.o_text_when_prerequisites_met, o.o_text_when_prerequisites_not_met,
|
|
-- preconditions and effects
|
|
local variables_used = {}
|
|
if(dialog and dialog.n_dialogs) then
|
|
for d_id, d in pairs(dialog.n_dialogs) do
|
|
anz_dialogs = anz_dialogs + 1
|
|
if(d) then
|
|
-- find all variables used in the text
|
|
variables_used = yl_speak_up.find_player_vars_in_text(variables_used, d.d_text)
|
|
end
|
|
if(d and d.d_options) then
|
|
for o_id, o in pairs(d.d_options) do
|
|
anz_options = anz_options + 1
|
|
variables_used = yl_speak_up.find_player_vars_in_text(variables_used, o.o_text_when_prerequisites_met)
|
|
variables_used = yl_speak_up.find_player_vars_in_text(variables_used, o.o_text_when_prerequisites_not_met)
|
|
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_used[ 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
|
|
-- actions can have alternate_text
|
|
variables_used = yl_speak_up.find_player_vars_in_text(variables_used, a_data.alternate_text)
|
|
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_used[ r.r_variable ] = true
|
|
end
|
|
-- effects can have alternate_text
|
|
variables_used = yl_speak_up.find_player_vars_in_text(variables_used, r.alternate_text)
|
|
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(),
|
|
}
|
|
|
|
-- delete all old entries that are not longer needed
|
|
for k, v in pairs(yl_speak_up.player_vars) do
|
|
if(not(variables_used[ k ])) then
|
|
yl_speak_up.set_variable_metadata(k, pname, "used_by_npc", n_id, false)
|
|
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_used) do
|
|
yl_speak_up.set_variable_metadata(k, pname, "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_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 ])
|
|
or not(yl_speak_up.player_vars[ var_name ][ "$META$"])) then
|
|
return nil
|
|
end
|
|
return yl_speak_up.player_vars[ var_name ][ "$META$"][ meta_name ]
|
|
end
|
|
-- no variable, or nothing stored? then it's not used by any NPC either
|
|
if(not(var_name)
|
|
or not(meta_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$"][ 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)
|
|
end
|
|
table.sort(meta_list)
|
|
return meta_list
|
|
end
|
|
|
|
|
|
-- show which variables the player is currently debugging
|
|
yl_speak_up.get_list_of_debugged_variables = function(pname)
|
|
if(not(pname) or pname == "") then
|
|
return
|
|
end
|
|
local res = {}
|
|
for k, v in pairs(yl_speak_up.player_vars) do
|
|
if(k and v and v[ "$META$" ] and v[ "$META$" ][ "debug" ]) then
|
|
-- this will be used in a table presented to the player
|
|
table.insert(res, minetest.formspec_escape(k))
|
|
end
|
|
end
|
|
return res
|
|
end
|
|
|
|
|
|
-- helper function; time is sometimes needed
|
|
yl_speak_up.get_time_in_seconds = function()
|
|
return math.floor(minetest.get_us_time()/1000000)
|
|
end
|
|
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Quests as such (up until here we mostly dealt with variables)
|
|
-----------------------------------------------------------------------------
|
|
|
|
-- uses yl_speak_up.quest_path
|
|
-- uses yl_speak_up.number_of_quests = yl_speak_up.modstorage:get_int("max_quest_id") or 0
|
|
|
|
-- table containing the quest data with q_id as index
|
|
yl_speak_up.quests = {}
|
|
|
|
|
|
-- store quest q_id to disc
|
|
yl_speak_up.save_quest = function(q_id)
|
|
local json = minetest.write_json(yl_speak_up.quests[q_id])
|
|
-- actually store it on disk
|
|
local file_name = yl_speak_up.worldpath..yl_speak_up.quest_path..DIR_DELIM..q_id..".json"
|
|
minetest.safe_file_write(file_name, json)
|
|
end
|
|
|
|
|
|
|
|
-- read quest q_id from disc
|
|
yl_speak_up.load_quest = function(q_id)
|
|
-- load the data from the file
|
|
local file_name = yl_speak_up.worldpath..yl_speak_up.quest_path..DIR_DELIM..q_id..".json"
|
|
local file, err = io.open(file_name, "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, "$NIL_VALUE$")
|
|
io.close(file)
|
|
|
|
if(type(data) ~= "table") then
|
|
return
|
|
end
|
|
yl_speak_up.quests[q_id] = yl_speak_up.handle_json_nil_values(data)
|
|
-- make sure all required fields exist
|
|
local quest = yl_speak_up.quests[q_id]
|
|
if(quest and not(quest.step_data)) then
|
|
quest.step_data = {}
|
|
end
|
|
if(quest and not(quest.npcs)) then
|
|
quest.npcs = {}
|
|
end
|
|
if(quest and not(quest.locations)) then
|
|
quest.locations = {}
|
|
end
|
|
if(quest) then
|
|
for s, d in pairs(quest.step_data) do
|
|
if(not(d.where)) then
|
|
quest.step_data[s].where = {}
|
|
end
|
|
if(not(d.one_step_required)) then
|
|
quest.step_data[s].one_step_required = {}
|
|
end
|
|
if(not(d.all_steps_required)) then
|
|
quest.step_data[s].all_steps_required = {}
|
|
end
|
|
end
|
|
end
|
|
yl_speak_up.quests[q_id] = quest
|
|
return yl_speak_up.quests[q_id]
|
|
end
|
|
|
|
|
|
-- get data of quest q_id
|
|
yl_speak_up.get_quest = function(q_id)
|
|
if(not(yl_speak_up.quests[q_id])) then
|
|
yl_speak_up.load_quest(q_id)
|
|
end
|
|
return yl_speak_up.quests[q_id]
|
|
end
|
|
|
|
|
|
-- add/create a new quest
|
|
-- a quest is based on a variable; the variable is needed to store quest progress;
|
|
-- as this variable is of type integer, quests can only be linear;
|
|
-- in order to offer alternatives, players can add as many quests as they want and
|
|
-- make them depend on each other
|
|
yl_speak_up.add_quest = function(owner_name, variable_name, quest_name, descr_long, descr_short, comment)
|
|
-- add a special variable (if needed) for saving quest meta data
|
|
if(not(yl_speak_up.player_vars[ "$QUEST_META_DATA$" ])) then
|
|
yl_speak_up.player_vars[ "$QUEST_META_DATA$" ] = {}
|
|
yl_speak_up.save_quest_variables(true)
|
|
end
|
|
|
|
if(not(variable_name) or variable_name == "") then
|
|
return "Missing name of variable."
|
|
end
|
|
-- determine the full name of the variable used to store quest progress
|
|
local var_name = yl_speak_up.add_pname_to_var(variable_name, owner_name)
|
|
-- if it is a new variable: make sure it gets created
|
|
if(not(yl_speak_up.player_vars[var_name])) then
|
|
-- create the new varialbe
|
|
yl_speak_up.add_quest_variable(owner_name, variable_name)
|
|
else
|
|
-- if it exists already: make sure it is of a type that can be used
|
|
local var_type = yl_speak_up.get_variable_metadata(var_name, "var_type")
|
|
if("var_type" == "time_based") then
|
|
return "Variable already used as a timer."
|
|
elseif("var_type" == "quest") then
|
|
return "Variable already used by another quest."
|
|
end
|
|
end
|
|
-- set the variable for the quest creator to 0 - so that it's possible to check for
|
|
-- var_name is set (to a value) in a precondition and thus only allow the quest creator
|
|
-- to test the quest in the beginning
|
|
yl_speak_up.set_quest_variable_value(owner_name, var_name, 0)
|
|
-- set the variable type to quest
|
|
yl_speak_up.set_variable_metadata(var_name, owner_name, "var_type", nil, "quest")
|
|
-- get a uniq ID for storing this quest (mostly needed for creating a safe file name)
|
|
local quest_nr = yl_speak_up.number_of_quests + 1
|
|
yl_speak_up.number_of_quests = quest_nr
|
|
yl_speak_up.modstorage:set_int("max_quest_nr", yl_speak_up.number_of_quests)
|
|
-- store this number in the variable $META$ data
|
|
yl_speak_up.set_variable_metadata(var_name, owner_name, "quest_data", "quest_nr", quest_nr)
|
|
-- the list of quest steps is stored in the variables' metadata for quicker access
|
|
-- (this way we won't have to load the quest file if we want to check a precondition
|
|
-- or update the variable value to the next quest step)
|
|
-- TODO: store those in the quest file
|
|
yl_speak_up.set_variable_metadata(var_name, owner_name, "quest_data", "steps", {"start","finish"})
|
|
|
|
-- create the quest data structure
|
|
local quest = {}
|
|
quest.nr = quest_nr
|
|
quest.id = "q_"..quest_nr -- quest ID
|
|
quest.name = quest_name -- human-readable name of the quest
|
|
quest.description = (descr_long or "")
|
|
-- long description of what the quest is about
|
|
quest.short_desc = (descr_short or "")
|
|
-- a short description of this quest which may later be used to
|
|
-- advertise for the quest in a quest log
|
|
quest.comment = (comment or "")
|
|
-- comment to other programmers who might want to maintain the
|
|
-- quest later on
|
|
quest.owner = owner_name -- creator of the quest
|
|
quest.var_name = var_name -- name of the variable where progress is stored for each player
|
|
-- quest.steps = { -- list of names (strings) of the quest steps
|
|
-- "start", -- the quest needs to start somehow
|
|
-- "finish"} -- and it needs to finish somehow
|
|
-- the following things can be determined automaticly, BUT: in order to PLAN a future
|
|
-- quest, it is easier to gather information here first
|
|
quest.step_data = {} -- table containing information about a quest step (=key)
|
|
-- this may also be information about WHERE a quest step shall
|
|
-- take place
|
|
quest.subquests = {} -- list of other quest_ids that contribute to this quest
|
|
-- -> determined from quests.npcs and quests.locations
|
|
quest.is_subquest_of = {} -- list of quest_ids this quest contributes to
|
|
-- -> determined from quests.npcs and quests.locations
|
|
quest.npcs = {} -- list of NPC that contribute to this quest
|
|
-- -> derived from quest.var_name
|
|
-- --> or derived from quest.step_data.where
|
|
quest.locations = {} -- list of locations that contribute to this quest
|
|
-- -> derived from quest.var_name
|
|
-- --> or derived from quest.step_data.where
|
|
quest.items = {} -- data of quest items created and accepted
|
|
-- -> derived from the quest steps
|
|
quest.rewards = {} -- list of rewards (item stacks) for this ques
|
|
quest.testers = {} -- list of player names that can test the quest
|
|
-- -> during the created/testing phase: any player for which
|
|
-- quest.var_name is set to a value
|
|
quest.solved_by = {} -- list of names of players that solved the quest at least once
|
|
quest.state = "created" -- state of the quest:
|
|
-- created: only the creator can do it
|
|
-- testing: players listed in quest.testers can do the quest
|
|
-- open: *all* players with interact can try to solve the quest
|
|
-- *AND* changes to the quest are now impossible (apart from
|
|
-- changing texts in the NPC)
|
|
-- official: official server quest; NPC can create items out of thin air
|
|
-- store the new quest in the quest table
|
|
yl_speak_up.quests[quest.id] = quest
|
|
-- and store it on disc
|
|
yl_speak_up.save_quest(quest.id)
|
|
return "OK"
|
|
end
|
|
|
|
|
|
-- delete a quest if possible
|
|
yl_speak_up.del_quest = function(q_id, pname)
|
|
if(not(q_id)) then
|
|
return "No quest ID given. Quest not found."
|
|
end
|
|
local quest = yl_speak_up.load_quest(q_id)
|
|
if(not(quest)) then
|
|
return "Quest "..tostring(q_id).." does not exist."
|
|
end
|
|
if(quest.owner ~= pname
|
|
and not(minetest.check_player_privs(pname, {npc_master=true}))
|
|
and not(minetest.check_player_privs(pname, {npc_talk_master=true}))
|
|
and not(minetest.check_player_privs(pname, {npc_talk_admin=true}))) then
|
|
return "Quest "..tostring(q_id).." is owned by "..tostring(quest.owner)..
|
|
".\n You can't delete it."
|
|
end
|
|
if(quest.state ~= "created" and quest.state ~= "testing") then
|
|
return "Quest "..tostring(q_id).." is in stage \""..tostring(quest.state)..
|
|
"\".\n Only quests in state \"created\" or \"testing\" can be deleted."
|
|
end
|
|
if(#quest.is_subquest_of > 0) then
|
|
return "Quest "..tostring(q_id).." is used by the following subquests:\n"..
|
|
table.concat(quest.subquests, ", ")..
|
|
".\nPlease remove the subquests first!"
|
|
end
|
|
|
|
for k, v in pairs(quest.step_data) do
|
|
if(v) then
|
|
return "Quest "..tostring(q_id).." contains at least one remaining quest step.\n"..
|
|
"Please remove all steps first!"
|
|
end
|
|
end
|
|
|
|
-- TODO: actually delete the file?
|
|
-- TODO: set the quest variable back to no type
|
|
-- TODO: delete (empty?) quest variable? yl_speak_up.del_quest_variable(pname, entry_name, nil)
|
|
return "OK"
|
|
end
|
|
|
|
|
|
-- returns a list of all quest IDs to which the player has write access
|
|
-- TODO: function is unused
|
|
yl_speak_up.get_quest_owner_list = function(pname)
|
|
local var_list = yl_speak_up.get_quest_variables_with_write_access(pname)
|
|
local quest_id_list = {}
|
|
for i, var_name in ipairs(var_list) do
|
|
local t = yl_speak_up.get_variable_metadata(var_name, "var_type")
|
|
if(t and t == "quest") then
|
|
local data = yl_speak_up.get_variable_metadata(var_name, "quest_data", true)
|
|
if(data and data["quest_nr"]) then
|
|
local q_id = "q_"..tostring(data["quest_nr"])
|
|
yl_speak_up.load_quest(q_id)
|
|
-- offer the quest only if it was loaded successfully
|
|
if(yl_speak_up.quests[q_id]) then
|
|
table.insert(quest_id_list, q_id)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return quest_id_list
|
|
end
|
|
|
|
|
|
yl_speak_up.get_sorted_quest_list = function(pname)
|
|
local quest_list = {}
|
|
local has_privs = (minetest.check_player_privs(pname, {npc_master=true})
|
|
or minetest.check_player_privs(pname, {npc_talk_master=true})
|
|
or minetest.check_player_privs(pname, {npc_talk_admin=true}))
|
|
for q_id, data in pairs(yl_speak_up.quests) do
|
|
if(data and data.var_name) then
|
|
if(has_privs
|
|
or(data.owner and data.owner == pname)
|
|
or(table.indexof(
|
|
yl_speak_up.get_access_list_for_var(
|
|
data.var_name, pname, "write_access") or {}) ~= -1)) then
|
|
table.insert(quest_list, data.var_name)
|
|
end
|
|
end
|
|
end
|
|
yl_speak_up.strip_pname_from_varlist(quest_list, pname)
|
|
table.sort(quest_list)
|
|
return quest_list
|
|
end
|
|
|
|
|
|
|
|
-- quests have a name and a variable which stores their data
|
|
-- this returns the q_id (index in the quest table) based on the variable name
|
|
yl_speak_up.get_quest_id_by_var_name = function(var_name, owner_name)
|
|
local var_name = yl_speak_up.add_pname_to_var(var_name, owner_name)
|
|
-- find out which quest we're talking about
|
|
for q_id, quest in pairs(yl_speak_up.quests) do
|
|
if(quest.var_name == var_name) then
|
|
return q_id
|
|
end
|
|
end
|
|
-- TODO or we may have a leftover variable with no quest information stored
|
|
-- local var_type = yl_speak_up.get_variable_metadata(var_name, "var_type")
|
|
-- if("var_type" == "quest") then
|
|
-- -- this is no longer a quest variable - the quest is long gone
|
|
-- yl_speak_up.set_variable_metadata(var_name, owner_name, "var_type", nil, "string")
|
|
-- yl_speak_up.set_variable_metadata(var_name, owner_name, "quest_data", "quest_nr", nil)
|
|
-- yl_speak_up.set_variable_metadata(var_name, owner_name, "quest_data", "steps", nil)
|
|
-- end
|
|
return nil
|
|
end
|
|
|
|
|
|
-- quests can also be identified by their name
|
|
-- this returns the q_id (index in the quest table) based on the quest name
|
|
yl_speak_up.get_quest_id_by_quest_name = function(quest_name)
|
|
-- find out which quest we're talking about
|
|
for q_id, quest in pairs(yl_speak_up.quests) do
|
|
if(quest.name == quest_name) then
|
|
return q_id
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
|
|
-- finds out if player pname is allowed to view (read_only is true)
|
|
-- or edit (read_only is false) the quest q_id
|
|
yl_speak_up.quest_allow_access = function(q_id, pname, read_only)
|
|
-- no quest with that variable as base found
|
|
if(not(q_id) or not(yl_speak_up.quests[q_id])) then
|
|
return "Quest not found (id: "..tostring(q_id)..")."
|
|
end
|
|
local quest = yl_speak_up.quests[q_id]
|
|
-- check if the player has access rights to that quest
|
|
if(quest.owner ~= pname
|
|
and not(minetest.check_player_privs(pname, {npc_master=true}))
|
|
and not(minetest.check_player_privs(pname, {npc_talk_master=true}))
|
|
and not(minetest.check_player_privs(pname, {npc_talk_admin=true}))) then
|
|
-- the player may have suitable privs (check access to the variable)
|
|
local access_what = "write_access"
|
|
if(read_only) then
|
|
access_what = "read_access"
|
|
end
|
|
local allowed = yl_speak_up.get_access_list_for_var(quest.var_name, "", access_what)
|
|
if(table.indexof(allowed, pname) == -1) then
|
|
return "Sorry. You have no write access to quest \""..tostring(quest.name).."\" "..
|
|
"["..tostring(k).."]."
|
|
end
|
|
end
|
|
-- quests that are already open to the public cannot be changed anymore
|
|
-- as that would cause chaos; only in "created" and "testing" stage it's
|
|
-- possible to change quest steps
|
|
if(not(read_only) and (quest.state == "open" or quest.state == "official")) then
|
|
return "The quest is in state \""..tostring(quest.state).."\". Quests in such "..
|
|
"a state cannot be changed/extended as that would confuse players. "..
|
|
"Reset quest state first if changes are unavoidable."
|
|
end
|
|
return "OK"
|
|
end
|
|
|
|
|
|
-- add a quest step to a quest
|
|
yl_speak_up.quest_step_add_quest_step = function(pname, q_id, quest_step_name)
|
|
local error_msg = yl_speak_up.quest_allow_access(q_id, pname, false)
|
|
if(error_msg ~= "OK") then
|
|
return error_msg
|
|
end
|
|
if(not(quest_step_name) or quest_step_name == ""
|
|
or string.len(quest_step_name) < 2 or string.len(quest_step_name) > 70) then
|
|
return "No name for this quest step given or too long (>70) or too short (<2 characters)."
|
|
end
|
|
if(not(yl_speak_up.quests[q_id].step_data)) then
|
|
yl_speak_up.quests[q_id].step_data = {}
|
|
end
|
|
-- create an entry for the quest step if needed
|
|
if(not(yl_speak_up.quests[q_id].step_data[quest_step_name])) then
|
|
yl_speak_up.quests[q_id].step_data[quest_step_name] = {
|
|
-- where (NPCs, locations) can this quest step be set?
|
|
where = {},
|
|
-- at least one of this quest steps has to be achieved before this one is possible
|
|
one_step_required = {},
|
|
-- all of these quest steps have to be achieved before this one is possible
|
|
all_steps_required = {}
|
|
}
|
|
yl_speak_up.save_quest(q_id)
|
|
end
|
|
-- return OK even if the quest step existed already
|
|
return "OK"
|
|
end
|
|
|
|
|
|
-- delete a quest step - but only if it's not used
|
|
yl_speak_up.quest_step_del_quest_step = function(pname, q_id, quest_step_name)
|
|
local error_msg = yl_speak_up.quest_allow_access(q_id, pname, false)
|
|
if(error_msg ~= "OK") then
|
|
return error_msg
|
|
end
|
|
if(not(quest_step_name)
|
|
or not(yl_speak_up.quests[q_id].step_data)
|
|
or not(yl_speak_up.quests[q_id].step_data[quest_step_name])) then
|
|
return "OK"
|
|
end
|
|
-- the quest step exists; can we delete it?
|
|
local quest_step = yl_speak_up.quests[q_id].step_data[quest_step_name]
|
|
local anz_where = 0
|
|
for k, _ in pairs(quest_step.where or {}) do
|
|
anz_where = anz_where + 1
|
|
end
|
|
if(anz_where > 0) then
|
|
return "This quest step is used/set by "..tostring(anz_where)..
|
|
" NPCs and/or locations.\nRemove them from this quest step first!"
|
|
end
|
|
-- is this the previous quest step of another step?
|
|
for sn, step_data in pairs(yl_speak_up.quests[q_id].step_data) do
|
|
if(step_data and step_data.previous_step and step_data.previous_step == quest_step_name) then
|
|
return "Quest step \""..tostring(sn).."\" names this quest step that you want "..
|
|
"to delete as its previous step. Please remove that requirement first "..
|
|
"for quest step \""..tostring(sn).."\" before deleting this step here."
|
|
end
|
|
if(step_data and step_data.further_required_steps
|
|
and table.indexof(step_data.further_required_steps, quest_step_name) ~= -1) then
|
|
return "Quest step \""..tostring(sn).."\" gives this quest step that you want "..
|
|
"to delete as one of the further steps required to reach it. Please "..
|
|
"remove that requirement first "..
|
|
"for quest step \""..tostring(sn).."\" before deleting this step here."
|
|
end
|
|
-- offered_until_quest_step_reached would be no problem/hinderance and doesn't need checking
|
|
end
|
|
yl_speak_up.quests[q_id].step_data[quest_step_name] = nil
|
|
yl_speak_up.save_quest(q_id)
|
|
return "OK"
|
|
end
|
|
|
|
|
|
-- turn a location {n_id=.., d_id=.., c_id=..} or position into a uniq string
|
|
yl_speak_up.get_location_id = function(loc)
|
|
if(not(loc) or type(loc) ~= "table") then
|
|
return nil
|
|
end
|
|
if(loc.is_block and loc.n_id and loc.d_id and loc.o_id) then
|
|
return "POS "..tostring(loc.n_id).." "..tostring(loc.d_id).." "..tostring(loc.o_id)
|
|
-- if it's an NPC:
|
|
elseif(loc.n_id and string.sub(loc.n_id, 1, 2) == "n_" and loc.d_id and loc.o_id) then
|
|
return "NPC "..tostring(loc.n_id).." "..tostring(loc.d_id).." "..tostring(loc.o_id)
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
|
|
-- add an NPC or location to a quest step (quest_step.where = list of such locations)
|
|
-- Note: This is for NPC and locations that SET this very quest step. They ought to be listed here.
|
|
-- new_location has to be a table, and new_loc_id an ID to avoid duplicates
|
|
-- for NPC, new_loc_id ought to look like this: "NPC <n_id> <d_id> <o_id>"
|
|
yl_speak_up.quest_step_add_where = function(pname, q_id, quest_step_name, new_location)
|
|
local error_msg = yl_speak_up.quest_allow_access(q_id, pname, false)
|
|
if(error_msg ~= "OK") then
|
|
return error_msg
|
|
end
|
|
local step_data = yl_speak_up.quests[q_id].step_data[quest_step_name]
|
|
if(not(step_data)) then
|
|
return "Quest step \""..tostring(quest_step_name).."\" does not exist."
|
|
end
|
|
if(not(step_data.where)) then
|
|
step_data.where = {}
|
|
end
|
|
local new_loc_id = yl_speak_up.get_location_id(new_location)
|
|
if(not(new_loc_id)) then
|
|
return "Failed to create location ID for this location/NPC."
|
|
end
|
|
-- overwrite existing/old entries
|
|
yl_speak_up.quests[q_id].step_data[quest_step_name].where[new_loc_id] = new_location
|
|
-- make sure quest.npcs or quest.locations contains this entry
|
|
local n_id = new_location.n_id or "?"
|
|
if(string.sub(n_id, 1, 2) == "n_") then
|
|
-- only npcs that are not yet added (and we store IDs without n_ prefix)
|
|
local id = tonumber(string.sub(n_id, 3))
|
|
if(id and table.indexof(yl_speak_up.quests[q_id].npcs or {}, id) == -1) then
|
|
table.insert(yl_speak_up.quests[q_id].npcs, id)
|
|
end
|
|
elseif(string.sub(n_id, 1, 1) == "p"
|
|
and table.indexof(yl_speak_up.quests[q_id].locations or {}, n_id) == -1) then
|
|
table.insert(yl_speak_up.quests[q_id].locations, n_id)
|
|
end
|
|
yl_speak_up.save_quest(q_id)
|
|
-- return OK even if the quest step existed already
|
|
return "OK"
|
|
end
|
|
|
|
|
|
-- delete a quest step location with the id location_id
|
|
yl_speak_up.quest_step_del_where = function(pname, q_id, quest_step_name, old_location)
|
|
local error_msg = yl_speak_up.quest_allow_access(q_id, pname, false)
|
|
if(error_msg ~= "OK") then
|
|
return error_msg
|
|
end
|
|
local quest_step = yl_speak_up.quests[q_id].step_data[quest_step_name]
|
|
if(not(quest_step)) then
|
|
return "Quest step \""..tostring(quest_step_name).."\" does not exist."
|
|
end
|
|
local loc_id = yl_speak_up.get_location_id(old_location)
|
|
if(not(loc_id)) then
|
|
return "Failed to create location ID for this location/NPC."
|
|
end
|
|
if(not(yl_speak_up.quests[q_id].step_data[quest_step_name])) then
|
|
yl_speak_up.quests[q_id].step_data[quest_step_name].where = {}
|
|
end
|
|
-- delete the quest step location
|
|
yl_speak_up.quests[q_id].step_data[quest_step_name].where[loc_id] = nil
|
|
yl_speak_up.save_quest(q_id)
|
|
return "OK"
|
|
end
|
|
|
|
|
|
-- TODO: quest_step: previous_step -> one_step_required
|
|
-- TODO: quest_step: further_required_steps -> all_steps_required
|
|
-- TODO: quest_step: offered_until_quest_step_reached
|
|
|
|
|
|
-- called for example by yl_speak_up.eval_all_preconditions to see if the player
|
|
-- can reach quest_step in quest quest_id
|
|
yl_speak_up.quest_step_possible = function(player, quest_step, quest_id, n_id, d_id, o_id)
|
|
-- TODO: evaluate that
|
|
-- TODO: the *previous* quest step needs to have been reached
|
|
-- TODO: the quest step *after* this quest step hasn't been reached (does that work?)
|
|
-- TODO: the quest needs to be owned by the player, the player be an authorized tester,
|
|
-- or the quest be in the official released stage
|
|
-- minetest.chat_send_player("singleplayer", "TESTING quest step "..tostring(quest_step).." for quest "..tostring(quest_id))
|
|
return true
|
|
end
|
|
|
|
|
|
-- sets quest_step in quest_id for player as achieved
|
|
-- called for example by yl_speak_up.execute_all_relevant_effects if the action was
|
|
yl_speak_up.quest_step_reached = function(player, quest_step, quest_id, n_id, d_id, o_id)
|
|
-- TODO: check again if it's possible? we don't want to step back in the quest_step chain
|
|
-- TODO: actually store the quest progress
|
|
-- minetest.chat_send_player("singleplayer", "SETTING quest step "..tostring(quest_step).." for quest "..tostring(quest_id))
|
|
end
|
|
|
|
|
|
|
|
-- load all known quests
|
|
yl_speak_up.load_all_quests = function()
|
|
for var_name, var_data in pairs(yl_speak_up.player_vars) do
|
|
local var_type = yl_speak_up.get_variable_metadata(var_name, "var_type")
|
|
if(var_type == "quest") then
|
|
local data = yl_speak_up.get_variable_metadata(var_name, "quest_data", true)
|
|
if(data and data["quest_nr"]) then
|
|
yl_speak_up.load_quest("q_"..tostring(data["quest_nr"]))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- do so on startup and reload
|
|
yl_speak_up.load_all_quests()
|