3862 lines
153 KiB
Lua
3862 lines
153 KiB
Lua
-- if player has npc_talk_owner priv AND is owner of this particular npc:
|
|
-- chat option: "I am your owner. I have new orders for you.
|
|
-- -> enters edit mode
|
|
-- when edit_mode has been enabled, the following chat options are added to the options:
|
|
-- chat option: "Add new answer/option to this dialog."
|
|
-- -> adds a new aswer/option
|
|
-- chat option: "That was all. I'm finished with giving you new orders. Remember them!"
|
|
-- -> ends edit mode
|
|
--
|
|
|
|
--###
|
|
-- Init
|
|
--###
|
|
|
|
-- store if the player is editing a particular NPC; format: yl_speak_up.edit_mode[pname] = npc_id
|
|
yl_speak_up.edit_mode = {}
|
|
|
|
-- changes applied in edit_mode are applied immediately - but not immediately stored to disk
|
|
-- (this gives the players a chance to back off in case of unwanted changes)
|
|
yl_speak_up.npc_was_changed = {}
|
|
|
|
-- 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 = {}
|
|
|
|
function yl_speak_up.init_mob_table()
|
|
return false
|
|
end
|
|
|
|
minetest.register_on_leaveplayer(
|
|
function(player)
|
|
yl_speak_up.speak_to[player:get_player_name()] = nil
|
|
yl_speak_up.edit_mode[player:get_player_name()] = nil
|
|
end
|
|
)
|
|
|
|
minetest.register_on_joinplayer(
|
|
function(player)
|
|
yl_speak_up.speak_to[player:get_player_name()] = nil
|
|
yl_speak_up.edit_mode[player:get_player_name()] = nil
|
|
end
|
|
)
|
|
|
|
--###
|
|
-- Debug
|
|
--###
|
|
|
|
yl_speak_up.debug = true
|
|
|
|
local function say(text)
|
|
if yl_speak_up.debug then
|
|
--minetest.chat_send_all(text)
|
|
minetest.log("action", "[MOD] yl_speak_up: " .. text)
|
|
end
|
|
end
|
|
|
|
--###
|
|
-- Helpers
|
|
--###
|
|
|
|
local function get_prerequisite_types()
|
|
--local t_gpt = {"item", "quest", "function", "auto", "delete"}
|
|
local t_gpt = {"item", "function", "delete"}
|
|
local s_gpt = ""
|
|
for _, v in pairs(t_gpt) do
|
|
s_gpt = s_gpt .. v .. ","
|
|
end
|
|
|
|
s_gpt = s_gpt:sub(1, -2) -- cut last comma
|
|
|
|
return s_gpt, t_gpt
|
|
end
|
|
|
|
local function get_result_types()
|
|
--local t_grt = {"dialog", "give_item", "quest", "function", "auto", "delete"}
|
|
local t_grt = {"dialog","give_item","take_item","move","function","trade","delete"}
|
|
local s_grt = ""
|
|
for _, v in pairs(t_grt) do
|
|
s_grt = s_grt .. v .. ","
|
|
end
|
|
|
|
s_grt = s_grt:sub(1, -2) -- cut last comma
|
|
|
|
return s_grt, t_grt
|
|
end
|
|
|
|
local function get_number_from_id(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
|
|
|
|
local function get_error_message()
|
|
local formspec = {
|
|
"formspec_version[3]",
|
|
"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
|
|
|
|
local function find_next_id(t)
|
|
local start_id = 1
|
|
|
|
if t == nil then
|
|
return start_id
|
|
end
|
|
|
|
local keynum = 1
|
|
for k, _ in pairs(t) do
|
|
local keynum = tonumber(get_number_from_id(k))
|
|
if keynum >= start_id then
|
|
start_id = keynum + 1
|
|
end
|
|
end
|
|
return start_id
|
|
end
|
|
|
|
local function sanitize_sort(options, value)
|
|
local retval = value
|
|
|
|
if value == "" or value == nil or tonumber(value) == nil then
|
|
local temp = 0
|
|
for k, v in pairs(options) do
|
|
if v.o_sort ~= nil then
|
|
if tonumber(v.o_sort) > temp then
|
|
temp = tonumber(v.o_sort)
|
|
end
|
|
end
|
|
end
|
|
retval = tostring(temp + 1)
|
|
end
|
|
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)
|
|
local content = minetest.write_json(dialog)
|
|
return minetest.safe_file_write(p, content)
|
|
end
|
|
|
|
|
|
local function load_dialog(n_id) -- returns the saved dialog
|
|
local p = save_path(n_id)
|
|
|
|
local file, err = io.open(p, "r")
|
|
if err then
|
|
return {}
|
|
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 dialog
|
|
end
|
|
|
|
local function fields_to_dialog(pname, fields)
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local dialog = load_dialog(n_id)
|
|
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_" .. 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
|
|
|
|
local function options_to_dialog(pname)
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local d_id = yl_speak_up.speak_to[pname].d_id
|
|
local o_id = yl_speak_up.speak_to[pname].o_id
|
|
local p_id = yl_speak_up.speak_to[pname].p_id
|
|
local r_id = yl_speak_up.speak_to[pname].r_id
|
|
|
|
if yl_speak_up.speak_to[pname].d_text then
|
|
dialog.n_dialogs[d_id].d_text = yl_speak_up.speak_to[pname].d_text
|
|
end
|
|
|
|
--Find the o_id to save to
|
|
local future_o_id = ""
|
|
if yl_speak_up.speak_to[pname].o_id ~= nil and yl_speak_up.speak_to[pname].o_id ~= yl_speak_up.text_new_option_id then
|
|
future_o_id = yl_speak_up.speak_to[pname].o_id
|
|
else
|
|
future_o_id = "o_" .. find_next_id(dialog.n_dialogs[d_id].d_options)
|
|
|
|
if dialog.n_dialogs[d_id].d_options == nil then
|
|
dialog.n_dialogs[d_id].d_options = {}
|
|
end
|
|
|
|
dialog.n_dialogs[d_id].d_options[future_o_id] = {}
|
|
end
|
|
|
|
--Find the p_id to save to
|
|
local future_p_id = ""
|
|
if
|
|
yl_speak_up.speak_to[pname].p_id ~= nil and
|
|
yl_speak_up.speak_to[pname].p_id ~= yl_speak_up.text_new_prerequisite_id
|
|
then
|
|
future_p_id = yl_speak_up.speak_to[pname].p_id
|
|
else
|
|
future_p_id = "p_" .. find_next_id(dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites)
|
|
if future_p_id == "p_1" then
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites = {}
|
|
end
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites[future_p_id] = {}
|
|
end
|
|
|
|
--Find the r_id to save to
|
|
local future_r_id = ""
|
|
if yl_speak_up.speak_to[pname].r_id ~= nil and yl_speak_up.speak_to[pname].r_id ~= yl_speak_up.text_new_result_id then
|
|
future_r_id = yl_speak_up.speak_to[pname].r_id
|
|
else
|
|
future_r_id = yl_speak_up.add_new_result(dialog, d_id, future_o_id)
|
|
end
|
|
|
|
--Edit options
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_id = future_o_id
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_hide_when_prerequisites_not_met =
|
|
yl_speak_up.speak_to[pname].o_hide or "false"
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_grey_when_prerequisites_not_met =
|
|
yl_speak_up.speak_to[pname].o_grey or "false"
|
|
local s = sanitize_sort(dialog.n_dialogs[d_id].d_options, yl_speak_up.speak_to[pname].o_sort)
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_sort = s
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_text_when_prerequisites_not_met =
|
|
yl_speak_up.speak_to[pname].o_not_met or ""
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_text_when_prerequisites_met =
|
|
yl_speak_up.speak_to[pname].o_met or ""
|
|
|
|
--Edit prerequisites
|
|
|
|
--Do we delete the prerequisite?
|
|
if
|
|
yl_speak_up.speak_to[pname].p_type == "delete" and yl_speak_up.speak_to[pname].p_id ~= nil and
|
|
yl_speak_up.speak_to[pname].p_id ~= yl_speak_up.text_new_prerequisite_id
|
|
then
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites[future_p_id] = nil
|
|
else
|
|
if yl_speak_up.speak_to[pname].p_value ~= nil and yl_speak_up.speak_to[pname].p_value ~= "" then
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites[future_p_id].p_id = future_p_id
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites[future_p_id].p_type =
|
|
yl_speak_up.speak_to[pname].p_type
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_prerequisites[future_p_id].p_value =
|
|
yl_speak_up.speak_to[pname].p_value
|
|
end
|
|
end
|
|
|
|
--Do we delete the result?
|
|
if
|
|
yl_speak_up.speak_to[pname].r_type == "delete" and yl_speak_up.speak_to[pname].r_id ~= nil and
|
|
yl_speak_up.speak_to[pname].r_id ~= yl_speak_up.text_new_result_id
|
|
then
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_results[future_r_id] = nil
|
|
else
|
|
if yl_speak_up.speak_to[pname].r_value ~= nil and yl_speak_up.speak_to[pname].r_value ~= "" then
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_results[future_r_id].r_id = future_r_id
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_results[future_r_id].r_type =
|
|
yl_speak_up.speak_to[pname].r_type
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_results[future_r_id].r_value =
|
|
yl_speak_up.speak_to[pname].r_value
|
|
end
|
|
end
|
|
|
|
return dialog
|
|
end
|
|
|
|
local function delete_dialog(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 = load_dialog(n_id)
|
|
|
|
dialog.n_dialogs[d_id] = nil
|
|
|
|
yl_speak_up.save_dialog(n_id, dialog)
|
|
end
|
|
|
|
local function delete_option(n_id, d_id, o_id)
|
|
if d_id == yl_speak_up.text_new_dialog_id then
|
|
return false
|
|
end -- We don't delete "New dialog" Also, something might have gone wrong here.
|
|
if o_id == yl_speak_up.text_new_option_id then
|
|
return false
|
|
end -- We don't delete "New option"
|
|
|
|
local dialog = load_dialog(n_id)
|
|
|
|
dialog.n_dialogs[d_id].d_options[o_id] = nil
|
|
|
|
yl_speak_up.save_dialog(n_id, dialog)
|
|
end
|
|
|
|
|
|
local function calculate_displayable_options(pname, d_options)
|
|
-- Let's go through all the options and see if we need to display them to the user
|
|
|
|
local retval = {}
|
|
|
|
local player = minetest.get_player_by_name(pname)
|
|
|
|
if d_options == nil then
|
|
return {}
|
|
end
|
|
|
|
for o_k, o_v in pairs(d_options) do
|
|
--minetest.chat_send_all("#####"..o_k.."#####")
|
|
local o_p_met = true
|
|
if o_v.o_prerequisites == nil then
|
|
--minetest.chat_send_all("display this because no prereq:"..dump(o_v))
|
|
else
|
|
--minetest.chat_send_all("prereqs exists")
|
|
if next(o_v.o_prerequisites) == nil then
|
|
--minetest.chat_send_all("prereqs exist, but empty")
|
|
else
|
|
--minetest.chat_send_all("prereqs exists and not empty")
|
|
--minetest.chat_send_all("if all prereqs are met, then we can display the option")
|
|
local p_met = {}
|
|
for p_k, p_v in pairs(o_v.o_prerequisites) do
|
|
--minetest.chat_send_all("this is in a single prereq: "..dump(p_v))
|
|
p_met[p_k] = false
|
|
local p_id = p_v.p_id
|
|
if p_v.p_type == "item" then
|
|
--minetest.chat_send_all("item! Does the player have this thing?")
|
|
if player:get_inventory():contains_item("main", p_v.p_value) then
|
|
--minetest.chat_send_all("found item:"..p_v.p_value)
|
|
p_met[p_k] = true
|
|
end
|
|
end
|
|
if p_v.p_type == "quest" then
|
|
--minetest.chat_send_all("quest! let's call the quest api?")
|
|
p_met[p_k] = false
|
|
end
|
|
if p_v.p_type == "function" then
|
|
|
|
local code = p_v.p_value
|
|
if code:byte(1) == 27 then
|
|
local obj = yl_speak_up.speak_to[pname].obj
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local npc = get_number_from_id(n_id)
|
|
if obj:get_luaentity() and tonumber(npc) then
|
|
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not compile the content of "..p_id.." :"..dump(code) .. " because of illegal bytecode for player "..pname)
|
|
else
|
|
minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not compile the content of "..p_id.." :"..dump(code) .. " for player unknown because of illegal bytecode")
|
|
end
|
|
end
|
|
|
|
local f, msg = loadstring("return function(playername) " .. code .. " end")
|
|
|
|
if not f then
|
|
local obj = yl_speak_up.speak_to[pname].obj
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local npc = get_number_from_id(n_id)
|
|
if obj:get_luaentity() and tonumber(npc) then
|
|
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not compile the content of "..p_id.." :"..dump(code) .. " for player "..pname)
|
|
else
|
|
minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not compile the content of "..p_id.." :"..dump(code) .. " for player unknown")
|
|
end
|
|
else
|
|
|
|
|
|
local func = f()
|
|
|
|
local ok, ret = pcall(func,pname)
|
|
|
|
if not ok then
|
|
local obj = yl_speak_up.speak_to[pname].obj
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local npc = get_number_from_id(n_id)
|
|
if obj:get_luaentity() and tonumber(npc) then
|
|
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not execute the content of "..p_id.." :"..dump(code) .. " for player "..pname)
|
|
else
|
|
minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not execute the content of "..p_id.." :"..dump(code) .. " for player unknown")
|
|
end
|
|
end
|
|
|
|
if type(ret) == "boolean" then
|
|
p_met[p_k] = ret
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
if p_v.p_type == "auto" then
|
|
--minetest.chat_send_all("auto! what shall we do now?")
|
|
p_met[p_k] = true
|
|
end
|
|
end
|
|
-- is there a "false" in the p_met ?
|
|
for m_k, m_v in pairs(p_met) do
|
|
--minetest.chat_send_all(o_k..",m_v="..dump(m_v))
|
|
if m_v == false then
|
|
o_p_met = false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
-- Can we display this option?
|
|
retval[o_k] = o_p_met
|
|
--[[
|
|
if o_p_met == true then
|
|
minetest.chat_send_all("Prereqs say YES")
|
|
retval[o_k] = o_p_met
|
|
else
|
|
minetest.chat_send_all("Prereqs say NOO")
|
|
end
|
|
]]--
|
|
end
|
|
return retval
|
|
end
|
|
|
|
local function calculate_portrait(pname, n_id)
|
|
local tex = yl_speak_up.speak_to[pname].textures
|
|
|
|
local head = ""
|
|
|
|
head = head .. "[combine:8x8:-8,-8=" .. tex[2] .. ":-40,-8=" .. tex[2]
|
|
|
|
return head
|
|
end
|
|
|
|
--###
|
|
--Formspecs
|
|
--###
|
|
|
|
-- get formspecs
|
|
|
|
-- dialog
|
|
|
|
local function get_fs_setdialog(clicker, n_id, d_id)
|
|
local dialog = load_dialog(n_id)
|
|
|
|
local items = yl_speak_up.text_new_dialog_id
|
|
local count = 1
|
|
local d_sort = ""
|
|
local text = ""
|
|
|
|
if next(dialog) == nil then -- file does not exist
|
|
dialog.n_id = n_id
|
|
dialog.n_npc = ""
|
|
dialog.n_description = ""
|
|
elseif dialog.n_dialogs == nil then -- file does exist, but no dialogs are set
|
|
dialog.n_id = n_id
|
|
text = ""
|
|
else -- file exists and there's already content
|
|
local n = 1
|
|
for k, v in pairs(dialog.n_dialogs) do
|
|
n = n + 1
|
|
if k == d_id then
|
|
count = n
|
|
d_sort = v.d_sort or ""
|
|
text = v.d_text or ""
|
|
end
|
|
items = items .. "," .. v.d_id
|
|
end
|
|
|
|
text = minetest.formspec_escape(text)
|
|
end
|
|
|
|
local formspec = {
|
|
"formspec_version[3]",
|
|
"size[13.4,8.5]",
|
|
"label[0.2,0.35;",
|
|
dialog.n_id,
|
|
"]",
|
|
"field[1.3,0.1;3.7,0.5;n_npc;;",
|
|
dialog.n_npc,
|
|
"]",
|
|
"field[5.2,0.1;8,0.5;n_description;;",
|
|
dialog.n_description,
|
|
"]",
|
|
"dropdown[5.2,0.75;5,0.75;d_id;",
|
|
items,
|
|
";",
|
|
count,
|
|
"]",
|
|
-- allow to change the owner of the npc
|
|
"label[0.2,1;Owner]",
|
|
"field[1.3,0.75;3.7,0.5;npc_owner;;",
|
|
(yl_speak_up.npc_owner[ n_id ] or "- nobody -"),
|
|
"]",
|
|
"label[10.9,1;Sort]",
|
|
"field[11.7,0.75;1.5,0.5;d_sort;;",
|
|
d_sort,
|
|
"]",
|
|
"textarea[0.2,1.6;13,6;d_text;;",
|
|
text,
|
|
"]",
|
|
"button_exit[0.2,7.7;3,0.75;button_cancel;Cancel]",
|
|
"button[4,7.7;3,0.75;button_delete;Delete]",
|
|
"button[7.1,7.7;3,0.75;button_option;Options]",
|
|
"button[10.2,7.7;3,0.75;button_save;Save]",
|
|
-- tooltips
|
|
|
|
"tooltip[npc_owner;npc_owner: The name of the owner of the NPC - who can edit dialogs of this NPC if he has the npc_talk_owner priv;#FFFFFF;#000000]",
|
|
"tooltip[n_npc;n_npc: The name of the NPC;#FFFFFF;#000000]",
|
|
"tooltip[n_description;n_description: A description for the NPC;#FFFFFF;#000000]",
|
|
"tooltip[d_sort;d_sort: Make this 0 on your dialog if you want it to be shown the first time a player talks to the NPC\nNegative values are ignored\n;#FFFFFF;#000000]",
|
|
"tooltip[5.2,0.75;5,0.75;d_id: Dialog Id;#FFFFFF;#000000]",
|
|
"tooltip[d_text;d_text: Dialog text. What the NPC says to you;#FFFFFF;#000000]"
|
|
}
|
|
|
|
return table.concat(formspec, "")
|
|
end
|
|
|
|
-- options
|
|
|
|
local function get_fs_optiondialog(player, n_id, d_id, o_id, p_id, r_id)
|
|
local dialog = load_dialog(n_id)
|
|
local pname = player:get_player_name()
|
|
|
|
if next(dialog) == nil or d_id == yl_speak_up.text_new_dialog_id or dialog.n_dialogs == nil then -- file does not exist or the user sent the New dialog
|
|
return get_error_message(n_id)
|
|
end
|
|
|
|
-- default values
|
|
local out = {}
|
|
|
|
-- npc
|
|
out.n_id = n_id
|
|
|
|
-- dialog
|
|
out.d_id = yl_speak_up.text_new_dialog_id
|
|
out.d_text = "Dialog Text"
|
|
|
|
-- option
|
|
out.o_id_items = yl_speak_up.text_new_option_id
|
|
out.o_id_count = 1
|
|
out.o_hide = "false"
|
|
out.o_grey = "false"
|
|
out.o_sort = ""
|
|
out.o_met = "Text when conditions are met"
|
|
out.o_not_met = "Text when conditions are not met"
|
|
|
|
-- prerequisite
|
|
out.p_id_items = yl_speak_up.text_new_prerequisite_id
|
|
out.p_id_count = 1
|
|
out.p_type_items, out.p_type_items_table = get_prerequisite_types()
|
|
out.p_type_count = 1
|
|
out.p_value = ""
|
|
|
|
-- result
|
|
out.r_id_items = yl_speak_up.text_new_result_id
|
|
out.r_id_count = 1
|
|
out.r_type_items = get_result_types()
|
|
out.r_type_count = 1
|
|
out.r_value = ""
|
|
|
|
--dialogs
|
|
for k, v in pairs(dialog.n_dialogs) do
|
|
if k == d_id then
|
|
out.d_id = v.d_id
|
|
out.d_text = minetest.formspec_escape(v.d_text):trim()
|
|
end
|
|
end
|
|
|
|
--options
|
|
if dialog.n_dialogs[d_id].d_options ~= nil then
|
|
local o_n = 1
|
|
for o_k, o_v in pairs(dialog.n_dialogs[d_id].d_options) do
|
|
o_n = o_n + 1
|
|
if o_k == o_id then
|
|
out.o_id_count = o_n
|
|
end
|
|
out.o_id_items = out.o_id_items .. "," .. o_v.o_id
|
|
end
|
|
if dialog.n_dialogs[d_id].d_options[o_id] then
|
|
if dialog.n_dialogs[d_id].d_options[o_id].o_hide_when_prerequisites_not_met then
|
|
out.o_hide = dialog.n_dialogs[d_id].d_options[o_id].o_hide_when_prerequisites_not_met
|
|
end
|
|
if dialog.n_dialogs[d_id].d_options[o_id].o_grey_when_prerequisites_not_met then
|
|
out.o_grey = dialog.n_dialogs[d_id].d_options[o_id].o_grey_when_prerequisites_not_met
|
|
end
|
|
if dialog.n_dialogs[d_id].d_options[o_id].o_sort then
|
|
out.o_sort = dialog.n_dialogs[d_id].d_options[o_id].o_sort
|
|
end
|
|
if dialog.n_dialogs[d_id].d_options[o_id].o_text_when_prerequisites_met then
|
|
out.o_met =
|
|
minetest.formspec_escape(dialog.n_dialogs[d_id].d_options[o_id].o_text_when_prerequisites_met):trim(
|
|
|
|
)
|
|
end
|
|
if dialog.n_dialogs[d_id].d_options[o_id].o_text_when_prerequisites_not_met then
|
|
out.o_not_met =
|
|
minetest.formspec_escape(dialog.n_dialogs[d_id].d_options[o_id].o_text_when_prerequisites_not_met):trim(
|
|
|
|
)
|
|
end
|
|
|
|
-- prerequisite
|
|
|
|
--if dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites ~= nil and p_id == yl_speak_up.text_new_prerequisite_id then -- new prerequisite
|
|
--end
|
|
if dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites ~= nil then
|
|
local p_n = 1
|
|
for p_k, p_v in pairs(dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites) do
|
|
p_n = p_n + 1
|
|
if p_k == p_id then
|
|
out.p_id_count = p_n
|
|
end
|
|
out.p_id_items = out.p_id_items .. "," .. p_v.p_id
|
|
end
|
|
|
|
out.p_type_items, out.p_type_items_table = get_prerequisite_types()
|
|
|
|
if
|
|
dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites[p_id] ~= nil and
|
|
dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites[p_id].p_type ~= nil
|
|
then
|
|
local p_type_n = 1
|
|
for _, p_type_v in pairs(out.p_type_items_table) do
|
|
if p_type_v == dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites[p_id].p_type then
|
|
out.p_type_count = p_type_n
|
|
end
|
|
p_type_n = p_type_n + 1
|
|
end
|
|
|
|
out.p_value =
|
|
minetest.formspec_escape(dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites[p_id].p_value):trim(
|
|
|
|
)
|
|
end
|
|
end
|
|
|
|
-- result
|
|
if dialog.n_dialogs[d_id].d_options[o_id].o_results ~= nil then
|
|
local r_n = 1
|
|
for r_k, r_v in pairs(dialog.n_dialogs[d_id].d_options[o_id].o_results) do
|
|
r_n = r_n + 1
|
|
if r_k == r_id then
|
|
out.r_id_count = r_n
|
|
end
|
|
out.r_id_items = out.r_id_items .. "," .. r_v.r_id
|
|
end
|
|
|
|
out.r_type_items, out.r_type_items_table = get_result_types()
|
|
|
|
if
|
|
dialog.n_dialogs[d_id].d_options[o_id].o_results[r_id] ~= nil and
|
|
dialog.n_dialogs[d_id].d_options[o_id].o_results[r_id].r_type ~= nil
|
|
then
|
|
local r_type_n = 1
|
|
for _, r_type_v in pairs(out.r_type_items_table) do
|
|
if r_type_v == dialog.n_dialogs[d_id].d_options[o_id].o_results[r_id].r_type then
|
|
out.r_type_count = r_type_n
|
|
end
|
|
r_type_n = r_type_n + 1
|
|
end
|
|
|
|
out.r_value =
|
|
minetest.formspec_escape(dialog.n_dialogs[d_id].d_options[o_id].o_results[r_id].r_value):trim()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local formspec = {
|
|
"formspec_version[3]",
|
|
"size[13.4,8.5]",
|
|
-- npc
|
|
"field[-10,-10;4,0.5;n_id;;",
|
|
out.n_id,
|
|
"]",
|
|
-- dialog
|
|
"field[-10,-10;4,0.5;d_id;;",
|
|
out.d_id,
|
|
"]",
|
|
"label[0.2,0.35;",
|
|
out.d_id,
|
|
"]",
|
|
"field[1,0.1;12.2,0.5;d_text;;",
|
|
out.d_text,
|
|
"]",
|
|
-- option
|
|
"dropdown[0.2,0.75;2,0.75;o_id;",
|
|
out.o_id_items,
|
|
";",
|
|
out.o_id_count,
|
|
"]",
|
|
"checkbox[0.2,1.85;o_hide;Hide;",
|
|
out.o_hide,
|
|
"]",
|
|
"checkbox[0.2,2.35;o_grey;Grey;",
|
|
out.o_grey,
|
|
"]",
|
|
"field[1.6,2.05;0.75,0.5;o_sort;Sort;",
|
|
out.o_sort,
|
|
"]",
|
|
"textarea[2.4,0.75;10.8,0.95;o_met;;",
|
|
out.o_met,
|
|
"]",
|
|
"textarea[2.4,1.75;10.8,0.95;o_not_met;;",
|
|
out.o_not_met,
|
|
"]",
|
|
-- prerequisite
|
|
"dropdown[0.2,2.8;3,0.75;p_id;",
|
|
out.p_id_items,
|
|
";",
|
|
out.p_id_count,
|
|
"]",
|
|
"dropdown[0.2,3.8;3,0.75;p_type;",
|
|
out.p_type_items,
|
|
";",
|
|
out.p_type_count,
|
|
"]",
|
|
"textarea[3.4,2.8;9.8,2.1;p_value;;",
|
|
out.p_value,
|
|
"]",
|
|
-- result
|
|
"dropdown[0.2,5;3,0.75;r_id;",
|
|
out.r_id_items,
|
|
";",
|
|
out.r_id_count,
|
|
"]",
|
|
"dropdown[0.2,6;3,0.75;r_type;",
|
|
out.r_type_items,
|
|
";",
|
|
out.r_type_count,
|
|
"]",
|
|
"textarea[3.4,5;9.8,2.1;r_value;;",
|
|
out.r_value,
|
|
"]",
|
|
-- buttons
|
|
"button[0.2,7.7;3,0.75;button_back;Back]",
|
|
"button[7.1,7.7;3,0.75;button_delete;Delete OPTION]",
|
|
"button[10.2,7.7;3,0.75;button_save;Save]",
|
|
-- tooltips
|
|
|
|
"tooltip[d_text;d_text: Dialog text. What the NPC says to you\n\n",
|
|
out.d_text,
|
|
";#FFFFFF;#000000]",
|
|
"tooltip[0.2,0.75;2,0.75;o_id: Option Id;#FFFFFF;#000000]",
|
|
"tooltip[o_met;o_met: Option text when prerequisites are met\n(What you say to the NPC)\n\n",
|
|
out.o_met,
|
|
";#FFFFFF;#000000]",
|
|
"tooltip[o_not_met;o_not_met: Option text when prerequisites are not met\n(What you say to the NPC instead)\n\n",
|
|
out.o_not_met,
|
|
";#FFFFFF;#000000]",
|
|
"tooltip[o_hide;o_hide: If checked, this option is hidden when prerequistes are not met;#FFFFFF;#000000]",
|
|
"tooltip[o_grey;o_grey: If checked, this option is shown when prerequistes are not met, but grey and not selectable;#FFFFFF;#000000]",
|
|
"tooltip[o_sort;o_sort: The lower the number, the higher up in the list this option goes\nNegative values are ignored;#FFFFFF;#000000]",
|
|
"tooltip[0.2,2.8;3,0.75;p_id: Prerequisite Id;#FFFFFF;#000000]",
|
|
"tooltip[0.2,3.8;3,0.75;p_type: Defines what the p_value stands for.\n\n",
|
|
"item: requires the user to have this item in the inventory\n",
|
|
"quest: requires the user to have completed a quest with this id\n",
|
|
"function: executes the given LUA function which must return true or false. true = prerequisite met\n",
|
|
"auto: ???\n",
|
|
"delete: Deletes the chosen prerequiste when the dialog is saved",
|
|
";#FFFFFF;#000000]",
|
|
"tooltip[p_value;p_value: This is evaluated to decide whether the prerequisites are met or not;#FFFFFF;#000000]",
|
|
"tooltip[0.2,5;3,0.75;r_id: Result Id;#FFFFFF;#000000]",
|
|
"tooltip[0.2,6;3,0.75;r_type: Defines what the r_value stands for.\n\n",
|
|
"dialog: forwards the user to the given dialog d_id\n",
|
|
"item: places this item in the inventory of the user\n",
|
|
"quest: starts a quest with this id for the user\n",
|
|
"function: executes the given LUA function\n",
|
|
"auto: automatically forwards the user to the given dialog d_id after a given time\n",
|
|
"delete: Deletes the chosen prerequiste when the dialog is saved",
|
|
";#FFFFFF;#000000]",
|
|
"tooltip[r_value;r_value: This is what happens when the user chooses this option;#FFFFFF;#000000]"
|
|
}
|
|
|
|
return table.concat(formspec, "")
|
|
end
|
|
|
|
|
|
-- edit options (not via staff but via the "I am your owner" dialog)
|
|
yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id)
|
|
-- n_id, d_id and o_id have already been checked when this function is called
|
|
local pname = player:get_player_name()
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
local n_dialog = dialog.n_dialogs[d_id]
|
|
local d_option = n_dialog.d_options[o_id]
|
|
|
|
-- are there any preconditions?
|
|
local list_of_preconditions = ""
|
|
local prereq = d_option.o_prerequisites
|
|
if(prereq) then
|
|
for k, v in pairs(prereq) do
|
|
list_of_preconditions = list_of_preconditions..
|
|
minetest.formspec_escape(v.p_id)..",#FFFF00,"..
|
|
minetest.formspec_escape(v.p_type)..","..
|
|
minetest.formspec_escape(v.p_value)..","
|
|
end
|
|
end
|
|
|
|
-- find the right target dialog for this option (if it exists)
|
|
local target_dialog = nil
|
|
-- and build the list of effects
|
|
local list_of_effects = ""
|
|
local results = d_option.o_results
|
|
if(results) then
|
|
for k, v in pairs(results) do
|
|
if v.r_type == "dialog" and dialog.n_dialogs[v.r_value] ~= nil then
|
|
-- there may be more than one in the data structure
|
|
target_dialog = v.r_value
|
|
elseif v.r_type ~= "dialog" then
|
|
list_of_effects = list_of_effects..
|
|
minetest.formspec_escape(v.r_id)..",#FFFF00,"..
|
|
minetest.formspec_escape(v.r_type)..","..
|
|
minetest.formspec_escape(v.r_value)..","
|
|
end
|
|
end
|
|
end
|
|
-- trade is one of the very few effects players can set
|
|
local button_add_edit_trade = ""
|
|
if(dialog and dialog.trades and dialog.trades[ tostring(d_id).." "..tostring(o_id)]) then
|
|
button_add_edit_trade = ""..
|
|
"button[14.8,10;2.0,0.9;effect_show_trade;Show trade]"..
|
|
"tooltip[effect_show_trade;Show and edit the trade that starts "..
|
|
"when selecting this option.]"
|
|
local tr = dialog.trades[ tostring(d_id).." "..tostring(o_id)]
|
|
-- show the trade in the result/effects list
|
|
list_of_effects = list_of_effects..
|
|
",#FFFF00,trade,"..
|
|
-- show a reasonable overview of what is traded for what
|
|
minetest.formspec_escape("NPC sells "..
|
|
table.concat(tr.buy, ";").." for "..
|
|
table.concat(tr.pay, ";"))..","
|
|
|
|
else
|
|
button_add_edit_trade = ""..
|
|
"button[14.8,10;2.0,0.9;effect_add_trade;Add trade]"..
|
|
"tooltip[effect_add_trade;Add a trade that will start "..
|
|
"when selecting this option.]"
|
|
end
|
|
|
|
-- if no target dialog has been selected: default is to go to the dialog with d_sort 0
|
|
if(not(target_dialog) or target_dialog == "" or not(dialog.n_dialogs[target_dialog])) then
|
|
for d, v in pairs(dialog.n_dialogs) do
|
|
if(v.d_sort and tonumber(v.d_sort) == 0) then
|
|
target_dialog = d
|
|
end
|
|
end
|
|
end
|
|
-- this is the text the NPC will say in reaction to this answer
|
|
local next_text = ""
|
|
if(target_dialog and dialog.n_dialogs[target_dialog]) then
|
|
next_text = dialog.n_dialogs[target_dialog].d_text
|
|
end
|
|
-- build the list of available dialogs for the dropdown list(s)
|
|
local dialog_list = yl_speak_up.text_new_dialog_id
|
|
local dialog_selected = "1"
|
|
-- if there are dialogs defined
|
|
if(dialog and dialog.n_dialogs) then
|
|
-- the first entry will be "New dialog"
|
|
local n = 1
|
|
for k, v in pairs(dialog.n_dialogs) do
|
|
dialog_list = dialog_list .. "," .. minetest.formspec_escape(v.d_id)
|
|
-- which one is the current dialog?
|
|
n = n + 1
|
|
if(v.d_id == target_dialog) then
|
|
dialog_selected = tostring(n)
|
|
end
|
|
end
|
|
end
|
|
if(not(target_dialog)) then
|
|
target_dialog = "- none -"
|
|
end
|
|
|
|
-- offer the correct preselection for hidden/grey/show text
|
|
local alternate_answer_option = "3"
|
|
if(d_option.o_hide_when_prerequisites_not_met == "true") then
|
|
alternate_answer_option = "1"
|
|
elseif(d_option.o_grey_when_prerequisites_not_met == "true") then
|
|
alternate_answer_option = "2"
|
|
end
|
|
|
|
-- can the button "prev(ious)" be shown?
|
|
local button_prev = ""
|
|
-- can the button "next" be shown?
|
|
local button_next = ""
|
|
-- sort all options by o_sort
|
|
local sorted_list = yl_speak_up.get_sorted_options(n_dialog.d_options, "o_sort")
|
|
local o_found = o_id
|
|
for i, o in ipairs(sorted_list) do
|
|
if(o == o_id and sorted_list[ i-1 ]) then
|
|
button_prev = ""..
|
|
"button[7.4,17;2.0,0.9;edit_option_prev;Prev]"..
|
|
"tooltip[edit_option_prev;Go to previous option/answer "..
|
|
"(according to o_sort).]"
|
|
end
|
|
if(o == o_id and sorted_list[ i+1 ]) then
|
|
button_next = ""..
|
|
"button[12.0,17;2.0,0.9;edit_option_next;Next]"..
|
|
"tooltip[edit_option_next;Go to next option/answer "..
|
|
"(according to o_sort).]"
|
|
end
|
|
end
|
|
-- build up the formspec
|
|
local formspec = ""..
|
|
"formspec_version[3]"..
|
|
"size[21,18]"..
|
|
"bgcolor[#00000000;false]"..
|
|
-- button back to the current dialog (of which this is an option)
|
|
"button[15.8,0.2;5.0,0.9;show_current_dialog;Back to dialog "..
|
|
minetest.formspec_escape(d_id).."]"..
|
|
"tooltip[show_current_dialog;Go back to dialog "..
|
|
minetest.formspec_escape(d_id).." and continue editing that dialog.]"..
|
|
-- the text the NPC says
|
|
"label[0.2,0.6;NPC says:]"..
|
|
"hypertext[1.2,1.2;19.6,2.5;d_text;<normal>"..
|
|
minetest.formspec_escape(n_dialog.d_text) .. "\n</normal>".."]"..
|
|
"tooltip[1.2,1.2;19.6,3.0;This is what the NPC says to the player.]"..
|
|
-- list the preconditions
|
|
"label[0.2,4.2;If all of the following pre(C)onditions are fulfilled:]"..
|
|
-- TODO: perhaps add tooltip for the type of the conditions
|
|
"tablecolumns[text;color,span=1;text;text]"..
|
|
"table[1.2,4.5;19.6,2.0;table_of_preconditions;"..
|
|
list_of_preconditions..";0]"..
|
|
-- answer of the player (the actual option)
|
|
"label[0.2,6.8;..the player may answer with this text:]"..
|
|
"label[1.2,7.6;A:]"..
|
|
"field[1.7,7.1;19.1,0.9;text_option_"..minetest.formspec_escape(o_id)..";;"..
|
|
minetest.formspec_escape(d_option.o_text_when_prerequisites_met).."]"..
|
|
"tooltip[option_text_met;This is the answer the player may choose if the "..
|
|
"preconditions are all fulfilled.]"..
|
|
-- dropdown for selecting weather to show the alternate answer or not
|
|
"label[0.2,8.6;..but if at least one pre(C)ondition is not fulfilled, then...]"..
|
|
"dropdown[12.0,8.2;8.6,0.9;hide_or_grey_or_alternate_answer;"..
|
|
"..hide this answer.,"..
|
|
"..grey out this answer.,"..
|
|
"..display the following alternate answer:;"..
|
|
alternate_answer_option..";]"..
|
|
-- alternate answer
|
|
"label[1.2,9.6;A:]"..
|
|
"field[1.7,9.1;19.1,0.9;option_text_not_met;;"..
|
|
minetest.formspec_escape(d_option.o_text_when_prerequisites_not_met).."]"..
|
|
"tooltip[option_text_not_met;This is the answer the player may choose if the "..
|
|
"preconditions are NOT all fulfilled.]"..
|
|
-- list of effects
|
|
"label[0.2,10.6;When this answer has been selected, apply the following (Ef)fects:]"..
|
|
button_add_edit_trade..
|
|
-- TODO: perhaps add tooltip for the type of the conditions
|
|
"tablecolumns[text;color,span=1;text;text]"..
|
|
"table[1.2,11.0;19.6,2.0;table_of_effect;"..
|
|
list_of_effects..";0]"..
|
|
-- ..and what the NPC will reply to that answer
|
|
"label[0.2,13.6;The NPC will react to this answer with the following dialog:]"..
|
|
"hypertext[1.2,14.2;19.6,2.5;d_text_next;<normal>"..
|
|
minetest.formspec_escape(next_text) .. "\n</normal>".."]"..
|
|
"tooltip[1.2,14.2;19.6,2.5;This is what the NPC will say next when the player has "..
|
|
"selected this answer here.]"..
|
|
-- allow to change the target dialog via a dropdown menu
|
|
"dropdown[14.8,13.2;5,0.9;d_id_"..minetest.formspec_escape(o_id)..";"..
|
|
dialog_list..";"..dialog_selected..",]"..
|
|
"tooltip[14.8,13.2;5,0.9;Select the target dialog with which the NPC shall react "..
|
|
"to this answer. Currently, dialog "..minetest.formspec_escape(target_dialog)..
|
|
" is beeing displayed.;#FFFFFF;#000000]"..
|
|
-- button: delete
|
|
"button[0.2,17;2.0,0.9;del_option;Delete]"..
|
|
"tooltip[del_option;Delete this option/answer.]"..
|
|
-- button: add new
|
|
"button[2.4,17;2.0,0.9;add_option;Add]"..
|
|
"tooltip[add_option;Add a new option/answer to this dialog.]"..
|
|
-- button: prev/next
|
|
button_prev..
|
|
button_next..
|
|
-- button: go back to dialog (repeated from top of the page)
|
|
"button[15.8,17;5.0,0.9;show_current_dialog;Back to dialog "..
|
|
minetest.formspec_escape(d_id).."]"..
|
|
"tooltip[show_current_dialog;Go back to dialog "..
|
|
minetest.formspec_escape(d_id).." and continue editing that dialog.]"..
|
|
-- allow to enter o_sort
|
|
"label[9.6,17.5;Sort:]"..
|
|
"field[10.6,17;1.0,0.9;edit_option_o_sort;;"..
|
|
minetest.formspec_escape(d_option.o_sort).."]"..
|
|
"tooltip[edit_option_o_sort;o_sort: The lower the number, the higher up in the "..
|
|
"list this option goes\nNegative values are ignored;#FFFFFF;#000000]"..
|
|
-- hidden field containing the value of o_id
|
|
"field[40,40;0.1,0.1;o_id;;"..minetest.formspec_escape(o_id).."]"
|
|
return formspec
|
|
end
|
|
|
|
|
|
-- initialize the npc without having to use a staff;
|
|
-- returns true when initialization possible
|
|
local function get_fs_initial_config(player, n_id, d_id, is_initial_config)
|
|
local pname = player:get_player_name()
|
|
|
|
-- is the player allowed to edit this npc?
|
|
if(not(yl_speak_up.may_edit_npc(player, n_id))) then
|
|
return get_error_message()
|
|
end
|
|
|
|
local tmp_name = n_id
|
|
local tmp_descr = "A new NPC without description"
|
|
local tmp_text = "Please provide your new NPC with a name and description!"
|
|
-- use existing name and description as presets when just editing
|
|
if(not(is_initial_config)) then
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
tmp_name = dialog.n_npc
|
|
tmp_descr = dialog.n_description
|
|
tmp_text = "You can change the name and description of your NPC."
|
|
end
|
|
local formspec = "size[10,4]"..
|
|
"label[0.2,0.2;"..tmp_text.."]"..
|
|
-- name of the npc
|
|
"label[0.2,1.05;Name:]"..
|
|
"field[2.0,1.2;4,0.9;n_npc;;"..minetest.formspec_escape(tmp_name).."]"..
|
|
"tooltip[n_npc;n_npc: The name of the NPC;#FFFFFF;#000000]"..
|
|
-- description of the npc
|
|
"label[0.2,2.05;Description:]"..
|
|
"field[2.0,2.2;8,0.9;n_description;;"..minetest.formspec_escape(tmp_descr).."]"..
|
|
"tooltip[n_description;n_description: A description for the NPC;#FFFFFF;#000000]"..
|
|
-- save and exit buttons
|
|
"button_exit[3.2,3.2;2,0.9;save_initial_config;Save]"..
|
|
"button_exit[5.4,3.2;2,0.9;exit;Exit]"
|
|
-- show the formspec to the player
|
|
return formspec
|
|
end
|
|
|
|
|
|
-- talk
|
|
|
|
yl_speak_up.get_fs_talkdialog = function(player, n_id, d_id)
|
|
local pname = player:get_player_name()
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
local context_d_id = yl_speak_up.speak_to[pname].d_id
|
|
local active_dialog
|
|
|
|
if not player and not player:is_player() then
|
|
minetest.log(
|
|
"action",
|
|
"[MOD] yl_speak_up: User " ..
|
|
pname ..
|
|
" talked to unconfigured NPC with ID n_" ..
|
|
n_id .. ", position of user was " .. minetest.pos_to_string(player:get_pos(), 0)
|
|
)
|
|
return get_error_message()
|
|
end
|
|
|
|
--[[ If we have an explicit call for a certain d_id, we grab it from parameters.
|
|
If not, we grab in from context.
|
|
When neither are present, we grab it from d_sort
|
|
]]--
|
|
|
|
local c_d_id
|
|
|
|
if d_id ~= nil then
|
|
active_dialog = dialog.n_dialogs[d_id]
|
|
c_d_id = d_id
|
|
elseif yl_speak_up.speak_to[pname].d_id ~= nil then
|
|
c_d_id = yl_speak_up.speak_to[pname].d_id
|
|
active_dialog = dialog.n_dialogs[c_d_id]
|
|
elseif dialog.n_dialogs ~= nil then
|
|
-- Find the dialog with d_sort = 0
|
|
local lowest_sort = nil
|
|
for k, v in pairs(dialog.n_dialogs) do
|
|
if tonumber(v.d_sort) ~= nil then
|
|
if not lowest_sort or tonumber(v.d_sort) < tonumber(lowest_sort) or v.d_sort == "0" then
|
|
if tonumber(v.d_sort) >= 0 then
|
|
lowest_sort = v.d_sort
|
|
active_dialog = v
|
|
c_d_id = k
|
|
end
|
|
end
|
|
end
|
|
end
|
|
else
|
|
-- it may be possible that this player can initialize this npc
|
|
minetest.log(
|
|
"action",
|
|
"[MOD] yl_speak_up: User " ..
|
|
pname ..
|
|
" talked to unconfigured NPC with ID n_" ..
|
|
n_id .. ", position of user was " .. minetest.pos_to_string(player:get_pos(), 0)
|
|
)
|
|
-- this is the initial config
|
|
return get_fs_initial_config(player, n_id, d_id, true)
|
|
end
|
|
|
|
if c_d_id == nil then return get_error_message() end
|
|
|
|
yl_speak_up.speak_to[pname].d_id = c_d_id
|
|
|
|
-- Now we have a dialog to display to the user
|
|
|
|
-- do not crash in case of error
|
|
if(not(active_dialog)) then
|
|
return "size[6,2]"..
|
|
"label[0.2,0.5;Ups! Something went wrong. Please try again.]"
|
|
end
|
|
local allowed = calculate_displayable_options(pname, active_dialog.d_options)
|
|
yl_speak_up.speak_to[pname].allowed = allowed
|
|
|
|
local portrait = calculate_portrait(pname, n_id)
|
|
|
|
local formspec_v = minetest.get_player_information(pname).formspec_version
|
|
local protocol_v = minetest.get_player_information(pname).protocol_version
|
|
local formspec = {}
|
|
local h
|
|
|
|
-- show who owns the NPC (and is thus more or less responsible for what it says)
|
|
local owner_info = ""
|
|
if(yl_speak_up.npc_owner[ n_id ]) then
|
|
owner_info = "\n\n(owned by "..minetest.formspec_escape(yl_speak_up.npc_owner[ n_id ])..")"
|
|
end
|
|
if formspec_v >= 4 then
|
|
formspec = {
|
|
"formspec_version[3]",
|
|
"size[57,33]",
|
|
"position[0,0.45]",
|
|
"anchor[0,0.45]",
|
|
"no_prepend[]",
|
|
"bgcolor[#00000000;false]",
|
|
-- Container
|
|
|
|
"container[2,0.75]",
|
|
-- Background
|
|
|
|
"background[0,0;20,23;yl_speak_up_bg_dialog.png;false]",
|
|
"background[0,24;54.5,7.5;yl_speak_up_bg_dialog.png;false]",
|
|
-- Frame Dialog
|
|
|
|
"image[-0.25,-0.25;1,1;yl_speak_up_bg_dialog_tl.png]",
|
|
"image[-0.25,22.25;1,1;yl_speak_up_bg_dialog_bl.png]",
|
|
"image[19.25,-0.25;1,1;yl_speak_up_bg_dialog_tr.png]",
|
|
"image[19.25,22.25;1,1;yl_speak_up_bg_dialog_br.png]",
|
|
"image[-0.25,0.75;1,21.5;yl_speak_up_bg_dialog_hl.png]",
|
|
"image[19.25,0.75;1,21.5;yl_speak_up_bg_dialog_hr.png]",
|
|
"image[0.75,-0.25;18.5,1;yl_speak_up_bg_dialog_vt.png]",
|
|
"image[0.75,22.25;18.5,1;yl_speak_up_bg_dialog_vb.png]",
|
|
-- Frame Options
|
|
|
|
"image[-0.25,23.75;1,1;yl_speak_up_bg_dialog_tl.png]",
|
|
"image[-0.25,30.75;1,1;yl_speak_up_bg_dialog_bl.png]",
|
|
"image[53.75,23.75;1,1;yl_speak_up_bg_dialog_tr.png]",
|
|
"image[53.75,30.75;1,1;yl_speak_up_bg_dialog_br.png]",
|
|
"image[-0.25,24.75;1,6;yl_speak_up_bg_dialog_hl.png]",
|
|
"image[53.75,24.75;1,6;yl_speak_up_bg_dialog_hr.png]",
|
|
"image[0.75,23.75;53,1;yl_speak_up_bg_dialog_vt.png]",
|
|
"image[0.75,30.75;53,1;yl_speak_up_bg_dialog_vb.png]",
|
|
"style_type[button;bgcolor=#a37e45]",
|
|
"style_type[button_exit;bgcolor=#a37e45]", -- Dialog
|
|
|
|
--[[
|
|
"background[-1,-1;22,25;yl_speak_up_bg_dialog2.png;false]",
|
|
"background[-1,23;58,10;yl_speak_up_bg_dialog2.png;false]",
|
|
"style_type[button;bgcolor=#a37e45]",
|
|
]]--
|
|
"label[0.3,0.6;",
|
|
minetest.formspec_escape(dialog.n_npc),
|
|
"]",
|
|
"label[0.3,1.8;",
|
|
minetest.formspec_escape(dialog.n_description)..owner_info,
|
|
"]",
|
|
"image[15.5,0.5;4,4;",
|
|
portrait,
|
|
"]",
|
|
}
|
|
|
|
-- Is the player working on this particular npc?
|
|
local edit_mode = (yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id)
|
|
|
|
-- this is used to build a list of all available dialogs for a dropdown menu in edit mode
|
|
-- (only relevant in edit mode)
|
|
local dialog_list = yl_speak_up.text_new_dialog_id
|
|
-- find the right index for the dialog_list dropdown above
|
|
local d_id_to_dropdown_index = {}
|
|
|
|
-- allow to change skin, wielded items etc.
|
|
if(edit_mode) then
|
|
table.insert(formspec, "button[15.75,3.5;3.5,0.9;edit_skin;Edit Skin]")
|
|
end
|
|
|
|
-- display the window with the text the NPC is saying
|
|
if(edit_mode and dialog and dialog.n_dialogs) then
|
|
-- sort all dialogs by d_sort
|
|
local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs, "d_sort")
|
|
-- add buttons for previous/next dialog
|
|
for i, d in ipairs(sorted_list) do
|
|
-- build the list of available dialogs for the dropdown list(s)
|
|
dialog_list = dialog_list..","..minetest.formspec_escape(d)
|
|
if(d == c_d_id and sorted_list[ i-1 ]) then
|
|
table.insert(formspec, "button[8.5,4.0;2,0.9;prev_dialog_"..
|
|
minetest.formspec_escape(sorted_list[i-1])..";<]")
|
|
table.insert(formspec, "tooltip[prev_dialog;Go to previous dialog "..
|
|
minetest.formspec_escape(sorted_list[i-1])..".]")
|
|
end
|
|
if(d == c_d_id and sorted_list[ i+1 ]) then
|
|
table.insert(formspec, "button[11,4.0;2,0.9;next_dialog_"..
|
|
minetest.formspec_escape(sorted_list[i+1])..";>]")
|
|
-- add a tooltip
|
|
table.insert(formspec, "tooltip[next_dialog;Go to next dialog "..
|
|
minetest.formspec_escape(sorted_list[i+1])..".]")
|
|
end
|
|
d_id_to_dropdown_index[d] = i + 1
|
|
end
|
|
|
|
table.insert(formspec, "label[0.2,4.6;Dialog:]") -- "..minetest.formspec_escape(c_d_id)..":]")
|
|
table.insert(formspec, "dropdown[3.0,4.0;5,1;d_id;"..dialog_list..";"..d_id_to_dropdown_index[c_d_id]..",]")
|
|
table.insert(formspec, "tooltip[3.0,4.0;5,1;Select the dialog you want to edit. Currently, dialog "..c_d_id.." is beeing displayed.;#FFFFFF;#000000]")
|
|
|
|
-- add a "+" button for creating a new dialog
|
|
table.insert(formspec, "button[13.9,4.0;1,0.9;show_new_dialog;+]")
|
|
table.insert(formspec, "tooltip[show_new_dialog;Create a new dialog.]")
|
|
|
|
-- allow to edit name and description of the NPC
|
|
table.insert(formspec, "button[13.4,0.3;2,0.9;button_edit_name_and_description;Edit]")
|
|
table.insert(formspec, "tooltip[button_edit_name_and_description;Edit name and description of your NPC.]")
|
|
|
|
table.insert(formspec, "textarea[0.2,5;19.6,17.8;d_text;;")
|
|
table.insert(formspec, minetest.formspec_escape(active_dialog.d_text))
|
|
table.insert(formspec, "]")
|
|
else
|
|
-- replace $NPC_NAME$ etc.
|
|
local t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text(
|
|
active_dialog.d_text, dialog, pname))
|
|
table.insert(formspec, "hypertext[0.2,5;19.6,17.8;d_text;<normal>")
|
|
table.insert(formspec, t .. "\n</normal>")
|
|
table.insert(formspec, "]")
|
|
table.insert(formspec, "tooltip[d_text;")
|
|
table.insert(formspec, t:trim())
|
|
table.insert(formspec, ";#000000;#FFFFFF]")
|
|
end
|
|
|
|
table.insert(formspec, "scrollbaroptions[min=0;max=14;smallstep=1;largestep=2;arrows=show]")
|
|
table.insert(formspec, "scrollbar[0.2,24.2;0.2,7;vertical;scr0;0]")
|
|
table.insert(formspec, "scroll_container[0,24;56,7;scr0;vertical;1]")
|
|
h = -0.8
|
|
|
|
-- allow to delete entries that have no options later on
|
|
local anz_options = 0
|
|
-- Let#s sort the options by o_sort
|
|
if active_dialog ~= nil and active_dialog.d_options ~= nil then
|
|
local sorted_buttons = {}
|
|
for _, ad_v in pairs(active_dialog.d_options) do
|
|
sorted_buttons[tonumber(ad_v.o_sort)] = ad_v
|
|
anz_options = anz_options + 1
|
|
end
|
|
|
|
for _, sb_v in pairs(sorted_buttons) do
|
|
local oid = minetest.formspec_escape(sb_v.o_id)
|
|
-- in edit_mode: show all options
|
|
if edit_mode then
|
|
h = h + 1
|
|
-- add a button "o_<nr>:" that leads to an edit formspec for this option
|
|
table.insert(formspec, "button[1.8," .. h .. ";2,0.9;edit_option_" .. oid .. ";"..oid..":]")
|
|
-- add a tooltip "Edit target dialog, pre(C)onditions and (Ef)fects for option o_<nr>"
|
|
table.insert(formspec, "tooltip[edit_option_" .. oid .. ";Edit target dialog, pre(C)onditions and (Ef)fects for option "..oid..".]")
|
|
|
|
-- find the right target dialog for this option (if it exists):
|
|
local target_dialog = nil
|
|
local results = active_dialog.d_options[sb_v.o_id].o_results
|
|
-- has this option more results/effects than just switching to another dialog?
|
|
local has_other_results = false
|
|
if(results ~= nil) then
|
|
for k, v in pairs(results) do
|
|
if v.r_type == "dialog" and dialog.n_dialogs[v.r_value] ~= nil then
|
|
-- there may be more than one in the data structure
|
|
target_dialog = v.r_value
|
|
elseif v.r_type ~= "dialog" then
|
|
has_other_results = true
|
|
end
|
|
end
|
|
end
|
|
if(target_dialog) then
|
|
-- add a button "-> d_<nr>" that leads to the target dialog (if one is set)
|
|
table.insert(formspec, "button[8.5," .. h .. ";1,0.9;button_" .. oid .. ";->]")
|
|
-- add a tooltip "Go to target dialog d_<nr>"
|
|
table.insert(formspec, "tooltip[button_" .. oid .. ";Go to target dialog "..minetest.formspec_escape(target_dialog).." that will be shown when this option ("..oid..") is selected.]")
|
|
|
|
-- selecting an option this way MUST NOT execute the pre(C)onditions or (Ef)fects!
|
|
end
|
|
-- allow to set a new target dialog
|
|
table.insert(formspec, "dropdown[3.9,"..h..";4.7,1;d_id_"..oid..";"..dialog_list..";"..minetest.formspec_escape(d_id_to_dropdown_index[target_dialog] or "0")..",]")
|
|
-- add a tooltip "Change target dialog"
|
|
table.insert(formspec, "tooltip[3.9,"..h..";4.7,1;Change target dialog for option "..oid..".;#FFFFFF;#000000]")
|
|
|
|
-- are there any prerequirements?
|
|
local prereq = active_dialog.d_options[sb_v.o_id].o_prerequisites
|
|
if(prereq) then
|
|
table.insert(formspec, "button[0.5," .. h .. ";0.5,0.9;conditions_"..oid..";C]")
|
|
-- label: "There are pre(C)onditions required for showing this option. Display them."
|
|
table.insert(formspec, "tooltip[conditions_" .. oid .. ";There are pre(C)onditions required for showing this option. Display them.]")
|
|
end
|
|
if(has_other_results) then
|
|
table.insert(formspec, "button[1.1," .. h .. ";0.6,0.9;effects_"..oid..";Ef]")
|
|
-- label: "There are further (Ef)fects (apart from switching to a new dialog)
|
|
-- set for this option. Display them."
|
|
table.insert(formspec, "tooltip[effects_" .. oid .. ";"..
|
|
"There are further (Ef)fects (apart from switching\n"..
|
|
"to a new dialog) set for this option. Display them.]")
|
|
-- if there are only trade effects: show that as well
|
|
elseif(dialog and dialog.trades
|
|
and dialog.trades[ tostring(c_d_id).." "..tostring(oid) ]) then
|
|
table.insert(formspec, "button[1.1," .. h .. ";0.6,0.9;effects_"..oid..";Tr]")
|
|
table.insert(formspec, "tooltip[effects_" .. oid .. ";"..
|
|
"There is a (Tr)ade that will happen when switching to a\n"..
|
|
"new dialog. Display effects and trade of this option.]")
|
|
end
|
|
|
|
-- show the actual text for the option
|
|
table.insert(formspec, "field[9.4," .. h .. ";44.9,0.9;text_option_" .. oid .. ";;"..
|
|
minetest.formspec_escape(sb_v.o_text_when_prerequisites_met).."]")
|
|
-- add a tooltip "Edit the text that is displayed on button o_<nr>."
|
|
table.insert(formspec, "tooltip[text_option_" .. oid .. ";Edit the text that is displayed on button "..oid..".]")
|
|
|
|
-- normal mode: show an option if the prerequirements (if any are defined) are met
|
|
elseif allowed[sb_v.o_id] == true then
|
|
h = h + 1
|
|
-- replace $NPC_NAME$ etc.
|
|
local t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text(
|
|
sb_v.o_text_when_prerequisites_met, dialog, pname))
|
|
table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. oid .. ";]")
|
|
table.insert(
|
|
formspec,
|
|
"tooltip[button_" .. oid .. ";" .. t .. "]"
|
|
)
|
|
local l = h + 0.45
|
|
table.insert(formspec, "label[0.7," .. l .. ";" .. t .. "]")
|
|
else
|
|
if sb_v.o_hide_when_prerequisites_not_met == "true" then
|
|
else
|
|
-- replace $NPC_NAME$ etc.
|
|
local t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text(
|
|
sb_v.o_text_when_prerequisites_not_met, dialog, pname))
|
|
if
|
|
sb_v.o_grey_when_prerequisites_not_met == "true" and
|
|
sb_v.o_text_when_prerequisites_not_met == ""
|
|
then
|
|
h = h + 1
|
|
table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. oid .. ";]")
|
|
table.insert(
|
|
formspec,
|
|
"tooltip[button_" ..
|
|
oid ..
|
|
";" .. yl_speak_up.message_button_option_prerequisites_not_met_default .. "]"
|
|
)
|
|
local l = h + 0.45
|
|
table.insert(
|
|
formspec,
|
|
"label[0.7," ..
|
|
l .. ";" .. yl_speak_up.message_button_option_prerequisites_not_met_default .. "]"
|
|
)
|
|
table.insert(formspec, "box[0.5," .. h .. ";53.8,0.9;#BBBBBB]")
|
|
end
|
|
if
|
|
sb_v.o_grey_when_prerequisites_not_met == "true" and
|
|
sb_v.o_text_when_prerequisites_not_met ~= ""
|
|
then
|
|
h = h + 1
|
|
table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. oid .. ";]")
|
|
table.insert(
|
|
formspec,
|
|
"tooltip[button_" .. oid .. ";" .. t .. "]"
|
|
)
|
|
local l = h + 0.45
|
|
table.insert(
|
|
formspec,
|
|
"label[0.7," .. l .. ";" .. t .. "]"
|
|
)
|
|
table.insert(formspec, "box[0.5," .. h .. ";53.8,0.9;#BBBBBB]")
|
|
end
|
|
if
|
|
sb_v.o_grey_when_prerequisites_not_met == "false" and
|
|
sb_v.o_text_when_prerequisites_not_met == ""
|
|
then
|
|
-- no hide, no grey, no text
|
|
end
|
|
if
|
|
sb_v.o_grey_when_prerequisites_not_met == "false" and
|
|
sb_v.o_text_when_prerequisites_not_met ~= ""
|
|
then
|
|
-- no grey, but text
|
|
h = h + 1
|
|
table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. oid .. ";]")
|
|
table.insert(
|
|
formspec,
|
|
"tooltip[button_" .. oid .. ";" .. t .. "]"
|
|
)
|
|
local l = h + 0.45
|
|
table.insert(
|
|
formspec,
|
|
"label[0.7," .. l .. ";" .. t .. "]"
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- If in edit mode, add two new menu entries: "add new options" and "end edit mode".
|
|
if(edit_mode) then
|
|
-- chat option: "Add a new answer/option to this dialog."
|
|
h = h + 1
|
|
if(anz_options < yl_speak_up.max_number_of_options_per_dialog) then
|
|
table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;add_option;]")
|
|
table.insert(formspec, "tooltip[add_option;Adds a new option to this dialog. You can "..
|
|
"delete options via the option edit menu.]")
|
|
table.insert(formspec, "label[0.7,"..(h+0.45)..";Add a new option/answer to this dialog. "..
|
|
"You can delete options via the option edit menu.]")
|
|
-- the amount of allowed options/answers has been reached
|
|
else
|
|
table.insert(formspec, "box[0.5,"..h..";53.8,0.9;#BBBBBB]")
|
|
table.insert(formspec, "label[0.7,"..(h+0.45)..";Maximum number of allowed answers/options "..
|
|
"reached. No further options/answers can be added.]")
|
|
end
|
|
|
|
-- chat option: "Delete this dialog."
|
|
h = h + 1
|
|
if(active_dialog
|
|
and active_dialog.d_text == ""
|
|
and anz_options == 0) then
|
|
table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;delete_this_empty_dialog;]")
|
|
table.insert(formspec, "tooltip[delete_this_empty_dialog;Dialogs can only be deleted "..
|
|
"when they are empty and have no more options/answers. This is the case here, "..
|
|
"so the dialog can be deleted.]")
|
|
table.insert(formspec, "label[0.7,"..(h+0.45)..";Delete this empty dialog.]")
|
|
-- (but only show this option if the dialog is empty)
|
|
else
|
|
table.insert(formspec, "box[0.5,"..h..";53.8,0.9;#BBBBBB]")
|
|
table.insert(formspec, "label[0.7,"..(h+0.45)..";If you want to delete this dialog, you "..
|
|
"need to delete all options and its text first.]")
|
|
end
|
|
|
|
-- chat option: "Make this dialog the first one shown when starting to talk."
|
|
h = h + 1
|
|
if(active_dialog and active_dialog.d_sort and tonumber(active_dialog.d_sort) ~= 0) then
|
|
table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;make_first_option;]")
|
|
table.insert(formspec, "tooltip[make_first_option;The NPC has to start with one dialog "..
|
|
"when he is right-clicked. Make this dialog the one shown.]")
|
|
table.insert(formspec, "label[0.7,"..(h+0.45)..";Make this dialog the first one shown "..
|
|
"when starting a conversation.]")
|
|
-- (but only show this option if it's not already the first one)
|
|
else
|
|
table.insert(formspec, "box[0.5,"..h..";53.8,0.9;#BBBBBB]")
|
|
table.insert(formspec, "label[0.7,"..(h+0.45)..";This dialog will be shown whenever "..
|
|
"a conversation is started.]")
|
|
end
|
|
|
|
-- access the inventory of the NPC
|
|
h = h + 1
|
|
table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;show_inventory;]")
|
|
table.insert(formspec, "tooltip[show_inventory;Access and manage the inventory of the NPC. "..
|
|
"This is used for adding trade items, getting collected payments and managing "..
|
|
"quest items.]")
|
|
table.insert(formspec, "label[0.7,"..(h+0.45)..";I am your owner. Show me your inventory!]")
|
|
|
|
-- chat option: Mute/Unmute NPC
|
|
h = h + 1
|
|
local obj = yl_speak_up.speak_to[pname].obj
|
|
-- some precautions - someone else might have eliminated the NPC in the meantime
|
|
local luaentity = nil
|
|
if(obj) then
|
|
luaentity = obj:get_luaentity()
|
|
end
|
|
if(luaentity and luaentity.yl_speak_up.talk) then
|
|
table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;mute_npc;]")
|
|
table.insert(formspec, "tooltip[mute_npc;The NPC will no longer show his dialogs "..
|
|
"when he is right-clicked. This is useful while you edit the NPC and don't "..
|
|
"want players to see unfinished entries and/or quests.]")
|
|
table.insert(formspec, "label[0.7,"..(h+0.45)..";State: Not muted. Stop talking to other "..
|
|
"players while I give you new orders.]")
|
|
elseif(luaentity) then
|
|
-- unmute the NPC
|
|
table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;un_mute_npc;]")
|
|
table.insert(formspec, "tooltip[un_mute_npc;The NPC will show his dialogs to other "..
|
|
"players when he is right-clicked. This is the normal mode of operation. Choose "..
|
|
"this when you are finished editing.]")
|
|
table.insert(formspec, "label[0.7,"..(h+0.45)..";State: You are currently muted. Talk to "..
|
|
"anyone again who wants to talk to you.]")
|
|
end
|
|
|
|
-- chat option: "That was all. I'm finished with giving you new orders. Remember them!"
|
|
h = h + 1
|
|
table.insert(formspec, "button_exit[0.5," .. h .. ";53.8,0.9;button_end_edit_mode;]")
|
|
table.insert(formspec, "tooltip[button_end_edit_mode;Ends edit mode. From now on, your NPC will talk to you like he talks to other players. You can always give him new orders by entering edit mode again.]")
|
|
table.insert(formspec, "label[0.7,"..(h+0.45)..";That was all. I'm finished with giving you new orders. Remember them!]")
|
|
|
|
-- Offer to enter edit mode if the player has the npc_talk_owner priv AND owns the npc.
|
|
-- The npc_master priv allows to edit all NPC.
|
|
elseif(yl_speak_up.may_edit_npc(player, n_id)) then
|
|
-- chat option: "I am your owner. I have new orders for you.
|
|
h = h + 1
|
|
table.insert(formspec, "button_exit[0.5," .. h .. ";53.8,0.9;button_start_edit_mode;]")
|
|
table.insert(formspec, "tooltip[button_start_edit_mode;Enters edit mode. In this mode, you can edit the texts the NPC says and the answers that can be given.]")
|
|
table.insert(formspec, "label[0.7,"..(h+0.45)..";I am your owner. I have new orders for you.]")
|
|
end
|
|
|
|
-- add a Let's trade button to the first dialog if the NPC has trades
|
|
if(active_dialog and active_dialog.d_sort and tonumber(active_dialog.d_sort) == 0
|
|
and dialog.trades) then
|
|
local has_trades = nil
|
|
for k, v in pairs(dialog.trades) do
|
|
-- has the NPC any *public* trades that are not effects/results?
|
|
if(not(v.hide) and not(v.d_id)) then
|
|
has_trades = true
|
|
break
|
|
end
|
|
end
|
|
if(has_trades) then
|
|
h = h + 1
|
|
table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;show_trade_list;]")
|
|
table.insert(formspec, "tooltip[show_trade_list;Show a list of trades the NPC has to offer.]")
|
|
table.insert(formspec, "label[0.7,"..(h+0.45)..";Let's trade!]")
|
|
end
|
|
end
|
|
|
|
|
|
h = h + 1
|
|
table.insert(formspec, "button_exit[0.5," .. h .. ";53.8,0.9;button_exit;]")
|
|
table.insert(formspec, "tooltip[button_exit;" .. yl_speak_up.message_button_option_exit .. "]")
|
|
local l = h + 0.45
|
|
table.insert(formspec, "label[0.7," .. l .. ";" .. yl_speak_up.message_button_option_exit .. "]")
|
|
|
|
table.insert(formspec, "scroll_container_end[]")
|
|
|
|
table.insert(formspec, "container_end[]")
|
|
else
|
|
minetest.log(
|
|
"info",
|
|
"[MOD] yl_speak_up: User " .. pname .. " talked to NPC ID n_" .. n_id .. " with an old formspec version!"
|
|
)
|
|
|
|
local upgrade_warning = ""
|
|
local max_number_of_buttons = yl_speak_up.max_number_of_buttons
|
|
local start_index = yl_speak_up.speak_to[pname].option_index
|
|
|
|
if formspec_v < 3 or protocol_v < 39 then
|
|
local warn = {
|
|
"box[0.3,3;15,2;red]",
|
|
"label[0.7,3.2;",
|
|
yl_speak_up.text_version_warning,
|
|
"]"
|
|
}
|
|
upgrade_warning = table.concat(warn, "")
|
|
end
|
|
|
|
formspec = {
|
|
"formspec_version[1]",
|
|
"size[48,28]",
|
|
"position[0,0.45]",
|
|
"anchor[0,0.45]",
|
|
"no_prepend[]",
|
|
"bgcolor[#00000000;false]",
|
|
-- Container
|
|
|
|
"container[2,0.75]",
|
|
-- Background
|
|
|
|
"background[0,0;20,17;yl_speak_up_bg_dialog.png]",
|
|
"background[0,18;45,8;yl_speak_up_bg_dialog.png]",
|
|
-- Frame Dialog
|
|
|
|
"image[-0.35,-0.35;1,1;yl_speak_up_bg_dialog_tl.png]",
|
|
"image[-0.35,16.25;1,1;yl_speak_up_bg_dialog_bl.png]",
|
|
"image[19.35,-0.35;1,1;yl_speak_up_bg_dialog_tr.png]",
|
|
"image[19.35,16.25;1,1;yl_speak_up_bg_dialog_br.png]",
|
|
"image[-0.35,0.35;1,19.5;yl_speak_up_bg_dialog_hl.png]",
|
|
"image[19.35,0.35;1,19.5;yl_speak_up_bg_dialog_hr.png]",
|
|
"image[0.35,-0.35;24.5,1;yl_speak_up_bg_dialog_vt.png]",
|
|
"image[0.35,16.25;24.5,1;yl_speak_up_bg_dialog_vb.png]",
|
|
-- Frame Options
|
|
|
|
"image[-0.35,17.65;1,1;yl_speak_up_bg_dialog_tl.png]",
|
|
"image[-0.35,25.35;1,1;yl_speak_up_bg_dialog_bl.png]",
|
|
"image[44.35,17.65;1,1;yl_speak_up_bg_dialog_tr.png]",
|
|
"image[44.35,25.35;1,1;yl_speak_up_bg_dialog_br.png]",
|
|
"image[-0.35,18.35;1,8.5;yl_speak_up_bg_dialog_hl.png]",
|
|
"image[44.35,18.35;1,8.5;yl_speak_up_bg_dialog_hr.png]",
|
|
"image[0.35,17.65;56.5,1;yl_speak_up_bg_dialog_vt.png]",
|
|
"image[0.35,25.35;56.5,1;yl_speak_up_bg_dialog_vb.png]",
|
|
-- Upgrade Warning
|
|
|
|
upgrade_warning,
|
|
-- Dialog
|
|
|
|
"label[0.3,0.6;",
|
|
minetest.formspec_escape(dialog.n_npc),
|
|
"]",
|
|
"label[0.3,1.8;",
|
|
minetest.formspec_escape(dialog.n_description),
|
|
"]",
|
|
"image[15.5,0.5;4,4;",
|
|
portrait,
|
|
"]",
|
|
"textarea[0.5,5;19.6,13.5;;;",
|
|
minetest.formspec_escape(active_dialog.d_text) .. "\n",
|
|
"]",
|
|
"container[0,18]"
|
|
}
|
|
h = -1
|
|
|
|
if active_dialog ~= nil and active_dialog.d_options ~= nil then
|
|
-- Let's sort the options by o_sort.
|
|
local sorted_buttons = {}
|
|
for _, ad_v in pairs(active_dialog.d_options) do
|
|
sorted_buttons[tonumber(ad_v.o_sort)] = ad_v
|
|
end
|
|
|
|
if #sorted_buttons > max_number_of_buttons then
|
|
-- Generate arrows
|
|
table.insert(formspec, "button[0.1,0;1,0.9;button_down;^]")
|
|
table.insert(formspec, "button[0.1,7.0;1,0.9;button_up;v]")
|
|
end
|
|
|
|
local counter = 1
|
|
|
|
for _, sb_v in pairs(sorted_buttons) do
|
|
local end_index = start_index + max_number_of_buttons
|
|
if counter >= start_index and counter < end_index then
|
|
local oid = minetest.formspec_escape(sb_v.o_id)
|
|
if allowed[sb_v.o_id] == true then
|
|
h = h + 1
|
|
table.insert(
|
|
formspec,
|
|
"button[1," ..
|
|
h .. ";44,0.9;button_" .. oid .. ";" .. minetest.formspec_escape(sb_v.o_text_when_prerequisites_met) .. "]"
|
|
)
|
|
table.insert(
|
|
formspec,
|
|
"tooltip[button_" .. oid .. ";" .. minetest.formspec_escape(sb_v.o_text_when_prerequisites_met) .. "]"
|
|
)
|
|
else
|
|
if sb_v.o_hide_when_prerequisites_not_met == "true" then
|
|
-- hide! Nothing to do
|
|
else
|
|
if
|
|
sb_v.o_grey_when_prerequisites_not_met == "true" and
|
|
sb_v.o_text_when_prerequisites_not_met == ""
|
|
then
|
|
h = h + 1
|
|
local l = h - 0.2
|
|
table.insert(formspec, "box[1," .. l .. ";43,1;#BBBBBB]")
|
|
table.insert(
|
|
formspec,
|
|
"label[3," ..
|
|
h ..
|
|
";" ..
|
|
yl_speak_up.message_button_option_prerequisites_not_met_default .. "]"
|
|
)
|
|
end
|
|
if
|
|
sb_v.o_grey_when_prerequisites_not_met == "true" and
|
|
sb_v.o_text_when_prerequisites_not_met ~= ""
|
|
then
|
|
h = h + 1
|
|
local l = h - 0.2
|
|
table.insert(formspec, "box[1," .. l .. ";43,1;#BBBBBB]")
|
|
table.insert(
|
|
formspec,
|
|
"label[3," ..
|
|
h ..
|
|
";" ..
|
|
yl_speak_up.message_button_option_prerequisites_not_met_default ..
|
|
" : " .. minetest.formspec_escape(sb_v.o_text_when_prerequisites_not_met) .. "]"
|
|
)
|
|
end
|
|
if
|
|
sb_v.o_grey_when_prerequisites_not_met == "false" and
|
|
sb_v.o_text_when_prerequisites_not_met == ""
|
|
then
|
|
-- no hide, no grey, no text
|
|
end
|
|
if
|
|
sb_v.o_grey_when_prerequisites_not_met == "false" and
|
|
sb_v.o_text_when_prerequisites_not_met ~= ""
|
|
then
|
|
h = h + 1
|
|
table.insert(
|
|
formspec,
|
|
"button[1," ..
|
|
h ..
|
|
";44,0.9;button_" ..
|
|
oid .. ";" .. minetest.formspec_escape(sb_v.o_text_when_prerequisites_not_met) .. "]"
|
|
)
|
|
table.insert(
|
|
formspec,
|
|
"tooltip[button_" ..
|
|
oid .. ";" .. minetest.formspec_escape(sb_v.o_text_when_prerequisites_not_met) .. "]"
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
counter = counter + 1
|
|
end
|
|
end
|
|
h = h + 1
|
|
table.insert(
|
|
formspec,
|
|
"button_exit[1," .. h .. ";44,0.9;button_exit;" .. yl_speak_up.message_button_option_exit .. "]"
|
|
)
|
|
table.insert(formspec, "tooltip[button_exit;" .. yl_speak_up.message_button_option_exit .. "]")
|
|
|
|
table.insert(formspec, "container_end[]")
|
|
table.insert(formspec, "container_end[]")
|
|
end
|
|
|
|
return table.concat(formspec, "")
|
|
end
|
|
|
|
-- receive fields
|
|
|
|
-- options
|
|
|
|
-- route player input to the right functions;
|
|
-- return true when the right function has been found
|
|
minetest.register_on_player_receive_fields( function(player, formname, fields)
|
|
if formname == "yl_speak_up:optiondialog" then
|
|
yl_speak_up.input_optiondialog(player, formname, fields)
|
|
return true
|
|
elseif formname == "yl_speak_up:setdialog" then
|
|
yl_speak_up.input_setdialog(player, formname, fields)
|
|
return true
|
|
elseif formname == "yl_speak_up:confirm_save" then
|
|
yl_speak_up.input_confirm_save(player, formname, fields)
|
|
return true
|
|
elseif formname == "yl_speak_up:edit_option_dialog" then
|
|
yl_speak_up.input_edit_option_dialog(player, formname, fields)
|
|
return true
|
|
elseif formname == "yl_speak_up:talk" then
|
|
yl_speak_up.input_talk(player, formname, fields)
|
|
return true
|
|
elseif formname == "yl_speak_up:fashion" then
|
|
yl_speak_up.input_fashion(player, formname, fields)
|
|
return true
|
|
elseif formname == "yl_speak_up:inventory" then
|
|
yl_speak_up.input_inventory(player, formname, fields)
|
|
return true
|
|
elseif formname == "yl_speak_up:trade_list" then
|
|
yl_speak_up.input_trade_list(player, formname, fields)
|
|
return true
|
|
elseif formname == "yl_speak_up:do_trade_simple" then
|
|
yl_speak_up.input_do_trade_simple(player, formname, fields)
|
|
return true
|
|
elseif formname == "yl_speak_up:add_trade_simple" then
|
|
yl_speak_up.input_add_trade_simple(player, formname, fields)
|
|
return true
|
|
end
|
|
end)
|
|
|
|
|
|
-- the player has closed the inventory formspec of the NPC - save it
|
|
yl_speak_up.input_inventory = function(player, formname, fields)
|
|
local pname = player:get_player_name()
|
|
local d_id = yl_speak_up.speak_to[pname].d_id
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
-- after closing the inventory formspec:
|
|
-- ..save the (very probably) modified inventory
|
|
yl_speak_up.save_npc_inventory(n_id)
|
|
-- show the trade list?
|
|
if(fields.inventory_show_tradelist) then
|
|
minetest.show_formspec(pname, "yl_speak_up:trade_list",
|
|
yl_speak_up.get_fs_trade_list(player))
|
|
return
|
|
end
|
|
-- ..and go back to the normal talk formspec
|
|
minetest.show_formspec(pname, "yl_speak_up:talk",
|
|
yl_speak_up.get_fs_talkdialog(player, n_id, d_id))
|
|
end
|
|
|
|
|
|
yl_speak_up.input_optiondialog = function(player, formname, fields)
|
|
if formname ~= "yl_speak_up:optiondialog" then
|
|
return
|
|
end
|
|
if(not(minetest.check_player_privs(player, {npc_master=true}))) then
|
|
return
|
|
end
|
|
|
|
local pname = player:get_player_name()
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local d_id = yl_speak_up.speak_to[pname].d_id
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
|
|
if fields then
|
|
-- Button Cancel: Exit the form
|
|
if fields.button_back or fields.quit then
|
|
local temp_dialog = yl_speak_up.speak_to[pname].dialog
|
|
|
|
yl_speak_up.speak_to[pname] = {}
|
|
|
|
yl_speak_up.speak_to[pname].dialog = temp_dialog
|
|
yl_speak_up.speak_to[pname].n_id = n_id
|
|
yl_speak_up.speak_to[pname].d_id = d_id
|
|
|
|
minetest.show_formspec(pname, "yl_speak_up:setdialog", get_fs_setdialog(player, n_id, d_id))
|
|
return
|
|
end
|
|
|
|
-- Button Delete Option: Delete option dialog, but do not exit
|
|
if fields.button_delete and fields.d_id and fields.o_id ~= yl_speak_up.text_new_option_id then
|
|
local temp_dialog = yl_speak_up.speak_to[pname].dialog
|
|
|
|
yl_speak_up.speak_to[pname] = {}
|
|
|
|
yl_speak_up.speak_to[pname].dialog = temp_dialog
|
|
yl_speak_up.speak_to[pname].n_id = n_id
|
|
yl_speak_up.speak_to[pname].d_id = d_id
|
|
|
|
yl_speak_up.speak_to[pname].o_id = yl_speak_up.text_new_option_id
|
|
yl_speak_up.speak_to[pname].p_id = yl_speak_up.text_new_prerequisite_id
|
|
yl_speak_up.speak_to[pname].r_id = yl_speak_up.text_new_result_id
|
|
|
|
local o_id = yl_speak_up.text_new_option_id
|
|
local p_id = yl_speak_up.text_new_prerequisite_id
|
|
local r_id = yl_speak_up.text_new_result_id
|
|
|
|
delete_option(fields.n_id, fields.d_id, fields.o_id)
|
|
yl_speak_up.log_change(pname, n_id,
|
|
"(staff) Deleted option "..tostring(fields.o_id).." in dialog "..tostring(fields.d_id)..".")
|
|
minetest.show_formspec(
|
|
pname,
|
|
"yl_speak_up:optiondialog",
|
|
get_fs_optiondialog(player, n_id, d_id, o_id, p_id, r_id)
|
|
)
|
|
return
|
|
end
|
|
|
|
if fields.d_text then
|
|
yl_speak_up.speak_to[pname].d_text = fields.d_text
|
|
end
|
|
if fields.d_type then
|
|
yl_speak_up.speak_to[pname].d_type = fields.d_type
|
|
end
|
|
|
|
-- When a new o_id is chosen, then all the other stuff is invalidated and we need to take values from the dialog or even default values
|
|
if fields.o_id ~= nil and fields.o_id ~= yl_speak_up.speak_to[pname].o_id then
|
|
local o_id = fields.o_id
|
|
yl_speak_up.speak_to[pname].o_id = o_id
|
|
yl_speak_up.speak_to[pname].p_id = yl_speak_up.text_new_prerequisite_id
|
|
yl_speak_up.speak_to[pname].r_id = yl_speak_up.text_new_result_id
|
|
|
|
-- Depends on the dialog
|
|
if o_id == yl_speak_up.text_new_option_id then --New dialog
|
|
yl_speak_up.speak_to[pname].o_hide = "false"
|
|
yl_speak_up.speak_to[pname].o_grey = "false"
|
|
yl_speak_up.speak_to[pname].o_sort = ""
|
|
yl_speak_up.speak_to[pname].o_met = "Text when conditions are not met"
|
|
yl_speak_up.speak_to[pname].o_not_met = "Text when conditions are met"
|
|
else -- existing dialog
|
|
yl_speak_up.speak_to[pname].o_hide =
|
|
dialog.n_dialogs[d_id].d_options[o_id].o_hide_when_prerequisites_not_met or "false"
|
|
yl_speak_up.speak_to[pname].o_grey =
|
|
dialog.n_dialogs[d_id].d_options[o_id].o_grey_when_prerequisites_not_met or "false"
|
|
yl_speak_up.speak_to[pname].o_sort = dialog.n_dialogs[d_id].d_options[o_id].o_sort or ""
|
|
yl_speak_up.speak_to[pname].o_met =
|
|
dialog.n_dialogs[d_id].d_options[o_id].o_text_when_prerequisites_not_met or
|
|
"Text when conditions are not met"
|
|
yl_speak_up.speak_to[pname].o_not_met =
|
|
dialog.n_dialogs[d_id].d_options[o_id].o_text_when_prerequisites_met or
|
|
"Text when conditions are met"
|
|
end
|
|
else
|
|
if fields.o_hide then
|
|
yl_speak_up.speak_to[pname].o_hide = fields.o_hide
|
|
end
|
|
if fields.o_grey then
|
|
yl_speak_up.speak_to[pname].o_grey = fields.o_grey
|
|
end
|
|
if fields.o_sort then
|
|
yl_speak_up.speak_to[pname].o_sort = fields.o_sort
|
|
end
|
|
if fields.o_met then
|
|
yl_speak_up.speak_to[pname].o_met = fields.o_met
|
|
end
|
|
if fields.o_not_met then
|
|
yl_speak_up.speak_to[pname].o_not_met = fields.o_not_met
|
|
end
|
|
|
|
if fields.p_id ~= nil and fields.p_id ~= yl_speak_up.speak_to[pname].p_id then
|
|
yl_speak_up.speak_to[pname].p_id = fields.p_id
|
|
yl_speak_up.speak_to[pname].p_value = fields.p_value
|
|
yl_speak_up.speak_to[pname].p_type = fields.p_type
|
|
end
|
|
if fields.p_value then
|
|
yl_speak_up.speak_to[pname].p_value = fields.p_value
|
|
end
|
|
if fields.p_type then
|
|
yl_speak_up.speak_to[pname].p_type = fields.p_type
|
|
end
|
|
if fields.r_id ~= nil and fields.r_id ~= yl_speak_up.speak_to[pname].r_id then
|
|
yl_speak_up.speak_to[pname].r_id = fields.r_id
|
|
yl_speak_up.speak_to[pname].r_value = fields.r_value
|
|
yl_speak_up.speak_to[pname].r_type = fields.r_type
|
|
end
|
|
if fields.r_value then
|
|
yl_speak_up.speak_to[pname].r_value = fields.r_value
|
|
end
|
|
if fields.r_type then
|
|
yl_speak_up.speak_to[pname].r_type = fields.r_type
|
|
end
|
|
end
|
|
end
|
|
|
|
local o_id = fields.o_id or yl_speak_up.speak_to[pname].o_id
|
|
local p_id = fields.p_id or yl_speak_up.speak_to[pname].p_id
|
|
local r_id = fields.r_id or yl_speak_up.speak_to[pname].r_id
|
|
|
|
-- Button Save
|
|
if fields and fields.button_save and fields.n_id and fields.d_id then
|
|
local temp_dialog = options_to_dialog(pname)
|
|
|
|
yl_speak_up.save_dialog(n_id, temp_dialog)
|
|
-- TODO: a better change detection would be good - because name, description and owner can change here
|
|
yl_speak_up.log_change(pname, n_id,
|
|
"(staff) Saved dialog "..tostring(fields.d_id)..".")
|
|
|
|
minetest.show_formspec(
|
|
pname,
|
|
"yl_speak_up:optiondialog",
|
|
get_fs_optiondialog(player, fields.n_id, fields.d_id, fields.o_id, fields.p_id, fields.r_id)
|
|
)
|
|
end
|
|
|
|
minetest.show_formspec(
|
|
pname,
|
|
"yl_speak_up:optiondialog",
|
|
get_fs_optiondialog(player, n_id, d_id, o_id, p_id, r_id)
|
|
)
|
|
end
|
|
|
|
-- dialog
|
|
|
|
yl_speak_up.input_setdialog = function(player, formname, fields)
|
|
if formname ~= "yl_speak_up:setdialog" then
|
|
return
|
|
end
|
|
if(not(minetest.check_player_privs(player, {npc_master=true}))) then
|
|
return
|
|
end
|
|
|
|
local pname = player:get_player_name()
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local d_id = yl_speak_up.speak_to[pname].d_id or yl_speak_up.text_new_dialog_id
|
|
|
|
-- Button Cancel: Exit the form
|
|
if fields.button_cancel or fields.quit then
|
|
yl_speak_up.speak_to[pname] = nil
|
|
minetest.close_formspec(pname, "yl_speak_up:setdialog")
|
|
return
|
|
end
|
|
|
|
-- Button Options with valid d_id: Show options formspec
|
|
if fields.button_option and fields.d_id and fields.d_id ~= yl_speak_up.text_new_dialog_id then
|
|
local o_id = yl_speak_up.text_new_option_id
|
|
local p_id = yl_speak_up.text_new_prerequisite_id
|
|
local r_id = yl_speak_up.text_new_result_id
|
|
|
|
-- Context
|
|
yl_speak_up.speak_to[pname].d_id = fields.d_id
|
|
yl_speak_up.speak_to[pname].o_id = o_id
|
|
yl_speak_up.speak_to[pname].p_id = p_id
|
|
yl_speak_up.speak_to[pname].r_id = r_id
|
|
|
|
minetest.show_formspec(
|
|
pname,
|
|
"yl_speak_up:optiondialog",
|
|
get_fs_optiondialog(player, n_id, fields.d_id, o_id, p_id, r_id)
|
|
)
|
|
return
|
|
end
|
|
|
|
-- Button Options with invalid d_id: Show options formspec
|
|
if fields.button_option and fields.d_id and fields.d_id == yl_speak_up.text_new_dialog_id then
|
|
yl_speak_up.speak_to[pname].d_id = yl_speak_up.text_new_dialog_id
|
|
minetest.show_formspec(pname, "yl_speak_up:optiondialog", get_error_message())
|
|
return
|
|
end
|
|
|
|
-- Button Save: Save the settings, but do not exit
|
|
if fields.button_save and fields.d_id then
|
|
local dialog = fields_to_dialog(pname, fields)
|
|
yl_speak_up.save_dialog(n_id, dialog)
|
|
-- TODO: a better change detection would be great (name, description, owner can be changed as well)
|
|
yl_speak_up.log_change(pname, n_id,
|
|
"(staff) Saved dialog "..tostring(fields.d_id)..".")
|
|
yl_speak_up.speak_to[pname].dialog = dialog
|
|
|
|
if yl_speak_up.speak_to[pname].obj then
|
|
local obj = yl_speak_up.speak_to[pname].obj
|
|
local ent = obj:get_luaentity()
|
|
if ent ~= nil then
|
|
ent.yl_speak_up.npc_name = dialog.n_npc
|
|
ent.yl_speak_up.npc_description = dialog.n_description
|
|
ent.owner = dialog.npc_owner
|
|
|
|
local i_text = dialog.n_npc .. "\n" .. dialog.n_description .. "\n" .. yl_speak_up.infotext
|
|
obj:set_properties({infotext = i_text})
|
|
yl_speak_up.update_nametag(ent)
|
|
end
|
|
end
|
|
|
|
minetest.show_formspec(pname, "yl_speak_up:setdialog", get_fs_setdialog(player, n_id, d_id))
|
|
return
|
|
end
|
|
|
|
-- Button Delete: Delete dialog, but do not exit
|
|
if fields.button_delete and fields.d_id then
|
|
delete_dialog(n_id, fields.d_id)
|
|
yl_speak_up.speak_to[pname].d_id = yl_speak_up.text_new_dialog_id
|
|
minetest.show_formspec(
|
|
pname,
|
|
"yl_speak_up:setdialog",
|
|
get_fs_setdialog(player, n_id, yl_speak_up.text_new_dialog_id)
|
|
)
|
|
yl_speak_up.log_change(pname, n_id,
|
|
"(staff) Deleted dialog "..tostring(fields.d_id)..".")
|
|
end
|
|
|
|
-- Change in Dropdown List: Show dialog formspec again with different dialog selected
|
|
if fields.d_id then
|
|
yl_speak_up.speak_to[pname].d_id = fields.d_id
|
|
minetest.show_formspec(pname, "yl_speak_up:setdialog", get_fs_setdialog(player, n_id, fields.d_id))
|
|
end
|
|
end
|
|
|
|
-- talk
|
|
|
|
-- helper function
|
|
-- the option to override next_id and provide a value is needed when a new dialog was
|
|
-- added, then edited, and then discarded; it's still needed after that, but has to
|
|
-- be reset to empty state (wasn't stored before)
|
|
yl_speak_up.add_new_dialog = function(dialog, pname, next_id)
|
|
if(not(next_id)) then
|
|
next_id = find_next_id(dialog.n_dialogs)
|
|
end
|
|
local future_d_id = "d_" .. next_id
|
|
-- Initialize empty dialog
|
|
dialog.n_dialogs[future_d_id] = {
|
|
d_id = future_d_id,
|
|
d_type = "text",
|
|
d_text = "",
|
|
d_sort = next_id
|
|
}
|
|
-- store that there have been changes to this npc
|
|
-- (better ask only when the new dialog is changed)
|
|
-- table.insert(yl_speak_up.npc_was_changed[ yl_speak_up.edit_mode[pname] ],
|
|
-- "Dialog "..future_d_id..": New dialog added.")
|
|
return future_d_id
|
|
end
|
|
|
|
|
|
-- add a new result to option o_id of dialog d_id
|
|
yl_speak_up.add_new_result = function(dialog, d_id, o_id)
|
|
if(not(dialog) or not(dialog.n_dialogs) or not(dialog.n_dialogs[d_id])
|
|
or not(dialog.n_dialogs[d_id].d_options) or not(dialog.n_dialogs[d_id].d_options[o_id])) then
|
|
return
|
|
end
|
|
-- create a new result (first the id, then the actual result)
|
|
local future_r_id = "r_" .. find_next_id(dialog.n_dialogs[d_id].d_options[o_id].o_results)
|
|
if future_r_id == "r_1" then
|
|
dialog.n_dialogs[d_id].d_options[o_id].o_results = {}
|
|
end
|
|
dialog.n_dialogs[d_id].d_options[o_id].o_results[future_r_id] = {}
|
|
return future_r_id
|
|
end
|
|
|
|
|
|
-- this is useful for result types that can exist only once per option
|
|
-- (apart from editing with the staff);
|
|
-- examples: "dialog" and "trade";
|
|
-- returns tue r_id or nil if no result of that type has been found
|
|
yl_speak_up.get_result_id_by_type = function(dialog, d_id, o_id, result_type)
|
|
if(not(dialog) or not(dialog.n_dialogs) or not(dialog.n_dialogs[d_id])
|
|
or not(dialog.n_dialogs[d_id].d_options) or not(dialog.n_dialogs[d_id].d_options[o_id])) then
|
|
return
|
|
end
|
|
local results = dialog.n_dialogs[d_id].d_options[o_id].o_results
|
|
if(not(results)) then
|
|
return
|
|
end
|
|
for k, v in pairs(results) do
|
|
if(v.r_type == result_type) then
|
|
return k
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-- this is always called when switching to a new dialog; but only in edit_mode does
|
|
-- it show a formspec asking for change/back/discard
|
|
yl_speak_up.save_changes_and_switch_to_other_dialog = function(player, fields, target_dialog)
|
|
|
|
if(not(target_dialog)) then
|
|
target_dialog = ""
|
|
end
|
|
|
|
local pname = player:get_player_name()
|
|
-- if the player is not even talking to this particular npc
|
|
if(not(yl_speak_up.speak_to[pname])) then
|
|
return
|
|
end
|
|
|
|
local edit_mode = (yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id)
|
|
local d_id = yl_speak_up.speak_to[pname].d_id
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
|
|
-- the player decided to go back and continue editing the current dialog
|
|
if(edit_mode and fields.back_to_dialog_changes) then
|
|
-- do NOT clear the list of changes; just show the old dialog again
|
|
minetest.show_formspec(pname, "yl_speak_up:talk",
|
|
yl_speak_up.get_fs_talkdialog(player, n_id, d_id))
|
|
return
|
|
|
|
-- save changes and continue on to the next dialog
|
|
elseif(edit_mode and fields.save_dialog_changes) then
|
|
-- actually save the dialog (the one the NPC currently has)
|
|
yl_speak_up.save_dialog(n_id, yl_speak_up.speak_to[pname].dialog)
|
|
-- log all the changes
|
|
for i, c in ipairs(yl_speak_up.npc_was_changed[ n_id ]) do
|
|
yl_speak_up.log_change(pname, n_id, c)
|
|
end
|
|
-- clear list of changes
|
|
yl_speak_up.npc_was_changed[ n_id ] = {}
|
|
|
|
-- discard changes and continue on to the next dialog
|
|
elseif(edit_mode and fields.discard_dialog_changes) then
|
|
-- the current dialog and the one we want to show next may both be new dialogs;
|
|
-- if we just reload the old state, they would both get lost
|
|
local target_dialog_data = yl_speak_up.speak_to[pname].dialog.n_dialogs[ target_dialog ]
|
|
-- actually restore the old state and discard the changes by loading the dialog anew
|
|
yl_speak_up.speak_to[pname].dialog = load_dialog(n_id)
|
|
-- clear list of changes
|
|
yl_speak_up.npc_was_changed[ n_id ] = {}
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
-- do we have to save again after restoring current and target dialog?
|
|
local need_to_save = false
|
|
-- if the current dialog was a new one, it will be gone now - restore it
|
|
if(not(dialog.n_dialogs[ d_id ])) then
|
|
-- we can't just restore the current dialog - after all the player wanted
|
|
-- to discard the changes; but we can recreate the current dialog so that it
|
|
-- is in the "new dialog" state again
|
|
local next_id = tonumber(string.sub( d_id, 3))
|
|
yl_speak_up.add_new_dialog(dialog, pname, next_id)
|
|
yl_speak_up.log_change(pname, n_id, "Saved new dialog "..tostring( d_id )..".")
|
|
need_to_save = true
|
|
end
|
|
if(not(dialog.n_dialogs[ target_dialog ])) then
|
|
-- restore the new target dialog
|
|
dialog.n_dialogs[ target_dialog ] = target_dialog_data
|
|
yl_speak_up.log_change(pname, n_id, "Saved new dialog "..tostring( target_dialog )..".")
|
|
need_to_save = true
|
|
end
|
|
if(need_to_save) then
|
|
yl_speak_up.save_dialog(n_id, dialog)
|
|
end
|
|
end
|
|
|
|
-- are there any changes which might be saved or discarded?
|
|
if(edit_mode
|
|
and yl_speak_up.npc_was_changed[ n_id ]
|
|
and #yl_speak_up.npc_was_changed[ n_id ] > 0) then
|
|
|
|
local target_name = "quit"
|
|
if(target_dialog and target_dialog ~= "") then
|
|
target_name = "go on to dialog "..minetest.formspec_escape(target_dialog)
|
|
end
|
|
|
|
local d_id = yl_speak_up.speak_to[pname].d_id
|
|
-- reverse the order of the changes in the log so that newest are topmost
|
|
local text = ""
|
|
for i,t in ipairs(yl_speak_up.npc_was_changed[ n_id ]) do
|
|
text = minetest.formspec_escape(t).."\n"..text
|
|
end
|
|
-- build a formspec showing the changes to this dialog and ask for save
|
|
local formspec =
|
|
"formspec_version[3]"..
|
|
"size[14,6.2]"..
|
|
"bgcolor[#00000000;false]"..
|
|
"label[0.2,0.2;You are about to leave dialog "..minetest.formspec_escape(d_id)..
|
|
" and "..target_name..".]"..
|
|
"label[0.2,0.65;These changes have been applied to dialog "..
|
|
minetest.formspec_escape(d_id)..":]"..
|
|
"hypertext[0.2,1;13.5,4;list_of_changes;<normal>"..
|
|
minetest.formspec_escape(text) .. "\n</normal>".."]"..
|
|
"button[1.2,5.2;3,0.9;discard_dialog_changes;Discard changes]"..
|
|
"button[5.7,5.2;3,0.9;back_to_dialog_changes;Back]"..
|
|
"button[10.2,5.2;3,0.9;save_dialog_changes;Save changes]"..
|
|
"tooltip[save_dialog_changes;Save all changes to this dialog and "..target_name..".]"..
|
|
"tooltip[discard_dialog_changes;Undo all changes and "..target_name..".]"..
|
|
"tooltip[back_to_dialog_changes;Go back to dialog "..
|
|
minetest.formspec_escape(d_id).." and continue editing it.]"..
|
|
|
|
-- hidden field telling about the target dialog
|
|
"field[20,20;0.1,0.1;goto_target_dialog;target_dialog;"..minetest.formspec_escape(target_dialog).."]"
|
|
-- actaully show the formspec to the player
|
|
minetest.show_formspec(pname, "yl_speak_up:confirm_save", formspec)
|
|
return
|
|
end
|
|
-- if there is nothing that needs to be saved or discarded: allow quit/exit
|
|
if fields.quit or fields.button_exit then
|
|
yl_speak_up.edit_mode[pname] = nil
|
|
-- Formspec was quit
|
|
yl_speak_up.speak_to[pname] = nil
|
|
return
|
|
end
|
|
-- end edit mode and start a new chat - but this time in normal mode
|
|
if not(target_dialog) or target_dialog == "" then
|
|
yl_speak_up.edit_mode[pname] = nil
|
|
target_dialog = nil
|
|
end
|
|
|
|
-- the trade list is not really a dialog...
|
|
if(target_dialog == "trade_list") then
|
|
minetest.show_formspec(pname, "yl_speak_up:trade_list",
|
|
yl_speak_up.get_fs_trade_list(player))
|
|
return
|
|
end
|
|
-- move on to the target dialog
|
|
yl_speak_up.speak_to[pname].d_id = target_dialog
|
|
minetest.show_formspec(pname, "yl_speak_up:talk",
|
|
yl_speak_up.get_fs_talkdialog(player, n_id, target_dialog))
|
|
end
|
|
|
|
|
|
-- helper function for sorting options/answers using options[o_id].o_sort
|
|
-- (or dialogs by d_sort)
|
|
yl_speak_up.get_sorted_options = function(options, sort_by)
|
|
local sorted_list = {}
|
|
for k,v in pairs(options) do
|
|
table.insert(sorted_list, k)
|
|
end
|
|
table.sort(sorted_list,
|
|
function(a,b)
|
|
-- sadly not all entries are numeric
|
|
if(tonumber(options[a][sort_by]) and tonumber(options[b][sort_by])) then
|
|
return (tonumber(options[a][sort_by]) < tonumber(options[b][sort_by]))
|
|
-- numbers have a higher priority
|
|
elseif(tonumber(options[a][sort_by])) then
|
|
return true
|
|
elseif(tonumber(options[b][sort_by])) then
|
|
return false
|
|
-- if the value is the same: sort by index
|
|
elseif(options[a][sort_by] == options[b][sort_by]) then
|
|
return (a < b)
|
|
else
|
|
return (options[a][sort_by] < options[b][sort_by])
|
|
end
|
|
end
|
|
)
|
|
return sorted_list
|
|
end
|
|
|
|
|
|
-- helper function for formspec "yl_speak_up:talk" *and* formspec "yl_speak_up:edit_option_dialog"
|
|
-- when a parameter was changed in edit mode;
|
|
-- this is called when the player is in edit_mode (editing the NPC);
|
|
-- the function checks if the player has changed any parameters
|
|
-- Parameters:
|
|
-- pname player name
|
|
-- fields the fields returned from the formspec
|
|
-- Returns:
|
|
-- result table with information about what was added
|
|
-- (for now, only result.show_next_option is of intrest in the option edit menu)
|
|
yl_speak_up.edit_mode_apply_changes = function(pname, fields)
|
|
local n_id = yl_speak_up.edit_mode[pname]
|
|
local d_id = yl_speak_up.speak_to[pname].d_id
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
|
|
-- this way we can store the actual changes and present them to the player for saving
|
|
if(not(yl_speak_up.npc_was_changed[ n_id ])) then
|
|
yl_speak_up.npc_was_changed[ n_id ] = {}
|
|
end
|
|
|
|
|
|
-- nothing to do if that dialog does not exist
|
|
if(not(d_id) or not(dialog.n_dialogs) or not(dialog.n_dialogs[ d_id ])) then
|
|
return
|
|
end
|
|
|
|
-- allow owner to mute/unmute npc (would be bad if players can already see what is going
|
|
-- to happen while the owner creates a long quest)
|
|
-- mute/unmute gets logged in the function and does not need extra log entries
|
|
local obj = yl_speak_up.speak_to[pname].obj
|
|
if(fields.mute_npc and obj) then
|
|
yl_speak_up.set_muted(pname, obj, true)
|
|
elseif(fields.un_mute_npc and obj) then
|
|
yl_speak_up.set_muted(pname, obj, false)
|
|
end
|
|
|
|
|
|
-- new options etc. may be added; store these IDs so that we can switch to the right target
|
|
local result = {}
|
|
|
|
-- make this the first dialog shown when starting a conversation
|
|
if(fields.make_first_option) then
|
|
-- check which dialog(s) previously had the highest priority and change thsoe
|
|
for k, v in pairs(dialog.n_dialogs) do
|
|
if(v and v.d_sort and (v.d_sort=="0" or v.d_sort==0)) then
|
|
-- try to derive a sensible future sort priority from the key:
|
|
-- here we make use of the d_<nr> pattern; but even if that fails to yield
|
|
-- a number, the sort function will later be able to deal with it anyway
|
|
local new_priority = string.sub(k, 3)
|
|
dialog.n_dialogs[ k ].d_sort = new_priority
|
|
end
|
|
end
|
|
-- actually make this the chat with the highest priority
|
|
dialog.n_dialogs[ d_id ].d_sort = "0"
|
|
-- this is not immediately saved, even though the changes to the previous dialog with
|
|
-- the highest priority cannot be automaticly undone (but as long as it is not saved,
|
|
-- it really does not matter; and when saving, the player has to take some care)
|
|
table.insert(yl_speak_up.npc_was_changed[ n_id ],
|
|
"Dialog "..d_id..": Turned into new start dialog.")
|
|
end
|
|
|
|
-- detect changes to d_text: text of the dialog (what the npc is saying)
|
|
-- (only happens in dialog edit menu)
|
|
if(fields.d_text and dialog.n_dialogs[ d_id ].d_text ~= fields.d_text) then
|
|
-- store that there have been changes to this npc
|
|
table.insert(yl_speak_up.npc_was_changed[ n_id ],
|
|
"Dialog "..d_id..": d_text (what the NPC says) was changed from \""..
|
|
tostring( dialog.n_dialogs[ d_id ].d_text)..
|
|
"\" to \""..tostring(fields.d_text).."\".")
|
|
-- actually change the text - but do not save to disk yet
|
|
dialog.n_dialogs[ d_id ].d_text = fields.d_text
|
|
end
|
|
|
|
-- add a new option/answer
|
|
if(fields[ "add_option"]) then
|
|
-- count the options/answers so that the limit is not exceeded
|
|
local anz_options = 0
|
|
local future_o_id = "o_" .. find_next_id(dialog.n_dialogs[d_id].d_options)
|
|
if dialog.n_dialogs[d_id].d_options == nil then
|
|
dialog.n_dialogs[d_id].d_options = {}
|
|
else
|
|
local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs[d_id].d_options, "o_sort")
|
|
anz_options = #sorted_list
|
|
end
|
|
-- we don't want an infinite amount of answers per dialog
|
|
if(anz_options >= yl_speak_up.max_number_of_options_per_dialog) then
|
|
minetest.chat_send_player(pname, "Sorry. Only "..
|
|
tostring(yl_speak_up.max_number_of_options_per_dialog)..
|
|
" options/answers are allowed per dialog.")
|
|
fields.add_option = nil
|
|
else
|
|
dialog.n_dialogs[d_id].d_options[future_o_id] = {
|
|
o_id = future_o_id,
|
|
o_hide_when_prerequisites_not_met = "false",
|
|
o_grey_when_prerequisites_not_met = "false",
|
|
o_sort = -1,
|
|
o_text_when_prerequisites_not_met = "",
|
|
o_text_when_prerequisites_met = "",
|
|
}
|
|
-- necessary in order for it to work
|
|
local s = sanitize_sort(dialog.n_dialogs[d_id].d_options, yl_speak_up.speak_to[pname].o_sort)
|
|
dialog.n_dialogs[d_id].d_options[future_o_id].o_sort = s
|
|
table.insert(yl_speak_up.npc_was_changed[ n_id ],
|
|
"Dialog "..d_id..": Added new option/answer "..future_o_id..".")
|
|
-- if this is selected in the options edit menu, we want to move straight on to the new option
|
|
result["show_next_option"] = future_o_id
|
|
end
|
|
end
|
|
|
|
if(fields[ "del_option"] and fields.o_id and dialog.n_dialogs[d_id].d_options[fields.o_id]) then
|
|
local o_id = fields.o_id
|
|
-- which dialog to show instead of the deleted one?
|
|
local next_o_id = o_id
|
|
local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs[d_id].d_options, "o_sort")
|
|
for i, o in ipairs(sorted_list) do
|
|
if(o == o_id and sorted_list[ i+1 ]) then
|
|
next_o_id = sorted_list[ i+1 ]
|
|
elseif(o == o_id and sorted_list[ i-1 ]) then
|
|
next_o_id = sorted_list[ i-1 ]
|
|
end
|
|
end
|
|
table.insert(yl_speak_up.npc_was_changed[ n_id ],
|
|
"Dialog "..d_id..": Option "..tostring(o_id).." deleted.")
|
|
-- actually delete the dialog
|
|
dialog.n_dialogs[d_id].d_options[o_id] = nil
|
|
-- the current dialog is deleted; we need to show another one
|
|
result["show_next_option"] = next_o_id
|
|
-- after deleting the entry, all previous/further changes to it are kind of unintresting
|
|
return result
|
|
end
|
|
|
|
-- ignore entries to o_sort if they are not a number
|
|
if(fields[ "edit_option_o_sort"]
|
|
and tonumber(fields[ "edit_option_o_sort"])
|
|
and fields.o_id and dialog.n_dialogs[d_id].d_options[fields.o_id]) then
|
|
local o_id = fields.o_id
|
|
local new_nr = tonumber(fields[ "edit_option_o_sort"])
|
|
local old_nr = tonumber(dialog.n_dialogs[d_id].d_options[o_id].o_sort)
|
|
-- if the nr is -1 (do not show) then we are done already: nothing to do
|
|
if(old_nr == new_nr) then
|
|
-- -1: do not list as option/answer (but still store and keep it)
|
|
elseif(new_nr == -1 and old_nr ~= -1) then
|
|
dialog.n_dialogs[d_id].d_options[o_id].o_sort = "-1"
|
|
table.insert(yl_speak_up.npc_was_changed[ n_id ],
|
|
"Dialog "..d_id..": Option "..tostring(o_id).." was set to -1 (do not list).")
|
|
else
|
|
-- get the old sorted list
|
|
local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs[d_id].d_options, "o_sort")
|
|
-- negative numbers are not shown
|
|
local entries_shown_list = {}
|
|
for i, o in ipairs(sorted_list) do
|
|
local n = tonumber(dialog.n_dialogs[d_id].d_options[o].o_sort)
|
|
if(n and n > 0 and o ~= o_id) then
|
|
table.insert(entries_shown_list, o)
|
|
end
|
|
end
|
|
-- insert the entry at the new position and let lua do the job
|
|
table.insert(entries_shown_list, new_nr, o_id)
|
|
-- take the indices from that new list as new sort values and store them;
|
|
-- this has the side effect that duplicate entries get sorted out as well
|
|
for i, o in ipairs(entries_shown_list) do
|
|
dialog.n_dialogs[d_id].d_options[o].o_sort = tostring(i)
|
|
end
|
|
-- store that there was a cahnge
|
|
table.insert(yl_speak_up.npc_was_changed[ n_id ],
|
|
"Dialog "..d_id..": Option "..tostring(o_id).." was moved to position "..
|
|
tostring(new_nr)..".")
|
|
end
|
|
end
|
|
|
|
-- changes to options are not possible if there are none
|
|
if(dialog.n_dialogs[ d_id ].d_options) then
|
|
|
|
-- detect changes to text_option_<o_id>: text for option <o_id>
|
|
for k, v in pairs(dialog.n_dialogs[ d_id ].d_options) do
|
|
if( fields[ "text_option_"..k ]
|
|
and fields[ "text_option_"..k ] ~= v.o_text_when_prerequisites_met ) then
|
|
-- store that there have been changes to this npc
|
|
table.insert(yl_speak_up.npc_was_changed[ n_id ],
|
|
"Dialog "..d_id..": The text for option "..tostring(k)..
|
|
" was changed from \""..tostring(v.o_text_when_prerequisites_met)..
|
|
"\" to \""..tostring(fields[ "text_option_"..k]).."\".")
|
|
-- actually change the text of the option
|
|
dialog.n_dialogs[ d_id ].d_options[ k ].o_text_when_prerequisites_met = fields[ "text_option_"..k ]
|
|
end
|
|
end
|
|
|
|
-- detect changes to d_id_<o_id>: target dialog for option <o_id>
|
|
for k, v in pairs(dialog.n_dialogs[ d_id ].d_options) do
|
|
if( fields[ "d_id_"..k ] and v.o_results) then
|
|
for kr, vr in pairs(v.o_results) do
|
|
if( vr.r_type == "dialog" and vr.r_value and vr.r_value ~= fields[ "d_id_"..k ]) then
|
|
-- this may also point to a new dialog
|
|
if(fields[ "d_id_"..k ] == yl_speak_up.text_new_dialog_id) then
|
|
-- create a new dialog and show it as new target dialog - but do not display this dialog directly (the player may follow the -> button)
|
|
fields[ "d_id_"..k ] = yl_speak_up.add_new_dialog(dialog, pname, nil)
|
|
end
|
|
-- store that there have been changes to this npc
|
|
table.insert(yl_speak_up.npc_was_changed[ n_id ],
|
|
"Dialog "..d_id..": The target dialog for option "..
|
|
tostring(k).." was changed from "..
|
|
tostring(vr.r_value)..
|
|
" to "..tostring(fields[ "d_id_"..k])..".")
|
|
-- actually change the target dialog
|
|
vr.r_value = fields[ "d_id_"..k ]
|
|
-- in options edit menu: show this update
|
|
result["show_next_option"] = k
|
|
end
|
|
end
|
|
-- this might be the first result
|
|
elseif( fields["d_id_"..k ]) then
|
|
-- this may also point to a new dialog
|
|
if(fields[ "d_id_"..k ] == yl_speak_up.text_new_dialog_id) then
|
|
-- create a new dialog and show it as new target dialog - but do not display this dialog directly (the player may follow the -> button)
|
|
fields[ "d_id_"..k ] = yl_speak_up.add_new_dialog(dialog, pname, nil)
|
|
end
|
|
-- store that a new option has been added to this dialog
|
|
table.insert(yl_speak_up.npc_was_changed[ n_id ],
|
|
"Dialog "..d_id..": The target dialog for option "..
|
|
tostring(k).." was changed from -default- to "..
|
|
tostring(fields[ "d_id_"..k])..".")
|
|
|
|
-- create a new result (first the id, then the actual result)
|
|
local future_r_id = yl_speak_up.add_new_result(dialog, d_id, k)
|
|
-- actually store the new result
|
|
dialog.n_dialogs[d_id].d_options[k].o_results[future_r_id] = {
|
|
r_id = future_r_id,
|
|
r_type = "dialog",
|
|
r_value = fields[ "d_id_"..k ]}
|
|
-- in options edit menu: show this update
|
|
result["show_next_option"] = k
|
|
end
|
|
end
|
|
end
|
|
|
|
-- add a new dialog; either via "+" button or "New dialog" in dialog dropdown menu
|
|
-- this has to be done after all the other changes because those (text changes etc.) still
|
|
-- apply to the *old* dialog
|
|
if(fields.show_new_dialog
|
|
or(fields["d_id"] and fields["d_id"] == yl_speak_up.text_new_dialog_id)) then
|
|
-- create the new dialog and make sure it gets shown
|
|
local d_id = yl_speak_up.add_new_dialog(dialog, pname, nil)
|
|
-- actually show the new dialog
|
|
fields["d_id"] = d_id
|
|
fields["show_new_dialog"] = nil
|
|
end
|
|
|
|
-- delete one empty dialog
|
|
if(fields.delete_this_empty_dialog) then
|
|
local anz_options = 0
|
|
-- we need to show a new dialog after this one was deleted
|
|
local new_dialog = d_id
|
|
local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs, "d_sort")
|
|
for i, k in ipairs(sorted_list) do
|
|
-- count the options of this dialog
|
|
if(k == d_id) then
|
|
if(dialog.n_dialogs[d_id].d_options) then
|
|
for o, w in pairs(dialog.n_dialogs[d_id].d_options) do
|
|
anz_options = anz_options + 1
|
|
end
|
|
end
|
|
if(sorted_list[i+1]) then
|
|
new_dialog = sorted_list[i+1]
|
|
elseif(sorted_list[i-1]) then
|
|
new_dialog = sorted_list[i-1]
|
|
end
|
|
end
|
|
end
|
|
-- there needs to be one dialog left after deleting this one,
|
|
if(#sorted_list > 1
|
|
-- this dialog isn't allowed to hold any more options/answers
|
|
and anz_options == 0
|
|
-- we really found a new dialog to show
|
|
and new_dialog ~= d_id
|
|
-- and the text needs to be empty
|
|
and dialog.n_dialogs[ d_id ].d_text == "") then
|
|
-- actually delete this dialog
|
|
dialog.n_dialogs[ d_id ] = nil
|
|
-- ..and store it to disk
|
|
delete_dialog(n_id, d_id)
|
|
yl_speak_up.log_change(pname, n_id,
|
|
"Deleted dialog "..tostring(d_id)..".")
|
|
-- switch to another dialog (this one was deleted after all)
|
|
fields["d_id"] = new_dialog
|
|
fields["show_new_dialog"] = nil
|
|
else
|
|
minetest.chat_send_player(pname, "Sorry. This dialog cannot be deleted (yet). "..
|
|
"It is either the only dialog left or has a non-empty text or has at "..
|
|
"least on remaining option/answer.")
|
|
end
|
|
end
|
|
|
|
-- not in options edit menu?
|
|
local o_id = fields.o_id
|
|
if(not(o_id)) then
|
|
return result
|
|
end
|
|
|
|
local d_option = dialog.n_dialogs[ d_id ].d_options[ o_id ]
|
|
-- change alternate text when preconditions are not met
|
|
-- (only happens in options edit menu)
|
|
if(fields.option_text_not_met and d_option
|
|
and d_option.o_text_when_prerequisites_not_met ~= fields.option_text_not_met) then
|
|
-- add change to changelog
|
|
table.insert(yl_speak_up.npc_was_changed[ n_id ],
|
|
"Dialog "..d_id..": The alternate text for option "..tostring(o_id)..
|
|
" was changed from \""..
|
|
tostring(d_option.o_text_when_prerequisites_not_met).."\" to \""..
|
|
tostring(fields.option_text_not_met).."\".")
|
|
-- actually change the text of the option
|
|
d_option.o_text_when_prerequisites_not_met = fields.option_text_not_met
|
|
end
|
|
|
|
-- handle hide/grey out/show alternate answer
|
|
-- (only happens in options edit menu)
|
|
if(fields.hide_or_grey_or_alternate_answer and d_option) then
|
|
if(fields.hide_or_grey_or_alternate_answer == "..hide this answer."
|
|
and d_option.o_hide_when_prerequisites_not_met ~= "true") then
|
|
d_option.o_hide_when_prerequisites_not_met = "true"
|
|
d_option.o_grey_when_prerequisites_not_met = "false"
|
|
table.insert(yl_speak_up.npc_was_changed[ n_id ],
|
|
"Dialog "..d_id..": If precondition for option "..tostring(o_id)..
|
|
" is not met, hide option/answer.")
|
|
-- make sure we show this options update next
|
|
result["show_next_option"] = o_id
|
|
elseif(fields.hide_or_grey_or_alternate_answer == "..grey out this answer."
|
|
and d_option.o_grey_when_prerequisites_not_met ~= "true") then
|
|
d_option.o_hide_when_prerequisites_not_met = "false"
|
|
d_option.o_grey_when_prerequisites_not_met = "true"
|
|
table.insert(yl_speak_up.npc_was_changed[ n_id ],
|
|
"Dialog "..d_id..": If precondition for option "..tostring(o_id)..
|
|
" is not met, grey out option/answer.")
|
|
result["show_next_option"] = o_id
|
|
elseif(fields.hide_or_grey_or_alternate_answer == "..display the following alternate answer:"
|
|
and (d_option.o_hide_when_prerequisites_not_met ~= "false"
|
|
or d_option.o_grey_when_prerequisites_not_met) ~= "false") then
|
|
d_option.o_hide_when_prerequisites_not_met = "false"
|
|
d_option.o_grey_when_prerequisites_not_met = "false"
|
|
table.insert(yl_speak_up.npc_was_changed[ n_id ],
|
|
"Dialog "..d_id..": If precondition for option "..tostring(o_id)..
|
|
" is not met, show alternate option/answer.")
|
|
result["show_next_option"] = o_id
|
|
end
|
|
end
|
|
|
|
-- currently only contains result["show_new_option"] (which is needed for options edit menu)
|
|
return result
|
|
end
|
|
-- end of yl_speak_up.edit_mode_apply_changes
|
|
|
|
|
|
yl_speak_up.input_confirm_save = function(player, formname, fields)
|
|
if formname ~= "yl_speak_up:confirm_save" then
|
|
return
|
|
end
|
|
yl_speak_up.save_changes_and_switch_to_other_dialog(player, fields, fields.goto_target_dialog)
|
|
end
|
|
|
|
|
|
-- process input from formspec created in get_fs_edit_option_dialog(..)
|
|
yl_speak_up.input_edit_option_dialog = function(player, formname, fields)
|
|
if formname ~= "yl_speak_up:edit_option_dialog" then
|
|
return
|
|
end
|
|
local pname = player:get_player_name()
|
|
|
|
-- Is the player working on this particular npc?
|
|
local edit_mode = (yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id)
|
|
if(not(edit_mode) or not(fields.o_id)) then
|
|
return
|
|
end
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local d_id = yl_speak_up.speak_to[pname].d_id
|
|
-- this is passed on as a hidden field
|
|
local o_id = fields.o_id
|
|
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
local n_dialog = dialog.n_dialogs[d_id]
|
|
local d_option = n_dialog.d_options[o_id]
|
|
|
|
-- this meny is specific to an option for a dialog; if no dialog is selected, we really
|
|
-- can't know what to do
|
|
if(not(d_id)) then
|
|
return
|
|
end
|
|
|
|
-- handles changes to o_text_when_prerequisites_met, target dialog, adding of a new dialog
|
|
local result = yl_speak_up.edit_mode_apply_changes(pname, fields)
|
|
-- if a new option was added or the target dialog of this one changed, display the right new option
|
|
if(result and result["show_next_option"] and n_dialog.d_options[result["show_next_option"]]) then
|
|
minetest.show_formspec(pname, "yl_speak_up:edit_option_dialog",
|
|
yl_speak_up.get_fs_edit_option_dialog(player, n_id, d_id, result["show_next_option"]))
|
|
return
|
|
end
|
|
|
|
-- back to the main dialog window?
|
|
-- (this also happens when the last option was deleted)
|
|
if(fields.show_current_dialog or fields.quit or fields.button_exit or not(d_option) or fields.del_option) then
|
|
minetest.show_formspec(pname, "yl_speak_up:talk",
|
|
yl_speak_up.get_fs_talkdialog(player, n_id, d_id))
|
|
return
|
|
end
|
|
|
|
-- add or edit the trade associated with this dialog and option
|
|
if(fields.effect_add_trade) then
|
|
-- remember which option was selected
|
|
yl_speak_up.speak_to[pname].o_id = o_id
|
|
-- do not switch target dialog (we are in edit mode)
|
|
yl_speak_up.speak_to[pname].target_d_id = nil
|
|
-- create a new trade for this dialog and option - with ID "<d_id> <o_id>"
|
|
minetest.show_formspec(pname, "yl_speak_up:add_trade_simple",
|
|
yl_speak_up.get_fs_add_trade_simple(player,
|
|
tostring(d_id).." "..tostring(o_id)))
|
|
return
|
|
|
|
-- the player wants to see the previous option/answer
|
|
elseif(fields.edit_option_prev) then
|
|
-- sort all options by o_sort
|
|
local sorted_list = yl_speak_up.get_sorted_options(n_dialog.d_options, "o_sort")
|
|
local o_found = o_id
|
|
for i, o in ipairs(sorted_list) do
|
|
if(o == o_id and sorted_list[ i-1]) then
|
|
o_found = sorted_list[ i-1 ]
|
|
end
|
|
end
|
|
-- show that dialog; fallback: show the same (o_id) again
|
|
minetest.show_formspec(pname, "yl_speak_up:edit_option_dialog",
|
|
yl_speak_up.get_fs_edit_option_dialog(player, n_id, d_id, o_found))
|
|
return
|
|
|
|
-- the player wants to see the next option/answer
|
|
elseif(fields.edit_option_next) then
|
|
-- sort all options by o_sort
|
|
local sorted_list = yl_speak_up.get_sorted_options(n_dialog.d_options, "o_sort")
|
|
local o_found = o_id
|
|
for i, o in ipairs(sorted_list) do
|
|
if(o == o_id and sorted_list[ i+1 ]) then
|
|
o_found = sorted_list[ i+1 ]
|
|
end
|
|
end
|
|
-- show that dialog; fallback: show the same (o_id) again
|
|
minetest.show_formspec(pname, "yl_speak_up:edit_option_dialog",
|
|
yl_speak_up.get_fs_edit_option_dialog(player, n_id, d_id, o_found))
|
|
return
|
|
|
|
-- show the trade associated with this dialog and option
|
|
elseif(fields.effect_show_trade) then
|
|
-- remember which option was selected
|
|
yl_speak_up.speak_to[pname].o_id = o_id
|
|
-- do not switch target dialog (we are in edit mode)
|
|
yl_speak_up.speak_to[pname].target_d_id = nil
|
|
-- show the trade with ID "<d_id> <o_id>"
|
|
minetest.show_formspec(pname, "yl_speak_up:do_trade_simple",
|
|
yl_speak_up.get_fs_trade_simple(player,
|
|
tostring(d_id).." "..tostring(o_id)))
|
|
return
|
|
end
|
|
|
|
-- if ESC is pressed or anything else unpredicted happens: go back to the main dialog edit window
|
|
-- reason: don't loose any unsaved changes to the dialog
|
|
minetest.show_formspec(pname, "yl_speak_up:talk",
|
|
yl_speak_up.get_fs_talkdialog(player, n_id, d_id))
|
|
end
|
|
|
|
|
|
-- identify multiple results that lead to target dialogs
|
|
yl_speak_up.check_for_disambigous_results = function(n_id, pname)
|
|
local errors_found = false
|
|
local dialog = load_dialog(n_id)
|
|
-- 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
|
|
minetest.chat_send_player(pname, "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!")
|
|
errors_found = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return errors_found
|
|
end
|
|
|
|
|
|
yl_speak_up.input_talk = function(player, formname, fields)
|
|
if formname ~= "yl_speak_up:talk" then
|
|
return
|
|
end
|
|
|
|
local pname = player:get_player_name()
|
|
local o = ""
|
|
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
-- the player is trying to save the initial configuration
|
|
if(fields.save_initial_config) then
|
|
-- is the player allowed to initialize this npc?
|
|
if(not(yl_speak_up.may_edit_npc(player, n_id))) then
|
|
return
|
|
end
|
|
if(not(fields.n_npc) or string.len(fields.n_npc) < 2) then
|
|
minetest.chat_send_player(pname, "The name of your NPC needs to be at least two characters long.")
|
|
return
|
|
end
|
|
if(not(fields.n_description) or string.len(fields.n_description) < 2) then
|
|
minetest.chat_send_player(pname, "Please provide a description of your NPC!")
|
|
return
|
|
end
|
|
-- sensible length limit
|
|
if(string.len(fields.n_npc)>40 or string.len(fields.n_description)>40) then
|
|
minetest.chat_send_player(pname, "The name and description of your NPC cannot be longer than 40 characters.")
|
|
return
|
|
end
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
local d_id = yl_speak_up.speak_to[pname].d_id
|
|
local count = 0
|
|
if(dialog and dialog.n_dialogs) then
|
|
for k,v in pairs(dialog.n_dialogs) do
|
|
count = count + 1
|
|
end
|
|
end
|
|
-- give the NPC its first dialog
|
|
if(not(dialog) or count==0) then
|
|
local f = {}
|
|
-- create a new dialog
|
|
f.d_id = yl_speak_up.text_new_dialog_id
|
|
-- ...with this text
|
|
f.d_text = "$GOOD_DAY$ $PLAYER_NAME$,\nI am $NPC_NAME$. I don't know much yet.\n"..
|
|
"Hopefully $OWNER_NAME$ will teach me to talk soon."
|
|
-- it is the first, initial dialog
|
|
f.d_sort = "0"
|
|
f.n_npc = fields.n_npc
|
|
f.n_description = fields.n_description
|
|
f.npc_owner = yl_speak_up.npc_owner[ n_id ]
|
|
-- create and save the first dialog for this npc
|
|
local dialog = fields_to_dialog(pname, f)
|
|
yl_speak_up.save_dialog(n_id, dialog)
|
|
yl_speak_up.speak_to[pname].dialog = dialog
|
|
|
|
yl_speak_up.log_change(pname, n_id,
|
|
"Initial config saved. "..
|
|
"NPC name: \""..tostring(fields.n_npc)..
|
|
"\" Description: \""..tostring(fields.n_description).."\"")
|
|
-- just change name and description
|
|
else
|
|
dialog = yl_speak_up.speak_to[pname].dialog
|
|
dialog.n_npc = fields.n_npc
|
|
dialog.n_description = fields.n_description
|
|
yl_speak_up.save_dialog(n_id, dialog)
|
|
|
|
yl_speak_up.log_change(pname, n_id,
|
|
"Name and/or description changed. "..
|
|
"NPC name: \""..tostring(fields.n_npc)..
|
|
"\" Description: \""..tostring(fields.n_description).."\"")
|
|
end
|
|
dialog = yl_speak_up.speak_to[pname].dialog
|
|
|
|
-- show nametag etc.
|
|
if yl_speak_up.speak_to[pname].obj then
|
|
local obj = yl_speak_up.speak_to[pname].obj
|
|
local ent = obj:get_luaentity()
|
|
if ent ~= nil then
|
|
ent.yl_speak_up.npc_name = dialog.n_npc
|
|
ent.yl_speak_up.npc_description = dialog.n_description
|
|
ent.owner = dialog.npc_owner
|
|
local i_text = dialog.n_npc .. "\n" .. dialog.n_description .. "\n" .. yl_speak_up.infotext
|
|
obj:set_properties({infotext = i_text})
|
|
yl_speak_up.update_nametag(ent)
|
|
end
|
|
end
|
|
|
|
-- actually start a chat with our new npc
|
|
minetest.show_formspec(pname, "yl_speak_up:talk",
|
|
yl_speak_up.get_fs_talkdialog(player, n_id, d_id))
|
|
return
|
|
end
|
|
|
|
-- Is the player working on this particular npc?
|
|
local edit_mode = (yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id)
|
|
|
|
-- if in edit mode: detect if something was changed;
|
|
if(edit_mode or fields.button_edit_name_and_description) then
|
|
local result = yl_speak_up.edit_mode_apply_changes(pname, fields)
|
|
end
|
|
|
|
-- the player wants to change name and description; show the formspec
|
|
if(edit_mode and fields.button_edit_name_and_description) then
|
|
-- this is not the initial config - but the same formspec can be used
|
|
minetest.show_formspec(pname, "yl_speak_up:talk", get_fs_initial_config(player, n_id, yl_speak_up.speak_to[pname].d_id, false))
|
|
return
|
|
end
|
|
|
|
-- the player wants to access the inventory of the NPC
|
|
if(edit_mode and fields.show_inventory) then
|
|
-- the inventory is just an inventory with a back button; come back to this dialog here
|
|
minetest.show_formspec(pname, "yl_speak_up:inventory", yl_speak_up.get_fs_inventory(player))
|
|
return
|
|
end
|
|
|
|
-- change skin, cape and wielded items
|
|
if(edit_mode and fields.edit_skin) then
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
-- necessary so that the fashin formspec can be created
|
|
yl_speak_up.speak_to[pname].n_npc = dialog.n_npc
|
|
minetest.show_formspec(pname, "yl_speak_up:fashion", yl_speak_up.get_fs_fashion(pname))
|
|
return
|
|
end
|
|
|
|
-- start edit mode (requires npc_talk_owner)
|
|
if fields.button_start_edit_mode then
|
|
-- check if this particular NPC is really owned by this player or if the player has global privs
|
|
if(not(yl_speak_up.may_edit_npc(player, n_id))) then
|
|
minetest.chat_send_player(pname, "Sorry. You do not have the npc_talk_owner or npc_master priv.")
|
|
return
|
|
end
|
|
-- the staff allows to create multiple target dialogs as result; this makes no sense
|
|
-- and is too disambigous
|
|
if(yl_speak_up.check_for_disambigous_results(n_id, pname)) then
|
|
-- this needs to be fixed by someone with a staff; we don't know which dialog is the right
|
|
-- result
|
|
return
|
|
end
|
|
-- enter edit mode with that particular NPC
|
|
yl_speak_up.edit_mode[pname] = yl_speak_up.speak_to[pname].n_id
|
|
-- start a new chat - but this time in edit mode
|
|
yl_speak_up.speak_to[pname].d_id = nil
|
|
minetest.show_formspec(pname, "yl_speak_up:talk",
|
|
yl_speak_up.get_fs_talkdialog(player, yl_speak_up.speak_to[pname].n_id, nil))
|
|
return
|
|
-- end edit mode (does not require the priv; will only switch back to normal behaviour)
|
|
elseif fields.button_end_edit_mode then
|
|
-- if there are any changes done: ask first and don't end edit mode yet
|
|
yl_speak_up.save_changes_and_switch_to_other_dialog(player, fields, nil)
|
|
return
|
|
end
|
|
|
|
if fields.quit or fields.button_exit then
|
|
-- if there are any changes done: ask first and don't quit yet
|
|
yl_speak_up.save_changes_and_switch_to_other_dialog(player, fields, nil)
|
|
return
|
|
end
|
|
|
|
if fields.button_up then
|
|
yl_speak_up.speak_to[pname].option_index =
|
|
yl_speak_up.speak_to[pname].option_index + yl_speak_up.max_number_of_buttons
|
|
minetest.show_formspec(
|
|
pname,
|
|
"yl_speak_up:talk",
|
|
yl_speak_up.get_fs_talkdialog(player, yl_speak_up.speak_to[pname].n_id, yl_speak_up.speak_to[pname].d_id)
|
|
)
|
|
return
|
|
elseif fields.button_down then --and yl_speak_up.speak_to[pname].option_index > yl_speak_up.max_number_of_buttons then
|
|
yl_speak_up.speak_to[pname].option_index =
|
|
yl_speak_up.speak_to[pname].option_index - yl_speak_up.max_number_of_buttons
|
|
if yl_speak_up.speak_to[pname].option_index < 0 then
|
|
yl_speak_up.speak_to[pname].option_index = 1
|
|
end
|
|
minetest.show_formspec(
|
|
pname,
|
|
"yl_speak_up:talk",
|
|
yl_speak_up.get_fs_talkdialog(player, yl_speak_up.speak_to[pname].n_id, yl_speak_up.speak_to[pname].d_id)
|
|
)
|
|
return
|
|
else
|
|
yl_speak_up.speak_to[pname].option_index = 1
|
|
end
|
|
|
|
-- the player wants to see the trade list
|
|
if(fields.show_trade_list) then
|
|
if(edit_mode) then
|
|
yl_speak_up.save_changes_and_switch_to_other_dialog(player, fields, "trade_list")
|
|
return
|
|
end
|
|
minetest.show_formspec(pname, "yl_speak_up:trade_list",
|
|
yl_speak_up.get_fs_trade_list(player))
|
|
return
|
|
end
|
|
|
|
for k, v in pairs(fields) do
|
|
local s = string.split(k, "_")
|
|
|
|
if
|
|
s[1] == "button" and s[2] ~= nil and s[2] ~= "" and s[2] ~= "exit" and s[2] ~= "back" and s[3] ~= nil and
|
|
s[2] ~= "up" and
|
|
s[2] ~= "down"
|
|
then
|
|
o = s[2] .. "_" .. s[3]
|
|
end
|
|
end
|
|
|
|
if not(edit_mode) and o == "" then
|
|
return
|
|
end
|
|
|
|
-- Let's check if the button was among the "allowed buttons". Only those may be executed
|
|
if not(edit_mode) and (yl_speak_up.speak_to[pname].allowed and yl_speak_up.speak_to[pname].allowed[o] == false) then
|
|
return
|
|
end
|
|
|
|
-- button was clicked, now let's execute the results
|
|
|
|
local d_id = yl_speak_up.speak_to[pname].d_id
|
|
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
|
|
|
|
-- all three buttons (pre(C)onditions, (Ef)fects, edit option) lead to the same new formspec
|
|
if( edit_mode ) then
|
|
local n_dialog = dialog.n_dialogs[d_id]
|
|
|
|
if(n_dialog and n_dialog.d_options) then
|
|
for o_id,v in pairs(n_dialog.d_options) do
|
|
if( fields["edit_option_"..o_id]
|
|
or fields["conditions_"..o_id]
|
|
or fields["effects_"..o_id]) then
|
|
minetest.show_formspec(pname, "yl_speak_up:edit_option_dialog",
|
|
yl_speak_up.get_fs_edit_option_dialog(
|
|
player, yl_speak_up.speak_to[pname].n_id, d_id, o_id))
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
-- in edit mode: another dialog was selected
|
|
if ( o == "" and edit_mode) then
|
|
-- if nothing better can be found: keep the old dialog
|
|
local show_dialog = d_id
|
|
-- dropdown menu was used; provided the dialog exists (and it's not the "New dialog" option)
|
|
-- (if a new dialog was added using the "+" button, fields.d_id gets set accordingly)
|
|
if(fields.d_id and fields.d_id ~= show_dialog and dialog.n_dialogs[fields.d_id]) then
|
|
show_dialog = fields.d_id
|
|
-- in edit mode: prev_dialog_../next_dialog_.. was selected
|
|
else
|
|
for k,v in pairs(dialog.n_dialogs) do
|
|
if(fields["prev_dialog_"..k]) then
|
|
show_dialog = k
|
|
elseif(fields["next_dialog_"..k]) then
|
|
show_dialog = k
|
|
end
|
|
end
|
|
end
|
|
-- if there are any changes done: ask first and don't switch to the new dialog yet
|
|
if(show_dialog ~= d_id) then
|
|
yl_speak_up.save_changes_and_switch_to_other_dialog(player, fields, show_dialog)
|
|
-- show the same dialog again
|
|
else
|
|
minetest.show_formspec(pname, "yl_speak_up:talk",
|
|
yl_speak_up.get_fs_talkdialog(player, n_id, d_id))
|
|
end
|
|
-- no option was selected - so we need to end this here
|
|
return
|
|
end
|
|
|
|
|
|
local n_dialog = dialog.n_dialogs[d_id]
|
|
|
|
-- if there are no options, there can be no results either
|
|
if(not(n_dialog) or not(n_dialog.d_options)) then
|
|
return
|
|
end
|
|
|
|
local d_option = n_dialog.d_options[o]
|
|
|
|
-- which dialog do we have to show next? default: keep this one
|
|
local target_dialog = d_id
|
|
|
|
-- Let's do something if results exist
|
|
if d_option.o_results ~= nil then
|
|
for k, v in pairs(d_option.o_results) do
|
|
-- in edit_mode, only the switching to the next dialog is executed (we want to edit,
|
|
-- not solve quests, get/take items, teleport...)
|
|
if not(edit_mode) and v.r_type == "give_item" then
|
|
local item = ItemStack(v.r_value)
|
|
|
|
if minetest.registered_items[item:get_name()] ~= nil then
|
|
local r = player:get_inventory():add_item("main", item)
|
|
else
|
|
say("Item not found!")
|
|
end
|
|
end
|
|
if not(edit_mode) and v.r_type == "take_item" then
|
|
local item = ItemStack(v.r_value)
|
|
|
|
if minetest.registered_items[item:get_name()] ~= nil then
|
|
local r = player:get_inventory():remove_item("main", item)
|
|
else
|
|
say("Item not found!")
|
|
end
|
|
end
|
|
if not(edit_mode) and v.r_type == "move" then
|
|
local target_pos = nil
|
|
local target_pos_valid = false
|
|
|
|
--pos like (100,20,400)
|
|
if minetest.string_to_pos(v.r_value) then
|
|
target_pos = minetest.string_to_pos(v.r_value)
|
|
target_pos_valid = true
|
|
end
|
|
|
|
--pos like 100,20,400
|
|
local maybe = string.split(v.r_value, ",")
|
|
if not target_pos_valid and maybe and tonumber(maybe[1]) and tonumber(maybe[2]) and tonumber(maybe[3]) and maybe[4] == nil and
|
|
tonumber(maybe[1]) <= 32000 and tonumber(maybe[1]) >= -32000 and
|
|
tonumber(maybe[2]) <= 32000 and tonumber(maybe[2]) >= -32000 and
|
|
tonumber(maybe[3]) <= 32000 and tonumber(maybe[3]) >= -32000 then
|
|
target_pos = {x=maybe[1],y=maybe[2],z=maybe[3]}
|
|
target_pos_valid = true
|
|
end
|
|
|
|
--pos like {x=100,y=20,z=400}
|
|
if not target_pos_valid and string.sub(v.r_value,1,1) == "{" and string.sub(v.r_value,-1,-1) == "}" then
|
|
local might_be_pos = minetest.deserialize("return " .. v.r_value)
|
|
if tonumber(might_be_pos.x) and tonumber(might_be_pos.x) <= 32000 and tonumber(might_be_pos.x) >= -32000 and
|
|
tonumber(might_be_pos.y) and tonumber(might_be_pos.y) <= 32000 and tonumber(might_be_pos.y) >= -32000 and
|
|
tonumber(might_be_pos.z) and tonumber(might_be_pos.z) <= 32000 and tonumber(might_be_pos.z) >= -32000 then
|
|
target_pos = might_be_pos
|
|
target_pos_valid = true
|
|
end
|
|
|
|
end
|
|
|
|
if target_pos_valid == true then
|
|
player:set_pos(target_pos)
|
|
if vector.distance(player:get_pos(),target_pos) >= 2 then
|
|
say("Something went wrong! Player wasn't moved properly.")
|
|
end
|
|
end
|
|
|
|
-- Debug
|
|
if target_pos_valid == false then
|
|
local obj = yl_speak_up.speak_to[pname].obj
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local npc = get_number_from_id(n_id)
|
|
if obj:get_luaentity() and tonumber(npc) then
|
|
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not move player "..pname.." because the content of "..v.r_id.." is wrong:"..dump(v.r_value))
|
|
else
|
|
minetest.log("error","[MOD] yl_speak_up: NPC with unknown ID or without proper object could not move player "..dump(pname).." because the content of "..v.r_id.." is wrong:"..dump(v.r_value))
|
|
end
|
|
end
|
|
|
|
end
|
|
if not(edit_mode) and v.r_type == "function" then
|
|
local code = v.r_value
|
|
if code:byte(1) == 27 then
|
|
local obj = yl_speak_up.speak_to[pname].obj
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local npc = get_number_from_id(n_id)
|
|
if obj:get_luaentity() and tonumber(npc) then
|
|
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not compile the content of "..v.r_id.." :"..dump(v.r_value) .. " because of illegal bytecode for player "..pname)
|
|
else
|
|
minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not compile the content of "..v.r_id.." :"..dump(v.r_value) .. " for player unknown because of illegal bytecode")
|
|
end
|
|
end
|
|
|
|
local f, msg = loadstring("return function(player) " .. code .. " end")
|
|
|
|
if not f then
|
|
local obj = yl_speak_up.speak_to[pname].obj
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local npc = get_number_from_id(n_id)
|
|
if obj:get_luaentity() and tonumber(npc) then
|
|
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not compile the content of "..v.r_id.." :"..dump(v.r_value) .. " for player "..pname)
|
|
else
|
|
minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not compile the content of "..v.r_id.." :"..dump(v.r_value) .. " for player unknown")
|
|
end
|
|
else
|
|
|
|
|
|
local func = f()
|
|
|
|
local ok, ret = pcall(func,pname)
|
|
|
|
if not ok then
|
|
local obj = yl_speak_up.speak_to[pname].obj
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local npc = get_number_from_id(n_id)
|
|
if obj:get_luaentity() and tonumber(npc) then
|
|
minetest.log("error","[MOD] yl_speak_up: NPC with ID n_"..npc.." at position "..minetest.pos_to_string(obj:get_pos(),0).." could not execute the content of "..v.r_id.." :"..dump(v.r_value) .. " for player "..pname)
|
|
else
|
|
minetest.log("error","[MOD] yl_speak_up: NPC with ID unknown could not execute the content of "..v.r_id.." :"..dump(v.r_value) .. " for player unknown")
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
if v.r_type == "dialog" then
|
|
if dialog.n_dialogs[v.r_value] ~= nil then
|
|
target_dialog = v.r_value
|
|
else
|
|
say("This dialog does not exist")
|
|
end
|
|
end
|
|
if v.r_type == "auto" then
|
|
say("auto forward")
|
|
end
|
|
end -- end of loop over d_option.o_results
|
|
|
|
-- if there is a trade associated with this dialog and option, show that trade now
|
|
local trade_id = tostring(d_id).." "..tostring(o)
|
|
if(dialog.trades and dialog.trades[ trade_id ]) then
|
|
-- remember which option was selected
|
|
yl_speak_up.speak_to[pname].o_id = o
|
|
-- which dialog shall be shown in case of a successful trade?
|
|
yl_speak_up.speak_to[pname].target_d_id = target_dialog
|
|
-- show the trade dialog
|
|
minetest.show_formspec(pname, "yl_speak_up:do_trade_simple",
|
|
yl_speak_up.get_fs_trade_simple(player, trade_id))
|
|
return
|
|
end
|
|
|
|
-- else switch to the target dialog as stated in results/effects
|
|
if(target_dialog ~= d_id) then
|
|
-- if there are any changes done: ask first and don't switch to the new dialog yet
|
|
yl_speak_up.save_changes_and_switch_to_other_dialog(player, fields, target_dialog)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Make the NPC talk
|
|
|
|
function yl_speak_up.config(clicker, npc)
|
|
if not clicker and not clicker:is_player() then
|
|
return
|
|
end
|
|
if not npc then
|
|
return
|
|
end
|
|
|
|
local npc_id = npc:get_luaentity().yl_speak_up.id
|
|
local n_id = "n_" .. npc_id
|
|
local pname = clicker:get_player_name()
|
|
|
|
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].d_id = yl_speak_up.text_new_dialog_id -- The only d_id we can rely on existing
|
|
yl_speak_up.speak_to[pname].dialog = load_dialog(n_id) -- Load the dialog and see what we can do with it
|
|
yl_speak_up.speak_to[pname].obj = npc
|
|
|
|
-- find out who owns the npc while we still have easy access to the luaentity
|
|
yl_speak_up.npc_owner[ n_id ] = npc:get_luaentity().owner
|
|
|
|
minetest.show_formspec(
|
|
pname,
|
|
"yl_speak_up:setdialog",
|
|
get_fs_setdialog(clicker, n_id, yl_speak_up.text_new_dialog_id)
|
|
)
|
|
end
|
|
|
|
--###
|
|
-- Mob functions
|
|
--###
|
|
|
|
function yl_speak_up.on_rightclick(self, clicker)
|
|
--local item = clicker:get_wielded_item()
|
|
local name = clicker:get_player_name()
|
|
|
|
-- Take the mob only with net or lasso
|
|
if self.owner and self.owner == name then
|
|
if mobs:capture_mob(self, clicker, nil, 100, 100, true, nil) then
|
|
return
|
|
end
|
|
end
|
|
|
|
-- protect npc with mobs:protector
|
|
if mobs:protect(self, clicker) then
|
|
return
|
|
end
|
|
|
|
-- bring up the dialog options
|
|
if clicker then
|
|
yl_speak_up.talk(self, clicker)
|
|
return
|
|
end
|
|
end
|
|
|
|
function yl_speak_up.on_spawn(self)
|
|
--Let's assign an ID
|
|
|
|
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)
|
|
|
|
yl_speak_up.mob_table[m_id] = "yl_speak_up:test_npc"
|
|
|
|
self.yl_speak_up = {
|
|
talk = m_talk,
|
|
id = m_id,
|
|
textures = self.textures
|
|
}
|
|
|
|
--Let's protect it
|
|
self.protected = true
|
|
self.tamed = true
|
|
self.object:set_armor_groups({immortal = 100})
|
|
|
|
minetest.log(
|
|
"action",
|
|
"[MOD] yl_speak_up: NPC with ID n_" ..
|
|
self.yl_speak_up.id .. " spawned at " .. minetest.pos_to_string(self.object:get_pos(), 0)
|
|
)
|
|
|
|
--Let's do it only once
|
|
return true
|
|
end
|
|
|
|
function yl_speak_up.after_activate(self, staticdata, def, dtime)
|
|
minetest.log(
|
|
"action",
|
|
"[MOD] yl_speak_up: NPC with ID n_" ..
|
|
self.yl_speak_up.id .. " activated at " .. minetest.pos_to_string(self.object:get_pos(), 0)
|
|
)
|
|
|
|
if yl_speak_up.status == 2 then
|
|
self.object:remove()
|
|
return true
|
|
end
|
|
|
|
if self.yl_speak_up and self.yl_speak_up.skin then
|
|
local tex = self.yl_speak_up.skin
|
|
self.object:set_properties({textures = {tex[1], tex[2], tex[3], tex[4]}})
|
|
end
|
|
|
|
if yl_speak_up.infotext then
|
|
local i_text = ""
|
|
if self.yl_speak_up.npc_name then
|
|
i_text = i_text .. self.yl_speak_up.npc_name .. "\n"
|
|
end
|
|
if self.yl_speak_up.npc_description then
|
|
i_text = i_text .. self.yl_speak_up.npc_description .. "\n"
|
|
end
|
|
i_text = i_text .. yl_speak_up.infotext
|
|
self.object:set_properties({infotext = i_text})
|
|
end
|
|
|
|
yl_speak_up.update_nametag(self)
|
|
|
|
-- create a detached inventory for the npc and load its inventory
|
|
yl_speak_up.load_npc_inventory("n_"..tostring(self.yl_speak_up.id))
|
|
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
|
|
if not self.yl_speak_up or not self.yl_speak_up.id then
|
|
return
|
|
end
|
|
|
|
local npc_id = self.yl_speak_up.id
|
|
local n_id = "n_" .. 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
|
|
|
|
-- 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
|
|
minetest.show_formspec(pname, "yl_spaek_up:ignore",
|
|
"size[6,2]"..
|
|
"label[1.2,0.0;"..minetest.formspec_escape((self.yl_speak_up.npc_name or "This NPC")..
|
|
" [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, "This NPC 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].dialog = load_dialog(n_id) -- Load the dialog and see what we can do with it
|
|
yl_speak_up.speak_to[pname].textures = self.yl_speak_up.textures
|
|
yl_speak_up.speak_to[pname].option_index = 1
|
|
yl_speak_up.speak_to[pname].obj = self.object
|
|
|
|
|
|
minetest.show_formspec(pname, "yl_speak_up:talk",
|
|
yl_speak_up.get_fs_talkdialog(clicker, n_id))
|
|
end
|
|
|
|
-- ###
|
|
-- Fashion
|
|
-- ###
|
|
|
|
local function cape2texture(t)
|
|
return "yl_speak_up_mask_cape.png^[combine:32x64:56,20=" .. t
|
|
end
|
|
|
|
local function shield2texture(t)
|
|
return "yl_speak_up_mask_shield.png^[combine:32x64:0,0=(" .. t .. ")"
|
|
end
|
|
|
|
local function textures2skin(textures)
|
|
local temp = {}
|
|
-- Cape
|
|
|
|
local cape = cape2texture(textures[1])
|
|
|
|
-- Main
|
|
|
|
local main = textures[2]
|
|
|
|
-- left (Shield)
|
|
|
|
local left = shield2texture(textures[3])
|
|
|
|
-- right (Sword)
|
|
|
|
local right = textures[4]
|
|
|
|
temp = {cape, main, left, right}
|
|
|
|
return temp
|
|
end
|
|
|
|
local function set_textures(obj, textures) -- this function takes the base name of the textures, converts them to usable textures and stores those on the NPC
|
|
local skins = textures2skin(textures)
|
|
|
|
local ent = obj:get_luaentity()
|
|
ent.yl_speak_up.skin = skins
|
|
|
|
obj:set_properties({textures = skins})
|
|
end
|
|
|
|
local function get_npc_skins(skin)
|
|
local retstring = "yl_speak_up_main_default.png"
|
|
local rettable = {"yl_speak_up_main_default.png"}
|
|
local temp = {}
|
|
|
|
-- get the files out of modpath
|
|
local mp_list = minetest.get_dir_list(yl_speak_up.modpath .. DIR_DELIM .. "textures", false)
|
|
|
|
-- get the files out of worlddir
|
|
local wp_list =
|
|
minetest.get_dir_list(
|
|
yl_speak_up.worldpath .. DIR_DELIM .. "worldmods" .. DIR_DELIM .. "yl_npc" .. DIR_DELIM .. "textures",
|
|
false
|
|
)
|
|
|
|
-- Let's join both lists.
|
|
table.insert_all(temp, mp_list)
|
|
table.insert_all(temp, wp_list)
|
|
|
|
--[[ Let's see if the files are the ones we want. Format is
|
|
yl_npc_main_name.png <-- Those are the ones we want
|
|
yl_npc_cape_name.png
|
|
yl_npc_item_name.png
|
|
]]--
|
|
local index = 1
|
|
local retindex = 1
|
|
|
|
for _, v in pairs(temp) do
|
|
local s = string.split(v, "_")
|
|
if s[1] == "yl" and s[2] == "npc" and s[3] == "main" then
|
|
index = index + 1
|
|
retstring = retstring .. "," .. v
|
|
table.insert(rettable, v)
|
|
if v == skin then
|
|
retindex = index
|
|
end
|
|
end
|
|
end
|
|
|
|
return rettable, retstring, retindex
|
|
end
|
|
|
|
local function get_npc_capes(cape)
|
|
local retstring = "yl_npc_cape_default.png"
|
|
local rettable = {"yl_npc_cape_default.png"}
|
|
local temp = {}
|
|
|
|
-- get the files out of modpath
|
|
local mp_list = minetest.get_dir_list(yl_speak_up.modpath .. DIR_DELIM .. "textures", false)
|
|
|
|
-- get the files out of worlddir
|
|
local wp_list =
|
|
minetest.get_dir_list(
|
|
yl_speak_up.worldpath .. DIR_DELIM .. "worldmods" .. DIR_DELIM .. "yl_npc" .. DIR_DELIM .. "textures",
|
|
false
|
|
)
|
|
|
|
-- Let's join both lists.
|
|
table.insert_all(temp, mp_list)
|
|
table.insert_all(temp, wp_list)
|
|
|
|
--[[ Let's see if the files are the ones we want. Format is
|
|
yl_npc_main_name.png
|
|
yl_npc_cape_name.png <-- Those are the ones we want
|
|
yl_npc_item_name.png
|
|
]]--
|
|
local index = 1
|
|
local retindex = 1
|
|
|
|
for _, v in pairs(temp) do
|
|
local s = string.split(v, "_")
|
|
|
|
if s[1] == "yl" and s[2] == "npc" and s[3] == "cape" then
|
|
index = index + 1
|
|
retstring = retstring .. "," .. v
|
|
table.insert(rettable, v)
|
|
if cape ~= "" and v == cape then
|
|
retindex = index
|
|
end
|
|
end
|
|
end
|
|
|
|
return rettable, retstring, retindex
|
|
end
|
|
|
|
local function create_preview(main_skin)
|
|
if main_skin == nil or main_skin == "" then
|
|
main_skin = "default_greyscale.png"
|
|
end
|
|
|
|
local player_skin = "(" .. main_skin .. ")"
|
|
local skin = ""
|
|
|
|
-- Consistent on both sizes:
|
|
--Chest
|
|
skin = skin .. "([combine:16x32:-16,-12=" .. player_skin .. "^[mask:yl_speak_up_mask_chest.png)^"
|
|
|
|
--Head
|
|
skin = skin .. "([combine:16x32:-4,-8=" .. player_skin .. "^[mask:yl_speak_up_mask_head.png)^"
|
|
|
|
--Hat
|
|
skin = skin .. "([combine:16x32:-36,-8=" .. player_skin .. "^[mask:yl_speak_up_mask_head.png)^"
|
|
--Right Arm
|
|
skin = skin .. "([combine:16x32:-44,-12=" .. player_skin .. "^[mask:yl_speak_up_mask_rarm.png)^"
|
|
--Right Leg
|
|
skin = skin .. "([combine:16x32:0,0=" .. player_skin .. "^[mask:yl_speak_up_mask_rleg.png)^"
|
|
|
|
-- Left Arm
|
|
skin = skin .. "([combine:16x32:-24,-44=" .. player_skin .. "^[mask:(yl_speak_up_mask_rarm.png^[transformFX))^"
|
|
--Left Leg
|
|
skin = skin .. "([combine:16x32:-12,-32=" .. player_skin .. "^[mask:(yl_speak_up_mask_rleg.png^[transformFX))^"
|
|
|
|
-- Add overlays for 64x skins. these wont appear if skin is 32x because it will be cropped out
|
|
--Chest Overlay
|
|
skin = skin .. "([combine:16x32:-16,-28=" .. player_skin .. "^[mask:yl_speak_up_mask_chest.png)^"
|
|
--Right Arm Overlay
|
|
skin = skin .. "([combine:16x32:-44,-28=" .. player_skin .. "^[mask:yl_speak_up_mask_rarm.png)^"
|
|
--Right Leg Overlay
|
|
skin = skin .. "([combine:16x32:0,-16=" .. player_skin .. "^[mask:yl_speak_up_mask_rleg.png)^"
|
|
--Left Arm Overlay
|
|
skin = skin .. "([combine:16x32:-40,-44=" .. player_skin .. "^[mask:(yl_speak_up_mask_rarm.png^[transformFX))^"
|
|
--Left Leg Overlay
|
|
skin = skin .. "([combine:16x32:4,-32=" .. player_skin .. "^[mask:(yl_speak_up_mask_rleg.png^[transformFX))"
|
|
|
|
-- Full Preview
|
|
skin = "(((" .. skin .. ")^[resize:64x128)^[mask:yl_speak_up_transform.png)"
|
|
|
|
return skin
|
|
end
|
|
|
|
yl_speak_up.get_fs_fashion = function(pname)
|
|
local textures = yl_speak_up.speak_to[pname].textures
|
|
|
|
local maintable, mainlist, mainindex = get_npc_skins(textures[2])
|
|
local capetable, capelist, capeindex = get_npc_capes(textures[1])
|
|
local preview = create_preview(textures[2])
|
|
|
|
local button_cancel = "Cancel"
|
|
-- is this player editing this particular NPC? then rename the button
|
|
if( yl_speak_up.edit_mode[pname]
|
|
and yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id) then
|
|
button_cancel = "Back"
|
|
end
|
|
local formspec = {
|
|
"formspec_version[3]",
|
|
"size[13.4,9.5]",
|
|
"dropdown[0.3,0.2;4,0.75;set_skin;",
|
|
mainlist,
|
|
";",
|
|
mainindex,
|
|
"]",
|
|
"label[4.6,0.45;",
|
|
yl_speak_up.speak_to[pname].n_id,
|
|
"]",
|
|
"label[6,0.45;",
|
|
yl_speak_up.speak_to[pname].n_npc,
|
|
"]",
|
|
"dropdown[9.1,0.2;4,0.75;set_cape;",
|
|
capelist,
|
|
";",
|
|
capeindex,
|
|
"]",
|
|
"field[0.3,3.2;4,0.75;set_sword;;",
|
|
textures[4],
|
|
"]",
|
|
"field[9.1,3.2;4,0.75;set_shield;;",
|
|
textures[3],
|
|
"]",
|
|
"field_close_on_enter[set_sword;false]",
|
|
"field_close_on_enter[set_shield;false]",
|
|
"image[0.3,1;4,2;",
|
|
textures[2],
|
|
"]", -- Main
|
|
"image[9.1,1;4,2;",
|
|
textures[1],
|
|
"]", -- Cape
|
|
"image[0.3,4.2;4,4;",
|
|
textures[4],
|
|
"]", -- Sword
|
|
"image[9.1,4.2;4,4;",
|
|
textures[3],
|
|
"]", --textures[3],"]", -- Shield
|
|
"image[4.7,1;4,8;",
|
|
preview,
|
|
"]",
|
|
"button_exit[0.3,8.4;3,0.75;button_cancel;"..button_cancel.."]",
|
|
"button[10.1,8.4;3,0.75;button_save;Save]"
|
|
}
|
|
return table.concat(formspec, "")
|
|
end
|
|
|
|
yl_speak_up.input_fashion = function(player, formname, fields)
|
|
if formname ~= "yl_speak_up:fashion" then
|
|
return
|
|
end
|
|
|
|
local pname = player:get_player_name()
|
|
local textures = yl_speak_up.speak_to[pname].textures
|
|
|
|
if fields then
|
|
-- catch ESC as well
|
|
if fields.quit or fields.button_cancel or fields.button_exit then
|
|
-- is the player editing this npc? then we need to go back to the edit menu
|
|
if( yl_speak_up.edit_mode[pname]
|
|
and yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id) then
|
|
minetest.show_formspec(pname, "yl_speak_up:talk",
|
|
yl_speak_up.get_fs_talkdialog(player, yl_speak_up.speak_to[pname].n_id, yl_speak_up.speak_to[pname].d_id))
|
|
return
|
|
end
|
|
yl_speak_up.speak_to[pname] = nil
|
|
return
|
|
end
|
|
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
|
|
-- only change cape if there really is a diffrent one selected
|
|
if fields.set_cape and fields.set_cape ~= textures[1] then
|
|
-- only set the cape if it is part of the list of allowed capes
|
|
local capetable, capelist, capeindex = get_npc_capes(textures[1])
|
|
for _, v in pairs(capetable) do
|
|
if(v == fields.set_cape) then
|
|
textures[1] = fields.set_cape
|
|
yl_speak_up.log_change(pname, n_id,
|
|
"(fashion) cape changed to "..tostring(fields.set_cape)..".")
|
|
-- we have found it
|
|
break
|
|
end
|
|
end
|
|
end
|
|
-- only change the skin if there really is a diffrent new one selected
|
|
if fields.set_skin and fields.set_skin ~= textures[2] then
|
|
-- only set the skin if it is part of the list of allowed skins
|
|
local maintable, mainlist, mainindex = get_npc_skins(textures[2])
|
|
for _, v in pairs(maintable) do
|
|
if(v == fields.set_skin) then
|
|
textures[2] = fields.set_skin
|
|
yl_speak_up.log_change(pname, n_id,
|
|
"(fashion) skin changed to "..tostring(fields.set_skin)..".")
|
|
-- we have found it
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if fields.set_shield then
|
|
textures[3] = fields.set_shield
|
|
yl_speak_up.log_change(pname, n_id,
|
|
"(fashion) shield changed to "..tostring(fields.set_shield)..".")
|
|
end
|
|
if fields.set_sword then
|
|
textures[4] = fields.set_sword
|
|
yl_speak_up.log_change(pname, n_id,
|
|
"(fashion) sword changed to "..tostring(fields.set_sword)..".")
|
|
end
|
|
|
|
if fields.button_save then
|
|
local obj = yl_speak_up.speak_to[pname].obj
|
|
if obj ~= nil and obj:get_luaentity() ~= nil then
|
|
-- save textures
|
|
yl_speak_up.speak_to[pname].skins = textures2skin(textures)
|
|
set_textures(obj, textures)
|
|
yl_speak_up.log_change(pname, n_id,
|
|
"(fashion) saved changes.")
|
|
end
|
|
end
|
|
|
|
--yl_speak_up.speak_to[pname].textures = textures
|
|
end
|
|
|
|
minetest.show_formspec(pname, "yl_speak_up:fashion", yl_speak_up.get_fs_fashion(pname))
|
|
end
|
|
|
|
function yl_speak_up.fashion(player, obj)
|
|
local luaentity = obj:get_luaentity()
|
|
local pname = player:get_player_name()
|
|
local npc_id = luaentity.yl_speak_up.id
|
|
local skins = luaentity.yl_speak_up.skins
|
|
local n_id = "n_" .. npc_id
|
|
local t = luaentity.textures
|
|
|
|
yl_speak_up.speak_to[pname] = {}
|
|
yl_speak_up.speak_to[pname].n_id = n_id
|
|
yl_speak_up.speak_to[pname].obj = obj
|
|
yl_speak_up.speak_to[pname].textures = t
|
|
yl_speak_up.speak_to[pname].skins = textures2skin(t)
|
|
|
|
local dialog = load_dialog(n_id)
|
|
if next(dialog) then
|
|
yl_speak_up.speak_to[pname].n_npc = dialog.n_npc
|
|
else
|
|
yl_speak_up.speak_to[pname].n_npc = "Unknown"
|
|
end
|
|
|
|
minetest.show_formspec(pname, "yl_speak_up:fashion", yl_speak_up.get_fs_fashion(pname))
|
|
end
|
|
|
|
|
|
|
|
yl_speak_up.update_nametag = function(self)
|
|
if self.yl_speak_up.npc_name then
|
|
-- the nametag is normal (green)
|
|
if(self.yl_speak_up.talk) then
|
|
self.object:set_nametag_attributes({color="#00ff00", text=self.yl_speak_up.npc_name})
|
|
-- the nametag has the addition "[muted]" and is magenta when muted
|
|
else
|
|
self.object:set_nametag_attributes({color="#ff00ff", text=self.yl_speak_up.npc_name.." [muted]"})
|
|
end
|
|
end
|
|
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 "..npc.." will shut up at pos "..
|
|
minetest.pos_to_string(obj:get_pos(),0).." on command of "..p_name)
|
|
minetest.log("action","[MOD] yl_speak_up: NPC with ID n_"..npc..
|
|
" will shut up at pos "..minetest.pos_to_string(obj:get_pos(),0)..
|
|
" on command of "..p_name)
|
|
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 with ID "..npc.." will resume speech at pos "..
|
|
minetest.pos_to_string(obj:get_pos(),0).." on command of "..p_name)
|
|
minetest.log("action","[MOD] yl_speak_up: NPC with ID n_"..npc..
|
|
" will resume speech at pos "..minetest.pos_to_string(obj:get_pos(),0)..
|
|
" on command of "..p_name)
|
|
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}))
|
|
end
|
|
|
|
|
|
-- replace some variables in the text the NPC speaks and which the player can use to reply
|
|
-- pname: the name of the player that is talking to the NPC
|
|
yl_speak_up.replace_vars_in_text = function(text, dialog, pname)
|
|
-- Note: the $ char is a special one. It needs to be escaped with %$ in lua.
|
|
text = string.gsub(text, "%$MY_NAME%$", dialog.n_npc)
|
|
text = string.gsub(text, "%$NPC_NAME%$", dialog.n_npc)
|
|
text = string.gsub(text, "%$OWNER_NAME%$", dialog.npc_owner)
|
|
text = string.gsub(text, "%$PLAYER_NAME%$", pname)
|
|
|
|
local day_time_name = "day"
|
|
local day_time = minetest.get_timeofday()
|
|
if(day_time < 0.5) then
|
|
day_time_name = "morning"
|
|
elseif(day_time < 0.75) then
|
|
day_time_name = "afternoon"
|
|
else
|
|
day_time_name = "evening"
|
|
end
|
|
text = string.gsub(text, "%$GOOD_DAY%$", "Good "..day_time_name)
|
|
text = string.gsub(text, "%$good_DAY%$", "good "..day_time_name)
|
|
|
|
return text
|
|
end
|
|
|
|
|
|
-- log changes done by players or admins to NPCs
|
|
yl_speak_up.log_change = function(pname, n_id, text)
|
|
-- make sure all variables are defined
|
|
if(not(pname)) then
|
|
pname = "- unkown player -"
|
|
end
|
|
if(not(n_id)) then
|
|
n_id = "- unknown NPC -"
|
|
end
|
|
if(not(text)) then
|
|
text = "- no text given -"
|
|
end
|
|
-- we don't want newlines in the texts
|
|
text = string.gsub(text, "\n", "\\n")
|
|
|
|
local log_text = "<"..tostring(n_id).."> ["..tostring(pname).."]: "..text
|
|
-- log in general logfile
|
|
minetest.log("yl_speak_up "..log_text)
|
|
-- log with timestamp
|
|
local log_text = tostring(os.time())..log_text
|
|
-- TODO: log in a file for each npc and show it on demand?
|
|
end
|