diff --git a/functions.lua b/functions_dialogs.lua similarity index 50% rename from functions.lua rename to functions_dialogs.lua index a8af983..68576a7 100644 --- a/functions.lua +++ b/functions_dialogs.lua @@ -1,33 +1,12 @@ ---### --- 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 = {} - -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 - +-- +-- 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. +-- +-- As this mod is about this "dialogs" data structure and its editing, this +-- isn't the only place in this mod where the data structure is accessed +-- and/or manipulated. This here just contains some common functions. +-- --### -- Helpers --### @@ -39,20 +18,6 @@ yl_speak_up.get_number_from_id = function(any_id) return string.split(any_id, "_")[2] 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) local start_id = 1 @@ -88,120 +53,12 @@ yl_speak_up.sanitize_sort = function(options, value) return retval 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 --### --- get formspecs - --- talk - --- receive fields - - --- talk -- helper function -- 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 --- 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 yl_speak_up.check_if_dialog_has_option = function(dialog, d_id, 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 --- 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) if(not(d_id)) then diff --git a/functions_save_restore_dialogs.lua b/functions_save_restore_dialogs.lua new file mode 100644 index 0000000..363087e --- /dev/null +++ b/functions_save_restore_dialogs.lua @@ -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 + diff --git a/functions_talk.lua b/functions_talk.lua new file mode 100644 index 0000000..8fdb551 --- /dev/null +++ b/functions_talk.lua @@ -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 + diff --git a/init.lua b/init.lua index eeed388..3165626 100644 --- a/init.lua +++ b/init.lua @@ -212,7 +212,9 @@ yl_speak_up.reload = function(modpath, log_entry) -- some generic dialogs dofile(modpath .. "api/api_properties.lua") -- 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: dofile(modpath .. "chat_commands.lua")