forked from your-land-mirror/yl_speak_up
split up functions.lua into diffrent files
This commit is contained in:
parent
7e3ea18653
commit
c4ebef21f0
@ -1,33 +1,12 @@
|
|||||||
--###
|
--
|
||||||
-- Init
|
-- These functions here access and manipulate the "dialogs" data structure.
|
||||||
--###
|
-- It is loaded for each player whenever the player talks to an NPC. Each
|
||||||
|
-- talking player gets *a copy* of that data structure.
|
||||||
-- self (the npc as such) is rarely passed on to any functions; in order to be able to check if
|
--
|
||||||
-- the player really owns the npc, we need to have that data available;
|
-- As this mod is about this "dialogs" data structure and its editing, this
|
||||||
-- format: yl_speak_up.npc_owner[ npc_id ] = owner_name
|
-- isn't the only place in this mod where the data structure is accessed
|
||||||
yl_speak_up.npc_owner = {}
|
-- and/or manipulated. This here just contains some common functions.
|
||||||
|
--
|
||||||
-- store the current trade between player and npc in case it gets edited in the meantime
|
|
||||||
yl_speak_up.trade = {}
|
|
||||||
|
|
||||||
-- store what the player last entered in an text_input action
|
|
||||||
yl_speak_up.last_text_input = {}
|
|
||||||
|
|
||||||
yl_speak_up.reset_vars_for_player = function(pname, reset_fs_version)
|
|
||||||
yl_speak_up.speak_to[pname] = nil
|
|
||||||
yl_speak_up.last_text_input[pname] = nil
|
|
||||||
-- when just stopping editing: don't reset the fs_version
|
|
||||||
if(reset_fs_version) then
|
|
||||||
yl_speak_up.fs_version[pname] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--###
|
|
||||||
-- Debug
|
|
||||||
--###
|
|
||||||
|
|
||||||
yl_speak_up.debug = true
|
|
||||||
|
|
||||||
--###
|
--###
|
||||||
-- Helpers
|
-- Helpers
|
||||||
--###
|
--###
|
||||||
@ -39,20 +18,6 @@ yl_speak_up.get_number_from_id = function(any_id)
|
|||||||
return string.split(any_id, "_")[2]
|
return string.split(any_id, "_")[2]
|
||||||
end
|
end
|
||||||
|
|
||||||
local function save_path(n_id)
|
|
||||||
return yl_speak_up.worldpath .. yl_speak_up.path .. DIR_DELIM .. n_id .. ".json"
|
|
||||||
end
|
|
||||||
|
|
||||||
yl_speak_up.get_error_message = function()
|
|
||||||
local formspec = {
|
|
||||||
"size[13.4,8.5]",
|
|
||||||
"bgcolor[#FF0000]",
|
|
||||||
"label[0.2,0.35;Please save a NPC file first]",
|
|
||||||
"button_exit[0.2,7.7;3,0.75;button_back;Back]"
|
|
||||||
}
|
|
||||||
|
|
||||||
return table.concat(formspec, "")
|
|
||||||
end
|
|
||||||
|
|
||||||
yl_speak_up.find_next_id = function(t)
|
yl_speak_up.find_next_id = function(t)
|
||||||
local start_id = 1
|
local start_id = 1
|
||||||
@ -88,120 +53,12 @@ yl_speak_up.sanitize_sort = function(options, value)
|
|||||||
return retval
|
return retval
|
||||||
end
|
end
|
||||||
|
|
||||||
--###
|
|
||||||
--Load and Save
|
|
||||||
--###
|
|
||||||
|
|
||||||
-- we can't really log changes here in this function because we don't know *what* has been changed
|
|
||||||
yl_speak_up.save_dialog = function(n_id, dialog)
|
|
||||||
if type(n_id) ~= "string" or type(dialog) ~= "table" then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local p = save_path(n_id)
|
|
||||||
-- save some data (in particular usage of quest variables)
|
|
||||||
yl_speak_up.update_stored_npc_data(n_id, dialog)
|
|
||||||
-- make sure we never store any automaticly added generic dialogs
|
|
||||||
dialog = yl_speak_up.strip_generic_dialogs(dialog)
|
|
||||||
-- never store d_dynamic dialogs
|
|
||||||
if(dialog.n_dialogs and dialog.n_dialogs["d_dynamic"]) then
|
|
||||||
dialog.n_dialogs["d_dynamic"] = nil
|
|
||||||
end
|
|
||||||
local content = minetest.write_json(dialog)
|
|
||||||
return minetest.safe_file_write(p, content)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- if a player is supplied: include generic dialogs
|
|
||||||
yl_speak_up.load_dialog = function(n_id, player) -- returns the saved dialog
|
|
||||||
local p = save_path(n_id)
|
|
||||||
|
|
||||||
-- note: add_generic_dialogs will also add an empty d_dynamic dialog
|
|
||||||
local file, err = io.open(p, "r")
|
|
||||||
if err then
|
|
||||||
return yl_speak_up.add_generic_dialogs({}, n_id, player)
|
|
||||||
end
|
|
||||||
io.input(file)
|
|
||||||
local content = io.read()
|
|
||||||
local dialog = minetest.parse_json(content)
|
|
||||||
io.close(file)
|
|
||||||
|
|
||||||
if type(dialog) ~= "table" then
|
|
||||||
dialog = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
return yl_speak_up.add_generic_dialogs(dialog, n_id, player)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- used by staff and input_inital_config
|
|
||||||
yl_speak_up.fields_to_dialog = function(pname, fields)
|
|
||||||
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
||||||
local dialog = yl_speak_up.load_dialog(n_id, false)
|
|
||||||
local save_d_id = ""
|
|
||||||
|
|
||||||
if next(dialog) == nil then -- No file found. Let's create the basic values
|
|
||||||
dialog = {}
|
|
||||||
dialog.n_dialogs = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
if dialog.n_dialogs == nil or next(dialog.n_dialogs) == nil then --No dialogs found. Let's make a table
|
|
||||||
dialog.n_dialogs = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
if fields.d_text ~= "" then -- If there is dialog text, then save new or old dialog
|
|
||||||
if fields.d_id == yl_speak_up.text_new_dialog_id then --New dialog --
|
|
||||||
-- Find highest d_id and increase by 1
|
|
||||||
save_d_id = "d_" .. yl_speak_up.find_next_id(dialog.n_dialogs)
|
|
||||||
|
|
||||||
-- Initialize empty dialog
|
|
||||||
dialog.n_dialogs[save_d_id] = {}
|
|
||||||
else -- Already existing dialog
|
|
||||||
save_d_id = fields.d_id
|
|
||||||
end
|
|
||||||
-- Change dialog
|
|
||||||
dialog.n_dialogs[save_d_id].d_id = save_d_id
|
|
||||||
dialog.n_dialogs[save_d_id].d_type = "text"
|
|
||||||
dialog.n_dialogs[save_d_id].d_text = fields.d_text
|
|
||||||
dialog.n_dialogs[save_d_id].d_sort = fields.d_sort
|
|
||||||
end
|
|
||||||
|
|
||||||
--Context
|
|
||||||
yl_speak_up.speak_to[pname].d_id = save_d_id
|
|
||||||
|
|
||||||
-- Just in case the NPC vlaues where changed or set
|
|
||||||
dialog.n_id = n_id
|
|
||||||
dialog.n_description = fields.n_description
|
|
||||||
dialog.n_npc = fields.n_npc
|
|
||||||
|
|
||||||
dialog.npc_owner = fields.npc_owner
|
|
||||||
|
|
||||||
return dialog
|
|
||||||
end
|
|
||||||
|
|
||||||
yl_speak_up.delete_dialog = function(n_id, d_id)
|
|
||||||
if d_id == yl_speak_up.text_new_dialog_id then
|
|
||||||
return false
|
|
||||||
end -- We don't delete "New dialog"
|
|
||||||
|
|
||||||
local dialog = yl_speak_up.load_dialog(n_id, false)
|
|
||||||
|
|
||||||
dialog.n_dialogs[d_id] = nil
|
|
||||||
|
|
||||||
yl_speak_up.save_dialog(n_id, dialog)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--###
|
--###
|
||||||
--Formspecs
|
--Formspecs
|
||||||
--###
|
--###
|
||||||
|
|
||||||
-- get formspecs
|
|
||||||
|
|
||||||
-- talk
|
|
||||||
|
|
||||||
-- receive fields
|
|
||||||
|
|
||||||
|
|
||||||
-- talk
|
|
||||||
|
|
||||||
-- helper function
|
-- helper function
|
||||||
-- the option to override next_id and provide a value is needed when a new dialog was
|
-- the option to override next_id and provide a value is needed when a new dialog was
|
||||||
@ -459,247 +316,6 @@ yl_speak_up.sort_keys = function(t, simple)
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- identify multiple results that lead to target dialogs
|
|
||||||
yl_speak_up.check_for_disambigous_results = function(n_id, pname)
|
|
||||||
local errors_found = false
|
|
||||||
-- this is only checked when trying to edit this npc;
|
|
||||||
-- let's stick to check the dialogs of this one without generic dialogs
|
|
||||||
local dialog = yl_speak_up.load_dialog(n_id, false)
|
|
||||||
-- nothing defined yet - nothing to repair
|
|
||||||
if(not(dialog.n_dialogs)) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
-- iterate over all dialogs
|
|
||||||
for d_id, d in pairs(dialog.n_dialogs) do
|
|
||||||
if(d_id and d and d.d_options) then
|
|
||||||
-- iterate over all options
|
|
||||||
for o_id, o in pairs(d.d_options) do
|
|
||||||
if(o_id and o and o.o_results) then
|
|
||||||
local dialog_results = {}
|
|
||||||
-- iterate over all results
|
|
||||||
for r_id, r in pairs(o.o_results) do
|
|
||||||
if(r.r_type == "dialog") then
|
|
||||||
table.insert(dialog_results, r_id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if(#dialog_results>1) then
|
|
||||||
local msg = "ERROR: Dialog "..
|
|
||||||
tostring(d_id)..", option "..tostring(o_id)..
|
|
||||||
", has multiple results of type dialog: "..
|
|
||||||
minetest.serialize(dialog_results)..". Please "..
|
|
||||||
"let someone with npc_master priv fix that first!"
|
|
||||||
yl_speak_up.log_change(pname, n_id, msg, "error")
|
|
||||||
if(pname) then
|
|
||||||
minetest.chat_send_player(pname, msg)
|
|
||||||
end
|
|
||||||
errors_found = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return errors_found
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- returns true if someone is speaking to the NPC
|
|
||||||
yl_speak_up.npc_is_in_conversation = function(n_id)
|
|
||||||
for name, data in pairs(yl_speak_up.speak_to) do
|
|
||||||
if(data and data.n_id and data.n_id == n_id) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- returns a list of players that are in conversation with this NPC
|
|
||||||
yl_speak_up.npc_is_in_conversation_with = function(n_id)
|
|
||||||
local liste = {}
|
|
||||||
for name, data in pairs(yl_speak_up.speak_to) do
|
|
||||||
if(data and data.n_id and data.n_id == n_id) then
|
|
||||||
table.insert(liste, name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return liste
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Make the NPC talk
|
|
||||||
|
|
||||||
-- assign n_ID
|
|
||||||
-- usually this happens when talking to the NPC for the first time;
|
|
||||||
-- but if you want to you can call this function earlier (on spawn)
|
|
||||||
-- so that logging of spawning with the ID is possible
|
|
||||||
yl_speak_up.initialize_npc = function(self)
|
|
||||||
-- already configured?
|
|
||||||
if(not(self) or (self.yl_speak_up and self.yl_speak_up.id)) then
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
local m_talk = yl_speak_up.talk_after_spawn or true
|
|
||||||
local m_id = yl_speak_up.number_of_npcs + 1
|
|
||||||
yl_speak_up.number_of_npcs = m_id
|
|
||||||
yl_speak_up.modstorage:set_int("amount", m_id)
|
|
||||||
|
|
||||||
self.yl_speak_up = {
|
|
||||||
talk = m_talk,
|
|
||||||
id = m_id,
|
|
||||||
textures = self.textures
|
|
||||||
}
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function yl_speak_up.talk(self, clicker)
|
|
||||||
|
|
||||||
if not clicker and not clicker:is_player() then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if not self then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local id_prefix = "n"
|
|
||||||
-- we are not dealing with an NPC but with a position/block on the map
|
|
||||||
if(self.is_block) then
|
|
||||||
id_prefix = "p"
|
|
||||||
local owner = "- unknown -"
|
|
||||||
local talk_name = "- unknown -"
|
|
||||||
if(self.pos and self.pos and self.pos.x) then
|
|
||||||
local meta = minetest.get_meta(self.pos)
|
|
||||||
if(meta) then
|
|
||||||
owner = meta:get_string("owner") or ""
|
|
||||||
talk_name = meta:get_string("talk_name") or ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.yl_speak_up = {
|
|
||||||
is_block = true,
|
|
||||||
talk = true,
|
|
||||||
id = minetest.pos_to_string(self.pos, 0),
|
|
||||||
textures = {},
|
|
||||||
owner = owner,
|
|
||||||
npc_name = talk_name,
|
|
||||||
object = nil, -- blocks don't have an object
|
|
||||||
}
|
|
||||||
-- TODO: remember somewhere that this block is relevant
|
|
||||||
|
|
||||||
-- initialize the mob if necessary; this happens at the time of first talk, not at spawn time!
|
|
||||||
elseif(not(self.yl_speak_up) or not(self.yl_speak_up.id)) then
|
|
||||||
self = yl_speak_up.initialize_npc(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local npc_id = self.yl_speak_up.id
|
|
||||||
local n_id = id_prefix.."_" .. npc_id
|
|
||||||
|
|
||||||
-- remember whom the npc belongs to (as long as we still have self.owner available for easy access)
|
|
||||||
yl_speak_up.npc_owner[ n_id ] = self.owner
|
|
||||||
|
|
||||||
local pname = clicker:get_player_name()
|
|
||||||
if not self.yl_speak_up or not self.yl_speak_up.talk or self.yl_speak_up.talk~=true then
|
|
||||||
|
|
||||||
local was = "This NPC"
|
|
||||||
if(id_prefix ~= "n") then
|
|
||||||
was = "This block"
|
|
||||||
end
|
|
||||||
-- show a formspec to other players that this NPC is busy
|
|
||||||
if(not(yl_speak_up.may_edit_npc(clicker, n_id))) then
|
|
||||||
-- show a formspec so that the player knows that he may come back later
|
|
||||||
yl_speak_up.show_fs(player, "msg", {input_to = "yl_spaek_up:ignore", formspec =
|
|
||||||
"size[6,2]"..
|
|
||||||
"label[1.2,0.0;"..minetest.formspec_escape((self.yl_speak_up.npc_name or was)..
|
|
||||||
" [muted]").."]"..
|
|
||||||
"label[0.2,0.5;Sorry! I'm currently busy learning new things.]"..
|
|
||||||
"label[0.2,1.0;Please come back later.]"..
|
|
||||||
"button_exit[2.5,1.5;1,0.9;ok;Ok]"})
|
|
||||||
return
|
|
||||||
end
|
|
||||||
-- allow the owner to edit (and subsequently unmute) the npc
|
|
||||||
minetest.chat_send_player(pname, was.." is muted. It will only talk to you.")
|
|
||||||
end
|
|
||||||
|
|
||||||
yl_speak_up.speak_to[pname] = {}
|
|
||||||
yl_speak_up.speak_to[pname].n_id = n_id -- Memorize which player talks to which NPC
|
|
||||||
yl_speak_up.speak_to[pname].textures = self.yl_speak_up.textures
|
|
||||||
yl_speak_up.speak_to[pname].option_index = 1
|
|
||||||
-- the object itself may be needed in load_dialog for adding generic dialogs
|
|
||||||
yl_speak_up.speak_to[pname].obj = self.object
|
|
||||||
-- this makes it a bit easier to access some values later on:
|
|
||||||
yl_speak_up.speak_to[pname]._self = self
|
|
||||||
-- Load the dialog and see what we can do with it
|
|
||||||
-- this inculdes generic dialog parts;
|
|
||||||
yl_speak_up.speak_to[pname].dialog = yl_speak_up.load_dialog(n_id, clicker)
|
|
||||||
|
|
||||||
-- is this player explicitly allowed to edit this npc?
|
|
||||||
if(yl_speak_up.speak_to[pname].dialog
|
|
||||||
and yl_speak_up.speak_to[pname].dialog.n_may_edit
|
|
||||||
and yl_speak_up.speak_to[pname].dialog.n_may_edit[pname]
|
|
||||||
and minetest.check_player_privs(clicker, {npc_talk_owner=true})) then
|
|
||||||
yl_speak_up.speak_to[pname].may_edit_this_npc = true
|
|
||||||
end
|
|
||||||
|
|
||||||
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
||||||
if(not(dialog.trades)) then
|
|
||||||
dialog.trades = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- create a detached inventory for the npc and load its inventory
|
|
||||||
yl_speak_up.load_npc_inventory(id_prefix.."_"..tostring(self.yl_speak_up.id), false, dialog)
|
|
||||||
|
|
||||||
|
|
||||||
-- some NPC may have reset the animation; at least set it to the desired
|
|
||||||
-- value whenever we talk to the NPC
|
|
||||||
if self.yl_speak_up and self.yl_speak_up.animation then
|
|
||||||
self.object:set_animation(self.yl_speak_up.animation)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- maintain a list of existing NPC, but do not force saving
|
|
||||||
yl_speak_up.update_npc_data(self, dialog, false)
|
|
||||||
|
|
||||||
yl_speak_up.show_fs(clicker, "talk", {n_id = n_id})
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- mute the npc; either via the appropriate staff or via talking to him
|
|
||||||
yl_speak_up.set_muted = function(p_name, obj, set_muted)
|
|
||||||
if(not(obj)) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local luaentity = obj:get_luaentity()
|
|
||||||
if(not(luaentity)) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local npc = luaentity.yl_speak_up.id
|
|
||||||
local npc_name = luaentity.yl_speak_up.npc_name
|
|
||||||
-- fallback
|
|
||||||
if(not(npc_name)) then
|
|
||||||
npc_name = npc
|
|
||||||
end
|
|
||||||
if(set_muted and luaentity.yl_speak_up.talk) then
|
|
||||||
-- the npc is willing to talk
|
|
||||||
luaentity.yl_speak_up.talk = false
|
|
||||||
yl_speak_up.update_nametag(luaentity)
|
|
||||||
|
|
||||||
-- minetest.chat_send_player(p_name,"NPC with ID n_"..npc.." will shut up at pos "..
|
|
||||||
-- minetest.pos_to_string(obj:get_pos(),0).." on command of "..p_name)
|
|
||||||
minetest.chat_send_player(p_name, "NPC n_"..tostring(npc).." is now muted and will "..
|
|
||||||
"only talk to those who can edit the NPC.")
|
|
||||||
yl_speak_up.log_change(p_name, "n_"..npc, "muted - NPC stops talking")
|
|
||||||
elseif(not(set_muted) and not(luaentity.yl_speak_up.talk)) then
|
|
||||||
-- mute the npc
|
|
||||||
luaentity.yl_speak_up.talk = true
|
|
||||||
yl_speak_up.update_nametag(luaentity)
|
|
||||||
|
|
||||||
minetest.chat_send_player(p_name, "NPC n_"..tostring(npc).." is no longer muted and "..
|
|
||||||
"will talk with any player who right-clicks the NPC.")
|
|
||||||
-- minetest.chat_send_player(p_name,"NPC with ID n_"..npc.." will resume speech at pos "..
|
|
||||||
-- minetest.pos_to_string(obj:get_pos(),0).." on command of "..p_name)
|
|
||||||
yl_speak_up.log_change(p_name, "n_"..npc, "unmuted - NPC talks again")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- checks if dialog contains d_id and o_id
|
-- checks if dialog contains d_id and o_id
|
||||||
yl_speak_up.check_if_dialog_has_option = function(dialog, d_id, o_id)
|
yl_speak_up.check_if_dialog_has_option = function(dialog, d_id, o_id)
|
||||||
return (dialog and d_id and o_id
|
return (dialog and d_id and o_id
|
||||||
@ -717,22 +333,6 @@ yl_speak_up.check_if_dialog_exists = function(dialog, d_id)
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- has the player the right privs?
|
|
||||||
-- this is used for the "I am your master" talk based configuration; *NOT* for the staffs!
|
|
||||||
yl_speak_up.may_edit_npc = function(player, n_id)
|
|
||||||
if(not(player)) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local pname = player:get_player_name()
|
|
||||||
-- is the player allowed to edit this npc?
|
|
||||||
return ((yl_speak_up.npc_owner[ n_id ] == pname
|
|
||||||
and minetest.check_player_privs(player, {npc_talk_owner=true}))
|
|
||||||
or minetest.check_player_privs(player, {npc_talk_master=true})
|
|
||||||
or minetest.check_player_privs(player, {npc_master=true})
|
|
||||||
or (yl_speak_up.speak_to[pname]
|
|
||||||
and yl_speak_up.speak_to[pname].may_edit_this_npc))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
yl_speak_up.is_special_dialog = function(d_id)
|
yl_speak_up.is_special_dialog = function(d_id)
|
||||||
if(not(d_id)) then
|
if(not(d_id)) then
|
112
functions_save_restore_dialogs.lua
Normal file
112
functions_save_restore_dialogs.lua
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
|
||||||
|
--###
|
||||||
|
--Load and Save
|
||||||
|
--###
|
||||||
|
|
||||||
|
local function save_path(n_id)
|
||||||
|
return yl_speak_up.worldpath .. yl_speak_up.path .. DIR_DELIM .. n_id .. ".json"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- we can't really log changes here in this function because we don't know *what* has been changed
|
||||||
|
yl_speak_up.save_dialog = function(n_id, dialog)
|
||||||
|
if type(n_id) ~= "string" or type(dialog) ~= "table" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local p = save_path(n_id)
|
||||||
|
-- save some data (in particular usage of quest variables)
|
||||||
|
yl_speak_up.update_stored_npc_data(n_id, dialog)
|
||||||
|
-- make sure we never store any automaticly added generic dialogs
|
||||||
|
dialog = yl_speak_up.strip_generic_dialogs(dialog)
|
||||||
|
-- never store d_dynamic dialogs
|
||||||
|
if(dialog.n_dialogs and dialog.n_dialogs["d_dynamic"]) then
|
||||||
|
dialog.n_dialogs["d_dynamic"] = nil
|
||||||
|
end
|
||||||
|
local content = minetest.write_json(dialog)
|
||||||
|
return minetest.safe_file_write(p, content)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- if a player is supplied: include generic dialogs
|
||||||
|
yl_speak_up.load_dialog = function(n_id, player) -- returns the saved dialog
|
||||||
|
local p = save_path(n_id)
|
||||||
|
|
||||||
|
-- note: add_generic_dialogs will also add an empty d_dynamic dialog
|
||||||
|
local file, err = io.open(p, "r")
|
||||||
|
if err then
|
||||||
|
return yl_speak_up.add_generic_dialogs({}, n_id, player)
|
||||||
|
end
|
||||||
|
io.input(file)
|
||||||
|
local content = io.read()
|
||||||
|
local dialog = minetest.parse_json(content)
|
||||||
|
io.close(file)
|
||||||
|
|
||||||
|
if type(dialog) ~= "table" then
|
||||||
|
dialog = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
return yl_speak_up.add_generic_dialogs(dialog, n_id, player)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- this deletes the dialog with id d_id from the npc n_id's dialogs;
|
||||||
|
-- it loads the dialogs from the npc's savefile, deletes dialog d_id,
|
||||||
|
-- and then saves the dialogs back to the npc's savefile in order to
|
||||||
|
-- keep things consistent
|
||||||
|
yl_speak_up.delete_dialog = function(n_id, d_id)
|
||||||
|
if d_id == yl_speak_up.text_new_dialog_id then
|
||||||
|
return false
|
||||||
|
end -- We don't delete "New dialog"
|
||||||
|
|
||||||
|
local dialog = yl_speak_up.load_dialog(n_id, false)
|
||||||
|
|
||||||
|
dialog.n_dialogs[d_id] = nil
|
||||||
|
|
||||||
|
yl_speak_up.save_dialog(n_id, dialog)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- used by staff and input_inital_config
|
||||||
|
yl_speak_up.fields_to_dialog = function(pname, fields)
|
||||||
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
||||||
|
local dialog = yl_speak_up.load_dialog(n_id, false)
|
||||||
|
local save_d_id = ""
|
||||||
|
|
||||||
|
if next(dialog) == nil then -- No file found. Let's create the basic values
|
||||||
|
dialog = {}
|
||||||
|
dialog.n_dialogs = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if dialog.n_dialogs == nil or next(dialog.n_dialogs) == nil then --No dialogs found. Let's make a table
|
||||||
|
dialog.n_dialogs = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if fields.d_text ~= "" then -- If there is dialog text, then save new or old dialog
|
||||||
|
if fields.d_id == yl_speak_up.text_new_dialog_id then --New dialog --
|
||||||
|
-- Find highest d_id and increase by 1
|
||||||
|
save_d_id = "d_" .. yl_speak_up.find_next_id(dialog.n_dialogs)
|
||||||
|
|
||||||
|
-- Initialize empty dialog
|
||||||
|
dialog.n_dialogs[save_d_id] = {}
|
||||||
|
else -- Already existing dialog
|
||||||
|
save_d_id = fields.d_id
|
||||||
|
end
|
||||||
|
-- Change dialog
|
||||||
|
dialog.n_dialogs[save_d_id].d_id = save_d_id
|
||||||
|
dialog.n_dialogs[save_d_id].d_type = "text"
|
||||||
|
dialog.n_dialogs[save_d_id].d_text = fields.d_text
|
||||||
|
dialog.n_dialogs[save_d_id].d_sort = fields.d_sort
|
||||||
|
end
|
||||||
|
|
||||||
|
--Context
|
||||||
|
yl_speak_up.speak_to[pname].d_id = save_d_id
|
||||||
|
|
||||||
|
-- Just in case the NPC vlaues where changed or set
|
||||||
|
dialog.n_id = n_id
|
||||||
|
dialog.n_description = fields.n_description
|
||||||
|
dialog.n_npc = fields.n_npc
|
||||||
|
|
||||||
|
dialog.npc_owner = fields.npc_owner
|
||||||
|
|
||||||
|
return dialog
|
||||||
|
end
|
||||||
|
|
314
functions_talk.lua
Normal file
314
functions_talk.lua
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
|
||||||
|
--###
|
||||||
|
-- Init
|
||||||
|
--###
|
||||||
|
|
||||||
|
-- self (the npc as such) is rarely passed on to any functions; in order to be able to check if
|
||||||
|
-- the player really owns the npc, we need to have that data available;
|
||||||
|
-- format: yl_speak_up.npc_owner[ npc_id ] = owner_name
|
||||||
|
yl_speak_up.npc_owner = {}
|
||||||
|
|
||||||
|
-- store the current trade between player and npc in case it gets edited in the meantime
|
||||||
|
yl_speak_up.trade = {}
|
||||||
|
|
||||||
|
-- store what the player last entered in an text_input action
|
||||||
|
yl_speak_up.last_text_input = {}
|
||||||
|
|
||||||
|
|
||||||
|
--###
|
||||||
|
-- Debug
|
||||||
|
--###
|
||||||
|
|
||||||
|
yl_speak_up.debug = true
|
||||||
|
|
||||||
|
|
||||||
|
---###
|
||||||
|
-- general formpsec
|
||||||
|
---###
|
||||||
|
yl_speak_up.get_error_message = function()
|
||||||
|
local formspec = {
|
||||||
|
"size[13.4,8.5]",
|
||||||
|
"bgcolor[#FF0000]",
|
||||||
|
"label[0.2,0.35;Please save a NPC file first]",
|
||||||
|
"button_exit[0.2,7.7;3,0.75;button_back;Back]"
|
||||||
|
}
|
||||||
|
|
||||||
|
return table.concat(formspec, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
---###
|
||||||
|
-- player related
|
||||||
|
---###
|
||||||
|
|
||||||
|
yl_speak_up.reset_vars_for_player = function(pname, reset_fs_version)
|
||||||
|
yl_speak_up.speak_to[pname] = nil
|
||||||
|
yl_speak_up.last_text_input[pname] = nil
|
||||||
|
-- when just stopping editing: don't reset the fs_version
|
||||||
|
if(reset_fs_version) then
|
||||||
|
yl_speak_up.fs_version[pname] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---###
|
||||||
|
-- player and npc related
|
||||||
|
---###
|
||||||
|
|
||||||
|
-- identify multiple results that lead to target dialogs
|
||||||
|
yl_speak_up.check_for_disambigous_results = function(n_id, pname)
|
||||||
|
local errors_found = false
|
||||||
|
-- this is only checked when trying to edit this npc;
|
||||||
|
-- let's stick to check the dialogs of this one without generic dialogs
|
||||||
|
local dialog = yl_speak_up.load_dialog(n_id, false)
|
||||||
|
-- nothing defined yet - nothing to repair
|
||||||
|
if(not(dialog.n_dialogs)) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- iterate over all dialogs
|
||||||
|
for d_id, d in pairs(dialog.n_dialogs) do
|
||||||
|
if(d_id and d and d.d_options) then
|
||||||
|
-- iterate over all options
|
||||||
|
for o_id, o in pairs(d.d_options) do
|
||||||
|
if(o_id and o and o.o_results) then
|
||||||
|
local dialog_results = {}
|
||||||
|
-- iterate over all results
|
||||||
|
for r_id, r in pairs(o.o_results) do
|
||||||
|
if(r.r_type == "dialog") then
|
||||||
|
table.insert(dialog_results, r_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if(#dialog_results>1) then
|
||||||
|
local msg = "ERROR: Dialog "..
|
||||||
|
tostring(d_id)..", option "..tostring(o_id)..
|
||||||
|
", has multiple results of type dialog: "..
|
||||||
|
minetest.serialize(dialog_results)..". Please "..
|
||||||
|
"let someone with npc_master priv fix that first!"
|
||||||
|
yl_speak_up.log_change(pname, n_id, msg, "error")
|
||||||
|
if(pname) then
|
||||||
|
minetest.chat_send_player(pname, msg)
|
||||||
|
end
|
||||||
|
errors_found = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return errors_found
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- returns true if someone is speaking to the NPC
|
||||||
|
yl_speak_up.npc_is_in_conversation = function(n_id)
|
||||||
|
for name, data in pairs(yl_speak_up.speak_to) do
|
||||||
|
if(data and data.n_id and data.n_id == n_id) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- returns a list of players that are in conversation with this NPC
|
||||||
|
yl_speak_up.npc_is_in_conversation_with = function(n_id)
|
||||||
|
local liste = {}
|
||||||
|
for name, data in pairs(yl_speak_up.speak_to) do
|
||||||
|
if(data and data.n_id and data.n_id == n_id) then
|
||||||
|
table.insert(liste, name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return liste
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Make the NPC talk
|
||||||
|
|
||||||
|
-- assign n_ID
|
||||||
|
-- usually this happens when talking to the NPC for the first time;
|
||||||
|
-- but if you want to you can call this function earlier (on spawn)
|
||||||
|
-- so that logging of spawning with the ID is possible
|
||||||
|
yl_speak_up.initialize_npc = function(self)
|
||||||
|
-- already configured?
|
||||||
|
if(not(self) or (self.yl_speak_up and self.yl_speak_up.id)) then
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
local m_talk = yl_speak_up.talk_after_spawn or true
|
||||||
|
local m_id = yl_speak_up.number_of_npcs + 1
|
||||||
|
yl_speak_up.number_of_npcs = m_id
|
||||||
|
yl_speak_up.modstorage:set_int("amount", m_id)
|
||||||
|
|
||||||
|
self.yl_speak_up = {
|
||||||
|
talk = m_talk,
|
||||||
|
id = m_id,
|
||||||
|
textures = self.textures
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function yl_speak_up.talk(self, clicker)
|
||||||
|
|
||||||
|
if not clicker and not clicker:is_player() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not self then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local id_prefix = "n"
|
||||||
|
-- we are not dealing with an NPC but with a position/block on the map
|
||||||
|
if(self.is_block) then
|
||||||
|
id_prefix = "p"
|
||||||
|
local owner = "- unknown -"
|
||||||
|
local talk_name = "- unknown -"
|
||||||
|
if(self.pos and self.pos and self.pos.x) then
|
||||||
|
local meta = minetest.get_meta(self.pos)
|
||||||
|
if(meta) then
|
||||||
|
owner = meta:get_string("owner") or ""
|
||||||
|
talk_name = meta:get_string("talk_name") or ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.yl_speak_up = {
|
||||||
|
is_block = true,
|
||||||
|
talk = true,
|
||||||
|
id = minetest.pos_to_string(self.pos, 0),
|
||||||
|
textures = {},
|
||||||
|
owner = owner,
|
||||||
|
npc_name = talk_name,
|
||||||
|
object = nil, -- blocks don't have an object
|
||||||
|
}
|
||||||
|
-- TODO: remember somewhere that this block is relevant
|
||||||
|
|
||||||
|
-- initialize the mob if necessary; this happens at the time of first talk, not at spawn time!
|
||||||
|
elseif(not(self.yl_speak_up) or not(self.yl_speak_up.id)) then
|
||||||
|
self = yl_speak_up.initialize_npc(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local npc_id = self.yl_speak_up.id
|
||||||
|
local n_id = id_prefix.."_" .. npc_id
|
||||||
|
|
||||||
|
-- remember whom the npc belongs to (as long as we still have self.owner available for easy access)
|
||||||
|
yl_speak_up.npc_owner[ n_id ] = self.owner
|
||||||
|
|
||||||
|
local pname = clicker:get_player_name()
|
||||||
|
if not self.yl_speak_up or not self.yl_speak_up.talk or self.yl_speak_up.talk~=true then
|
||||||
|
|
||||||
|
local was = "This NPC"
|
||||||
|
if(id_prefix ~= "n") then
|
||||||
|
was = "This block"
|
||||||
|
end
|
||||||
|
-- show a formspec to other players that this NPC is busy
|
||||||
|
if(not(yl_speak_up.may_edit_npc(clicker, n_id))) then
|
||||||
|
-- show a formspec so that the player knows that he may come back later
|
||||||
|
yl_speak_up.show_fs(player, "msg", {input_to = "yl_spaek_up:ignore", formspec =
|
||||||
|
"size[6,2]"..
|
||||||
|
"label[1.2,0.0;"..minetest.formspec_escape((self.yl_speak_up.npc_name or was)..
|
||||||
|
" [muted]").."]"..
|
||||||
|
"label[0.2,0.5;Sorry! I'm currently busy learning new things.]"..
|
||||||
|
"label[0.2,1.0;Please come back later.]"..
|
||||||
|
"button_exit[2.5,1.5;1,0.9;ok;Ok]"})
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- allow the owner to edit (and subsequently unmute) the npc
|
||||||
|
minetest.chat_send_player(pname, was.." is muted. It will only talk to you.")
|
||||||
|
end
|
||||||
|
|
||||||
|
yl_speak_up.speak_to[pname] = {}
|
||||||
|
yl_speak_up.speak_to[pname].n_id = n_id -- Memorize which player talks to which NPC
|
||||||
|
yl_speak_up.speak_to[pname].textures = self.yl_speak_up.textures
|
||||||
|
yl_speak_up.speak_to[pname].option_index = 1
|
||||||
|
-- the object itself may be needed in load_dialog for adding generic dialogs
|
||||||
|
yl_speak_up.speak_to[pname].obj = self.object
|
||||||
|
-- this makes it a bit easier to access some values later on:
|
||||||
|
yl_speak_up.speak_to[pname]._self = self
|
||||||
|
-- Load the dialog and see what we can do with it
|
||||||
|
-- this inculdes generic dialog parts;
|
||||||
|
yl_speak_up.speak_to[pname].dialog = yl_speak_up.load_dialog(n_id, clicker)
|
||||||
|
|
||||||
|
-- is this player explicitly allowed to edit this npc?
|
||||||
|
if(yl_speak_up.speak_to[pname].dialog
|
||||||
|
and yl_speak_up.speak_to[pname].dialog.n_may_edit
|
||||||
|
and yl_speak_up.speak_to[pname].dialog.n_may_edit[pname]
|
||||||
|
and minetest.check_player_privs(clicker, {npc_talk_owner=true})) then
|
||||||
|
yl_speak_up.speak_to[pname].may_edit_this_npc = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
||||||
|
if(not(dialog.trades)) then
|
||||||
|
dialog.trades = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- create a detached inventory for the npc and load its inventory
|
||||||
|
yl_speak_up.load_npc_inventory(id_prefix.."_"..tostring(self.yl_speak_up.id), false, dialog)
|
||||||
|
|
||||||
|
|
||||||
|
-- some NPC may have reset the animation; at least set it to the desired
|
||||||
|
-- value whenever we talk to the NPC
|
||||||
|
if self.yl_speak_up and self.yl_speak_up.animation then
|
||||||
|
self.object:set_animation(self.yl_speak_up.animation)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- maintain a list of existing NPC, but do not force saving
|
||||||
|
yl_speak_up.update_npc_data(self, dialog, false)
|
||||||
|
|
||||||
|
yl_speak_up.show_fs(clicker, "talk", {n_id = n_id})
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- mute the npc; either via the appropriate staff or via talking to him
|
||||||
|
yl_speak_up.set_muted = function(p_name, obj, set_muted)
|
||||||
|
if(not(obj)) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local luaentity = obj:get_luaentity()
|
||||||
|
if(not(luaentity)) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local npc = luaentity.yl_speak_up.id
|
||||||
|
local npc_name = luaentity.yl_speak_up.npc_name
|
||||||
|
-- fallback
|
||||||
|
if(not(npc_name)) then
|
||||||
|
npc_name = npc
|
||||||
|
end
|
||||||
|
if(set_muted and luaentity.yl_speak_up.talk) then
|
||||||
|
-- the npc is willing to talk
|
||||||
|
luaentity.yl_speak_up.talk = false
|
||||||
|
yl_speak_up.update_nametag(luaentity)
|
||||||
|
|
||||||
|
-- minetest.chat_send_player(p_name,"NPC with ID n_"..npc.." will shut up at pos "..
|
||||||
|
-- minetest.pos_to_string(obj:get_pos(),0).." on command of "..p_name)
|
||||||
|
minetest.chat_send_player(p_name, "NPC n_"..tostring(npc).." is now muted and will "..
|
||||||
|
"only talk to those who can edit the NPC.")
|
||||||
|
yl_speak_up.log_change(p_name, "n_"..npc, "muted - NPC stops talking")
|
||||||
|
elseif(not(set_muted) and not(luaentity.yl_speak_up.talk)) then
|
||||||
|
-- mute the npc
|
||||||
|
luaentity.yl_speak_up.talk = true
|
||||||
|
yl_speak_up.update_nametag(luaentity)
|
||||||
|
|
||||||
|
minetest.chat_send_player(p_name, "NPC n_"..tostring(npc).." is no longer muted and "..
|
||||||
|
"will talk with any player who right-clicks the NPC.")
|
||||||
|
-- minetest.chat_send_player(p_name,"NPC with ID n_"..npc.." will resume speech at pos "..
|
||||||
|
-- minetest.pos_to_string(obj:get_pos(),0).." on command of "..p_name)
|
||||||
|
yl_speak_up.log_change(p_name, "n_"..npc, "unmuted - NPC talks again")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- has the player the right privs?
|
||||||
|
-- this is used for the "I am your master" talk based configuration; *NOT* for the staffs!
|
||||||
|
yl_speak_up.may_edit_npc = function(player, n_id)
|
||||||
|
if(not(player)) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
local pname = player:get_player_name()
|
||||||
|
-- is the player allowed to edit this npc?
|
||||||
|
return ((yl_speak_up.npc_owner[ n_id ] == pname
|
||||||
|
and minetest.check_player_privs(player, {npc_talk_owner=true}))
|
||||||
|
or minetest.check_player_privs(player, {npc_talk_master=true})
|
||||||
|
or minetest.check_player_privs(player, {npc_master=true})
|
||||||
|
or (yl_speak_up.speak_to[pname]
|
||||||
|
and yl_speak_up.speak_to[pname].may_edit_this_npc))
|
||||||
|
end
|
||||||
|
|
4
init.lua
4
init.lua
@ -212,7 +212,9 @@ yl_speak_up.reload = function(modpath, log_entry)
|
|||||||
-- some generic dialogs
|
-- some generic dialogs
|
||||||
dofile(modpath .. "api/api_properties.lua")
|
dofile(modpath .. "api/api_properties.lua")
|
||||||
-- the main functionality of the mod
|
-- the main functionality of the mod
|
||||||
dofile(modpath .. "functions.lua")
|
dofile(modpath .. "functions_dialogs.lua")
|
||||||
|
dofile(modpath .. "functions_save_restore_dialogs.lua")
|
||||||
|
dofile(modpath .. "functions_talk.lua")
|
||||||
-- implementation of the chat commands registered in register_once.lua:
|
-- implementation of the chat commands registered in register_once.lua:
|
||||||
dofile(modpath .. "chat_commands.lua")
|
dofile(modpath .. "chat_commands.lua")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user