yl_speak_up/functions.lua
2021-05-01 14:00:53 +02:00

2546 lines
101 KiB
Lua

-- TODO: just for the apple quest...needs to be deleted later on
yl_sokomine = {}
yl_sokomine["q_apple_explorer"] = {}
-- 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
--
-- TODO: add the actual edit menu for options
-- can be entered from the edit, conditions and effects button;
-- style: Answer/Option: <text>
-- Lets the NPC answer with: <dialog_id> [Button GOTO target dialog]
--
-- Only offer this answer if all the following conditions are true:
-- <list of preconditions>
-- If at least one condition is not fulfilled, ..
-- <display the following text|grey out this answer|do not offer this answer>
-- <alternate answer>
-- When this answer has been selected, apply the following effects:
-- <list of effects>
--
-- [Store] [Delete this option] [Back to dialog xyz] [GOTO target diaglog]
--
-- TODO: check if security is ok (minetest.formspec_escape etc)
-- TODO: actually store changed entries
-- TODO: 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)
--###
-- 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 = {}
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","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
--###
local function save_dialog(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 = "r_" .. find_next_id(dialog.n_dialogs[d_id].d_options[future_o_id].o_results)
if future_r_id == "r_1" then
dialog.n_dialogs[d_id].d_options[future_o_id].o_results = {}
end
dialog.n_dialogs[d_id].d_options[future_o_id].o_results[future_r_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
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
save_dialog(n_id, dialog)
end
local function delete_prerequisite(n_id, d_id, o_id, p_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" Also, something might have gone wrong here.
if p_id == yl_speak_up.text_new_prerequisite_id then
return false
end
local dialog = load_dialog(n_id)
dialog.n_dialogs[d_id].d_options[o_id].o_prerequisites[p_id] = nil
save_dialog(n_id, dialog)
end
local function delete_result(n_id, d_id, o_id, r_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" Also, something might have gone wrong here.
if r_id == yl_speak_up.text_new_result_id then
return false
end
local dialog = load_dialog(n_id)
dialog.n_dialogs[d_id].d_options[o_id].o_resultss[r_id] = nil
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
-- talk
local function get_fs_talkdialog(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
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 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
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
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),
"]",
"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 = {}
-- display the window with the text the NPC is saying
-- TODO: make name and description likewise editable?
-- TODO: make name of dialog and sort option editable?
-- TODO: show who owns the npc/is responsible for its talks?
if(edit_mode) then
-- TODO: sort by name?
-- build the list of available dialogs for the dropdown list(s)
-- 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 .. "," .. v.d_id
-- which one is the current dialog?
n = n + 1
d_id_to_dropdown_index[v.d_id] = n
end
end
local i = (d_id_to_dropdown_index[c_d_id] or 0)
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..";"..i..",]")
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 previous/next buttons for previous/next dialog (sorted as in the dropdown menu);
-- this is just an alternate way of walking through the dialogs
for k,v in pairs(d_id_to_dropdown_index) do
if( v == i-1) then
table.insert(formspec, "button[8.5,4.0;2,0.9;prev_dialog_"..k..";<]")
-- add a tooltip
table.insert(formspec, "tooltip[prev_dialog_" .. k .. ";Go to previous dialog "..k..".]")
elseif(v == i+1) then
table.insert(formspec, "button[11,4.0;2,0.9;next_dialog_"..k..";>]")
-- add a tooltip
table.insert(formspec, "tooltip[next_dialog_" .. k .. ";Go to next dialog "..k..".]")
end
end
-- 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.]")
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
table.insert(formspec, "hypertext[0.2,5;19.6,17.8;d_text;<normal>")
table.insert(formspec, minetest.formspec_escape(active_dialog.d_text) .. "\n</normal>")
table.insert(formspec, "]")
table.insert(formspec, "tooltip[d_text;")
table.insert(formspec, minetest.formspec_escape(active_dialog.d_text):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
-- 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
end
for _, sb_v in pairs(sorted_buttons) do
-- 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_" .. sb_v.o_id .. ";"..sb_v.o_id..":]")
-- add a tooltip "Edit target dialog, pre(C)onditions and (E)ffects for option o_<nr>"
table.insert(formspec, "tooltip[edit_option_" .. sb_v.o_id .. ";Edit target dialog, pre(C)onditions and (E)ffects for option "..sb_v.o_id..".]")
-- 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_" .. sb_v.o_id .. ";->]")
-- add a tooltip "Go to target dialog d_<nr>"
table.insert(formspec, "tooltip[button_" .. sb_v.o_id .. ";Go to target dialog "..target_dialog.." that will be shown when this option ("..sb_v.o_id..") is selected.]")
-- selecting an option this way MUST NOT execute the pre(C)onditions or (E)ffects!
end
-- allow to set a new target dialog
table.insert(formspec, "dropdown[3.9,"..h..";4.7,1;d_id_"..sb_v.o_id..";"..dialog_list..";"..(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 "..sb_v.o_id..".;#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_"..sb_v.o_id..";C]")
-- label: "There are pre(C)onditions required for showing this option. Display them."
table.insert(formspec, "tooltip[conditions_" .. sb_v.o_id .. ";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_"..sb_v.o_id..";Ef]")
-- label: "There are further (E)ffects (apart from switching to a new dialog) set for this option. Display them."
table.insert(formspec, "tooltip[effects_" .. sb_v.o_id .. ";There are further (E)ffects (apart from switching to a new dialog) set for this option. Display them.]")
end
-- show the actual text for the option
table.insert(formspec, "field[9.4," .. h .. ";44.9,0.9;text_option_" .. sb_v.o_id .. ";;"..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_" .. sb_v.o_id .. ";Edit the text that is displayed on button "..sb_v.o_id..".]")
-- 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
table.insert(formspec, "button[0.5," .. h .. ";53.8,0.9;button_" .. sb_v.o_id .. ";]")
table.insert(
formspec,
"tooltip[button_" .. sb_v.o_id .. ";" .. sb_v.o_text_when_prerequisites_met .. "]"
)
local l = h + 0.45
table.insert(formspec, "label[0.7," .. l .. ";" .. sb_v.o_text_when_prerequisites_met .. "]")
else
if sb_v.o_hide_when_prerequisites_not_met == "true" then
else
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_" .. sb_v.o_id .. ";]")
table.insert(
formspec,
"tooltip[button_" ..
sb_v.o_id ..
";" .. 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_" .. sb_v.o_id .. ";]")
table.insert(
formspec,
"tooltip[button_" .. sb_v.o_id .. ";" .. sb_v.o_text_when_prerequisites_not_met .. "]"
)
local l = h + 0.45
table.insert(
formspec,
"label[0.7," .. l .. ";" .. sb_v.o_text_when_prerequisites_not_met .. "]"
)
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_" .. sb_v.o_id .. ";]")
table.insert(
formspec,
"tooltip[button_" .. sb_v.o_id .. ";" .. sb_v.o_text_when_prerequisites_not_met .. "]"
)
local l = h + 0.45
table.insert(
formspec,
"label[0.7," .. l .. ";" .. sb_v.o_text_when_prerequisites_not_met .. "]"
)
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
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.]")
-- 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.npc_owner[ n_id ] == pname
and minetest.check_player_privs(player, {npc_talk_owner=true}))
or (minetest.check_player_privs(player, {npc_master=true}))) 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
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
if allowed[sb_v.o_id] == true then
h = h + 1
table.insert(
formspec,
"button[1," ..
h .. ";44,0.9;button_" .. sb_v.o_id .. ";" .. sb_v.o_text_when_prerequisites_met .. "]"
)
table.insert(
formspec,
"tooltip[button_" .. sb_v.o_id .. ";" .. 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 ..
" : " .. 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_" ..
sb_v.o_id .. ";" .. sb_v.o_text_when_prerequisites_not_met .. "]"
)
table.insert(
formspec,
"tooltip[button_" ..
sb_v.o_id .. ";" .. 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
minetest.register_on_player_receive_fields(
function(player, formname, fields)
if formname ~= "yl_speak_up:optiondialog" 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)
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)
save_dialog(n_id, temp_dialog)
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
minetest.register_on_player_receive_fields(
function(player, formname, fields)
if formname ~= "yl_speak_up:setdialog" 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)
save_dialog(n_id, dialog)
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})
obj:set_nametag_attributes({color="#00ff00", text=dialog.n_npc})
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)
)
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
yl_speak_up.add_new_dialog = function(dialog, pname)
next_id = find_next_id(dialog.n_dialogs)
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
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
-- helper function for formspec "yl_speak_up:talk" 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
-- TODO: implement the change detection and ask the user what to do (save? discard?)
-- TODO: what if it is a new dialog?
-- Parameters:
-- pname player name
-- fields the fields returned from the formspec
yl_speak_up.edit_mode_apply_changes = function(pname, fields)
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[ yl_speak_up.edit_mode[pname]])) then
yl_speak_up.npc_was_changed[ yl_speak_up.edit_mode[pname]] = {}
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
-- detect changes to d_text: text of the dialog (what the npc is saying)
if(fields.d_text and dialog.n_dialogs[ d_id ].d_text ~= fields.d_text) then
--minetest.chat_send_player("singleplayer","CHANGED: d_text (what the NPC says) was changed\n from: "..tostring( dialog.n_dialogs[ d_id ].d_text).."\n to: "..tostring(fields.d_text))
-- store that there have been changes to this npc
table.insert(yl_speak_up.npc_was_changed[ yl_speak_up.edit_mode[pname] ],
"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
-- TODO: option to delete dialogs (when text empty and no options present?)
end
-- add a new option/answer
-- TODO: limit the amount of possible answers to a sensible amount
if(fields[ "add_option"]) then
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 = {}
end
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[ yl_speak_up.edit_mode[pname] ],
"Dialog "..d_id..": Added new option/answer "..future_o_id..".")
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
-- TODO: delete option if text is empty
--minetest.chat_send_player("singleplayer", "CHANGED: Text for option "..tostring(k).." was changed\n from: "..tostring(v.o_text_when_prerequisites_met).."\n to: "..tostring(fields[ "text_option_"..k]))
-- store that there have been changes to this npc
table.insert(yl_speak_up.npc_was_changed[ yl_speak_up.edit_mode[pname] ],
"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>
-- TODO: this may also point to a new dialog
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
--minetest.chat_send_player("singleplayer", "CHANGED: Target dialog for option "..tostring(k).." was changed\n from: "..tostring(vr.r_value).."\n to: "..tostring(fields[ "d_id_"..k]))
-- store that there have been changes to this npc
table.insert(yl_speak_up.npc_was_changed[ yl_speak_up.edit_mode[pname] ],
"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 ]
end
end
-- this might be the first option
elseif( fields["d_id_"..k ]) then
--minetest.chat_send_player("singleplayer", "CHANGED: Target dialog for option "..tostring(k).." was added\n as: "..tostring(fields[ "d_id_"..k]))
-- store that a new option has been added to this dialog
table.insert(yl_speak_up.npc_was_changed[ yl_speak_up.edit_mode[pname] ],
"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 = "r_" .. find_next_id(dialog.n_dialogs[d_id].d_options[k].o_results)
if future_r_id == "r_1" then
dialog.n_dialogs[d_id].d_options[k].o_results = {}
end
-- 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 ]}
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)
-- actually show the new dialog
fields["d_id"] = d_id
fields["show_new_dialog"] = nil
end
-- TODO just for debugging
for k,v in pairs(yl_speak_up.npc_was_changed[ yl_speak_up.edit_mode[pname] ]) do
minetest.chat_send_player("singleplayer", v )
end
end
-- end of yl_speak_up.edit_mode_apply_changes
minetest.register_on_player_receive_fields(
function(player, formname, fields)
if formname ~= "yl_speak_up:talk" then
return
end
local pname = player:get_player_name()
local o = ""
-- 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) then
yl_speak_up.edit_mode_apply_changes(pname, fields)
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.npc_owner[ yl_speak_up.speak_to[pname].n_id ] == pname
and minetest.check_player_privs(player, {npc_talk_owner=true}))
and not(minetest.check_player_privs(player, {npc_master=true}))) then
minetest.chat_send_player(pname, "Sorry. You do not have the npc_talk_owner or npc_master priv.")
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", 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
yl_speak_up.edit_mode[pname] = nil
-- start a new chat - but this time in normal mode
yl_speak_up.speak_to[pname].d_id = nil
minetest.show_formspec(pname, "yl_speak_up:talk", get_fs_talkdialog(player, yl_speak_up.speak_to[pname].n_id, nil))
return
end
if fields.quit or fields.button_exit then
-- Formspec was quit
yl_speak_up.speak_to[pname] = 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",
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",
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
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
-- 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
yl_speak_up.speak_to[pname].d_id = show_dialog
minetest.show_formspec(pname, "yl_speak_up:talk", get_fs_talkdialog(player, yl_speak_up.speak_to[pname].n_id, show_dialog))
return
end
local n_dialog = dialog.n_dialogs[d_id]
local d_option = n_dialog.d_options[o]
-- 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
yl_speak_up.speak_to[pname].d_id = v.r_value
local n_id = yl_speak_up.speak_to[pname].n_id
minetest.show_formspec(pname, "yl_speak_up:talk", get_fs_talkdialog(player, n_id, v.r_value))
else
say("This dialog does not exist")
end
end
if v.r_type == "auto" then
say("auto forward")
end
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
if self.yl_speak_up.npc_name then
self.object:set_nametag_attributes({color="#00ff00", text=self.yl_speak_up.npc_name})
end
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
if not self.yl_speak_up or not self.yl_speak_up.talk or self.yl_speak_up.talk~=true then
return
end
local npc_id = self.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].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
-- 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
minetest.show_formspec(pname, "yl_speak_up:talk", 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
local function get_fs_fashion(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 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;Cancel]",
"button[10.1,8.4;3,0.75;button_save;Save]"
}
return table.concat(formspec, "")
end
minetest.register_on_player_receive_fields( --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
if fields.quit or fields.button_cancel then
yl_speak_up.speak_to[pname] = nil
return
end
if fields.set_cape then
textures[1] = fields.set_cape
end
if fields.set_skin then
textures[2] = fields.set_skin
end
if fields.set_shield then
textures[3] = fields.set_shield
end
if fields.set_sword then
textures[4] = 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)
end
end
--yl_speak_up.speak_to[pname].textures = textures
end
minetest.show_formspec(pname, "yl_speak_up:fashion", 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", get_fs_fashion(pname))
end