956 lines
38 KiB
Lua
956 lines
38 KiB
Lua
-- This is the main talkdialog the NPC shows when right-clicked.
|
|
|
|
|
|
-- helper function for yl_speak_up.fs_talkdialog
|
|
yl_speak_up.calculate_portrait = function(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
|
|
|
|
|
|
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 = ""
|
|
|
|
-- error: not talking?
|
|
if(not(yl_speak_up.speak_to[pname])) then
|
|
return
|
|
end
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
|
|
-- the NPC needs to be configured first; route input to the configuration dialog
|
|
if(not(yl_speak_up.speak_to[pname].dialog)
|
|
or not(yl_speak_up.speak_to[pname].dialog.n_npc)
|
|
or not(yl_speak_up.speak_to[pname].d_id)) then
|
|
yl_speak_up.input_fs_initial_config(player, formname, fields)
|
|
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
|
|
|
|
-- show which dialogs point to this one
|
|
if(edit_mode and fields.show_what_points_to_this_dialog) then
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
local d_id = yl_speak_up.speak_to[pname].d_id
|
|
yl_speak_up.show_fs(player, "show_what_points_to_this_dialog",
|
|
yl_speak_up.speak_to[pname].d_id)
|
|
return
|
|
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
|
|
yl_speak_up.show_fs(player, "initial_config",
|
|
{n_id = n_id, d_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
|
|
yl_speak_up.show_fs(player, "inventory")
|
|
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
|
|
yl_speak_up.show_fs(player, "fashion")
|
|
return
|
|
end
|
|
|
|
if(edit_mode and fields.button_save_dialog) then
|
|
yl_speak_up.show_fs(player, "talk",
|
|
{n_id = n_id, d_id = yl_speak_up.speak_to[pname].d_id, do_save = true})
|
|
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
|
|
-- for older formspec versions: reset scroll counter
|
|
yl_speak_up.speak_to[pname].counter = 1
|
|
yl_speak_up.speak_to[pname].option_index = 1
|
|
-- 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
|
|
yl_speak_up.show_fs(player, "talk", {n_id = yl_speak_up.speak_to[pname].n_id, d_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.show_fs(player, "quit", 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.show_fs(player, "quit", nil)
|
|
return
|
|
end
|
|
|
|
-- allow the player to take the item back
|
|
if(fields.show_player_offers_item and fields.show_player_offers_item ~= "") then
|
|
yl_speak_up.show_fs(player, "player_offers_item", 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
|
|
yl_speak_up.show_fs(player, "talk", {n_id = yl_speak_up.speak_to[pname].n_id,
|
|
d_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
|
|
yl_speak_up.show_fs(player, "talk", {n_id = yl_speak_up.speak_to[pname].n_id,
|
|
d_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
|
|
yl_speak_up.show_fs(player, "trade_list", nil)
|
|
return
|
|
end
|
|
|
|
-- the player wants to give something to the NPC
|
|
if(fields.player_offers_item) then
|
|
if(not(edit_mode)) then
|
|
-- normal mode: take the item the player wants to offer
|
|
yl_speak_up.show_fs(player, "player_offers_item", nil)
|
|
else
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
local future_d_id = "d_got_item"
|
|
-- make sure this dialog exists; create if needed
|
|
if(not(dialog.n_dialogs[ future_d_id ])) then
|
|
dialog.n_dialogs[future_d_id] = {
|
|
d_id = future_d_id,
|
|
d_type = "text",
|
|
d_text = "",
|
|
d_sort = 9999 -- make this the last option
|
|
}
|
|
end
|
|
-- in edit mode: allow to edit the options
|
|
yl_speak_up.show_fs(player, "talk", {n_id = n_id, d_id = future_d_id})
|
|
end
|
|
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
|
|
|
|
-- we may soon need actions and o_results from the selected_option
|
|
local selected_option = {}
|
|
if(d_id and o and dialog
|
|
and dialog.n_dialogs
|
|
and dialog.n_dialogs[d_id]
|
|
and dialog.n_dialogs[d_id].d_options
|
|
and dialog.n_dialogs[d_id].d_options[o]) then
|
|
selected_option = dialog.n_dialogs[d_id].d_options[o]
|
|
end
|
|
|
|
if(not(edit_mode)) then
|
|
-- abort if the option does not exist
|
|
if(not(selected_option)) then
|
|
return
|
|
end
|
|
yl_speak_up.speak_to[pname].o_id = o
|
|
-- start with executing the first action
|
|
yl_speak_up.execute_next_action(player, nil, true)
|
|
return
|
|
end
|
|
|
|
-- 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["actions_"..o_id]
|
|
or fields["effects_"..o_id]) then
|
|
-- store which option we want to edit
|
|
yl_speak_up.speak_to[pname].o_id = o_id
|
|
-- if something was changed: ask for confirmation
|
|
yl_speak_up.show_fs(player, "edit_option_dialog",
|
|
{n_id = yl_speak_up.speak_to[pname].n_id,
|
|
d_id = d_id, o_id = o_id, caller="button"})
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
-- in edit mode: has another dialog been selected?
|
|
-- if nothing better can be found: keep the old dialog
|
|
local show_dialog = d_id
|
|
-- an option button was selected;
|
|
-- since we do not execute actions and effects in edit mode, we need to find out the
|
|
-- right target dialog manually (and assume all went correct)
|
|
if( o ~= "" ) then
|
|
-- find out the normal target dialog of this option
|
|
if(selected_option and selected_option.o_results) then
|
|
for k, v in pairs(selected_option.o_results) do
|
|
if(v and v.r_type == "dialog") then
|
|
show_dialog = v.r_value
|
|
end
|
|
end
|
|
end
|
|
-- 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)
|
|
elseif(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.show_fs(player, "talk", {n_id = n_id, d_id = show_dialog})
|
|
-- no option was selected - so we need to end this here
|
|
return
|
|
end
|
|
end
|
|
|
|
|
|
-- older formspecs (before v3) do not offer a scroll container and have to scroll manually;
|
|
-- we maintain a player-name-based counter in order to see if this line ought to be shown
|
|
yl_speak_up.old_fs_version_show_line = function(pname)
|
|
-- the player is using a new enough version for scroll_container to work
|
|
if(not(pname)) then
|
|
return true
|
|
end
|
|
local max_number_of_buttons = yl_speak_up.max_number_of_buttons
|
|
local start_index = yl_speak_up.speak_to[pname].option_index
|
|
local counter = yl_speak_up.speak_to[pname].counter
|
|
yl_speak_up.speak_to[pname].counter = counter + 1
|
|
if counter < start_index or counter >= start_index + max_number_of_buttons then
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
|
|
-- show an edit option in the main menu of the NPC;
|
|
-- helper function for yl_speak_up.fs_talkdialog(..)
|
|
yl_speak_up.add_edit_button_fs_talkdialog = function(formspec, h, button_name, tooltip, label,
|
|
show_main_not_alternate, alternate_label, is_exit_button,
|
|
pname)
|
|
-- do not show this button at all if there is no alternate text and the condition is false
|
|
if(not(alternate_label) and not(show_main_not_alternate)) then
|
|
return h
|
|
end
|
|
local button_dimensions = "0.5,"..(h+1)..";53.8,0.9;"
|
|
local label_start_pos = "0.7"
|
|
-- older formspecs (before v4) do not offer a scroll container and have to scroll manually
|
|
if(pname) then
|
|
if(not(yl_speak_up.old_fs_version_show_line(pname))) then
|
|
return h
|
|
end
|
|
-- there has to be more room for the up and down arrows
|
|
button_dimensions = "1.2,"..(h+1)..";52.3,0.9;"
|
|
label_start_pos = "1.4"
|
|
end
|
|
h = h + 1
|
|
if(show_main_not_alternate) then
|
|
if(is_exit_button) then
|
|
table.insert(formspec, "button_exit["..button_dimensions..tostring(button_name)..";]")
|
|
else
|
|
table.insert(formspec, "button["..button_dimensions..tostring(button_name)..";]")
|
|
end
|
|
table.insert(formspec, "tooltip["..tostring(button_name)..";"..tostring(tooltip).."]")
|
|
table.insert(formspec, "label["..label_start_pos..","..(h+0.45)..";"..tostring(label).."]")
|
|
else
|
|
table.insert(formspec, "box["..button_dimensions.."#BBBBBB]")
|
|
table.insert(formspec, "label["..label_start_pos..","..(h+0.45)..";"..
|
|
tostring(alternate_label).."]")
|
|
end
|
|
return h
|
|
end
|
|
|
|
|
|
-- show a formspec element in the main menu of the NPC (with tooltip);
|
|
-- helper function for yl_speak_up.fs_talkdialog(..)
|
|
yl_speak_up.add_formspec_element_with_tooltip_if = function(formspec, element_type, position, element_name,
|
|
element_text, tooltip, condition)
|
|
if(not(condition)) then
|
|
return
|
|
end
|
|
table.insert(formspec, element_type.."["..position..";"..element_name..";"..element_text.."]")
|
|
table.insert(formspec, "tooltip["..tostring(element_name)..";"..tostring(tooltip).."]")
|
|
end
|
|
|
|
|
|
-- recursion_depth is increased each time autoanswer is automaticly selected
|
|
yl_speak_up.get_fs_talkdialog = function(player, n_id, d_id, alternate_text, recursion_depth)
|
|
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 yl_speak_up.get_error_message()
|
|
end
|
|
|
|
-- currently no trade running (we're editing options)
|
|
yl_speak_up.trade[pname] = nil
|
|
yl_speak_up.speak_to[pname].trade_id = nil
|
|
|
|
--[[ 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
|
|
-- (input ends up at yl_speak_up.input_talk and needs to be rerouted)
|
|
return yl_speak_up.get_fs_initial_config(player, n_id, d_id, true)
|
|
end
|
|
|
|
if c_d_id == nil then return yl_speak_up.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
|
|
|
|
-- 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)
|
|
|
|
-- evaluate the preconditions of each option and check if the option can be offered
|
|
local allowed = yl_speak_up.calculate_displayable_options(pname, active_dialog.d_options, edit_mode,
|
|
-- avoid loops by limiting max recoursion depths for autoanswers
|
|
(recursion_depth < yl_speak_up.max_allowed_recursion_depth))
|
|
-- abort here if needed - the autoanswer/autoselection did choose an option for us alread
|
|
if(not(edit_mode) and allowed and allowed["autoanswer"] and allowed["autoanswer"] ~= "") then
|
|
-- no actions shall be executed
|
|
local o_id = allowed["autoanswer"]
|
|
local effects = active_dialog.d_options[o_id].o_results
|
|
-- execute all effects/results
|
|
local res = yl_speak_up.execute_all_relevant_effects(player, effects, o_id, true)
|
|
local target_dialog = res.next_dialog
|
|
yl_speak_up.speak_to[pname].o_id = nil
|
|
yl_speak_up.speak_to[pname].a_id = nil
|
|
if(not(target_dialog)
|
|
or target_dialog == ""
|
|
or not(dialog.n_dialogs[target_dialog])) then
|
|
target_dialog = yl_speak_up.speak_to[pname].d_id
|
|
end
|
|
-- show the new target dialog and exit
|
|
-- the recursion_depth will be increased by one (we did autoselect here and need to
|
|
-- avoid infinite loops)
|
|
return yl_speak_up.get_fs_talkdialog(player, n_id, target_dialog, res.alternate_text,
|
|
recursion_depth + 1)
|
|
end
|
|
|
|
-- is the player comming back from trying to offer something to the NPC?
|
|
-- And is the NPC trying to return the item?
|
|
if(not(edit_mode) and d_id == "d_got_item") then
|
|
local pname = player:get_player_name()
|
|
local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname})
|
|
if(not(trade_inv:is_empty("npc_wants"))) then
|
|
return "size[8,1.5]"..
|
|
"label[0.2,-0.2;"..
|
|
minetest.formspec_escape(dialog.n_npc or "- ? -")..
|
|
" does not seem to be intrested.\n"..
|
|
"Please take your item back and try something else.]"..
|
|
"button[2,1.0;1.5,0.9;show_player_offers_item;Ok]"
|
|
end
|
|
end
|
|
|
|
yl_speak_up.speak_to[pname].allowed = allowed
|
|
|
|
local portrait = yl_speak_up.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
|
|
|
|
local fs_version = 1
|
|
if formspec_v >= 4 then
|
|
fs_version = 3
|
|
elseif formspec_v >= 2 then
|
|
fs_version = 2
|
|
else
|
|
fs_version = 1
|
|
end
|
|
-- store which formspec version the player has
|
|
if(not(yl_speak_up.fs_version[pname])) then
|
|
yl_speak_up.fs_version[pname] = fs_version
|
|
end
|
|
formspec = {
|
|
"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]",
|
|
|
|
"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,
|
|
"]",
|
|
}
|
|
|
|
-- add those things that only exist in formspec_v >= 4
|
|
if(fs_version > 2) then
|
|
table.insert(formspec, "style_type[button;bgcolor=#a37e45]")
|
|
table.insert(formspec, "style_type[button_exit;bgcolor=#a37e45]") -- Dialog
|
|
-- table.insert(formspec, "background[-1,-1;22,25;yl_speak_up_bg_dialog2.png;false]")
|
|
-- table.insert(formspec, "background[-1,23;58,10;yl_speak_up_bg_dialog2.png;false]")
|
|
-- table.insert(formspec, "style_type[button;bgcolor=#a37e45]")
|
|
end
|
|
|
|
-- 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) then
|
|
local prev_dialog = tostring(minetest.formspec_escape(sorted_list[i-1]))
|
|
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
|
|
"button", "8.5,4.0;2,0.9", "prev_dialog_"..prev_dialog,
|
|
"<",
|
|
"Go to previous dialog "..prev_dialog..".",
|
|
(sorted_list[ i-1 ]))
|
|
local next_dialog = tostring(minetest.formspec_escape(sorted_list[i+1]))
|
|
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
|
|
"button", "11,4.0;2,0.9", "next_dialog_"..next_dialog,
|
|
">",
|
|
"Go to next dialog "..next_dialog..".",
|
|
(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]")
|
|
|
|
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
|
|
"button", "13.9,4.0;1,0.9", "show_new_dialog",
|
|
"+",
|
|
"Create a new dialog.",
|
|
true)
|
|
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
|
|
"button", "13.4,0.3;2,0.9", "button_edit_name_and_description",
|
|
"Edit",
|
|
"Edit name and description of your NPC.",
|
|
true)
|
|
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
|
|
"button", "15.7,0.3;2,0.9", "button_save_dialog",
|
|
"Save",
|
|
"Save this dialog.",
|
|
true)
|
|
|
|
table.insert(formspec, "textarea[0.2,5;19.6,17.8;d_text;;"..
|
|
minetest.formspec_escape(active_dialog.d_text)..
|
|
"]")
|
|
else
|
|
if(alternate_text) then
|
|
alternate_text = string.gsub(alternate_text, "%$TEXT%$", active_dialog.d_text)
|
|
end
|
|
-- replace $NPC_NAME$ etc.
|
|
local t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text(
|
|
(alternate_text or active_dialog.d_text), dialog, pname))
|
|
if(fs_version > 2) then
|
|
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
|
|
"hypertext", "0.2,5;19.6,17.8", "d_text",
|
|
"<normal>"..t.."\n</normal>",
|
|
t:trim()..";#000000;#FFFFFF",
|
|
true)
|
|
else
|
|
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
|
|
"textarea", "0.2,5;19.6,17.8", "",
|
|
";"..t.."\n",
|
|
t:trim(),
|
|
true)
|
|
end
|
|
end
|
|
|
|
local pname_for_old_fs = nil
|
|
if(fs_version > 2) then
|
|
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]")
|
|
else
|
|
if(fs_version < 2) then
|
|
-- if the player has an older formspec version
|
|
minetest.log( "info",
|
|
"[MOD] yl_speak_up: User " .. pname .. " talked to NPC ID n_" .. n_id ..
|
|
" with an old formspec version!")
|
|
table.insert(formspec,
|
|
"box[0.3,20;19,2.6;red]"..
|
|
"label[0.7,20.3;"..yl_speak_up.text_version_warning.."]")
|
|
-- The up and down buttons are microscopic. Offer some (still small)
|
|
-- additional text buttons so that players have a chance to hit them.
|
|
table.insert(formspec, "button[49,22.8;6,0.9;button_down;^ Scroll Up ^]")
|
|
table.insert(formspec, "button[49,31.8;6,0.9;button_up;v Scroll Down v]")
|
|
end
|
|
-- old formspec versions need to remember somewhere extern how far the player scrolled
|
|
pname_for_old_fs = pname
|
|
yl_speak_up.speak_to[pname_for_old_fs].counter = 1
|
|
table.insert(formspec, "container[0,24]")
|
|
if(fs_version < 2) then
|
|
-- very small, ugly, and difficult to hit
|
|
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]")
|
|
else
|
|
-- somewhat larger and quite usable (v2 is pretty ok)
|
|
table.insert(formspec, "button[0.1,0;1,3;button_down;^\nU\np]")
|
|
table.insert(formspec, "button[0.1,3.2;1,4.5;button_up;D\no\nw\nn\nv]")
|
|
table.insert(formspec, "button[53.5,0;1,3;button_down;^\nU\np]")
|
|
table.insert(formspec, "button[53.5,3.2;1,4.5;button_up;D\no\nw\nn\nv]")
|
|
end
|
|
end
|
|
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 and yl_speak_up.old_fs_version_show_line(pname_for_old_fs)) then
|
|
local offset = 0.0
|
|
local field_length = 44.4
|
|
if(pname_for_old_fs) then
|
|
offset = 0.7
|
|
field_length = 42.4
|
|
end
|
|
h = h + 1
|
|
-- add a button "o_<nr>:" that leads to an edit formspec for this option
|
|
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
|
|
"button", tostring(2.3+offset).."," .. h .. ";2,0.9", "edit_option_" .. oid,
|
|
oid,
|
|
"Edit target dialog, pre(C)onditions and (Ef)fects for option "..oid..".",
|
|
true)
|
|
-- 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
|
|
-- add a button "-> d_<nr>" that leads to the target dialog (if one is set)
|
|
-- selecting an option this way MUST NOT execute the pre(C)onditions or (Ef)fects!
|
|
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
|
|
"button", tostring(9.0+offset).."," .. h .. ";1,0.9", "button_" .. oid,
|
|
"->",
|
|
"Go to target dialog "..minetest.formspec_escape(target_dialog or "")..
|
|
" that will be shown when this option ("..oid..") is selected.",
|
|
(target_dialog))
|
|
|
|
-- allow to set a new target dialog
|
|
table.insert(formspec, "dropdown["..tostring(4.4+offset)..","..h..";4.7,1;d_id_"..
|
|
oid..";"..
|
|
dialog_list..";"..
|
|
minetest.formspec_escape(d_id_to_dropdown_index[(target_dialog or "?")] or "0")..",]")
|
|
-- add a tooltip "Change target dialog"
|
|
table.insert(formspec, "tooltip[4.4,"..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
|
|
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
|
|
"button", tostring(0.5+offset).."," .. h .. ";0.5,0.9", "conditions_"..oid,
|
|
"C",
|
|
"There are pre(C)onditions required for showing this option. Display them.",
|
|
(prereq and next(prereq)))
|
|
|
|
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
|
|
"button", tostring(1.6+offset).."," .. h .. ";0.6,0.9", "effects_"..oid,
|
|
"Ef",
|
|
"There are further (Ef)fects (apart from switching\n"..
|
|
"to a new dialog) set for this option. Display them.",
|
|
(has_other_results))
|
|
|
|
-- are there any actions defined?
|
|
local actions = active_dialog.d_options[sb_v.o_id].actions
|
|
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
|
|
"button", tostring(1.1+offset).."," .. h .. ";0.5,0.9", "actions_"..oid,
|
|
"A",
|
|
"There is an (A)ction (i.e. a trade) that will happen\n"..
|
|
"when switching to a new dialog. Display actions and\n"..
|
|
"trade of this option.",
|
|
(actions and next(actions)))
|
|
|
|
-- show the actual text for the option
|
|
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
|
|
"field", tostring(9.9+offset).."," .. h .. ";"..
|
|
tostring(field_length)..",0.9",
|
|
"text_option_" .. oid,
|
|
";"..minetest.formspec_escape(sb_v.o_text_when_prerequisites_met),
|
|
"Edit the text that is displayed on button "..oid..".",
|
|
true)
|
|
|
|
-- normal mode: show an option if the prerequirements (if any are defined) are met
|
|
elseif(not(edit_mode)) then
|
|
local t = "- no text given -"
|
|
local t_alt = nil
|
|
-- the preconditions are fulfilled; showe the option
|
|
if(allowed[sb_v.o_id] == true) then
|
|
-- replace $NPC_NAME$ etc.
|
|
t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text(
|
|
sb_v.o_text_when_prerequisites_met, dialog, pname))
|
|
-- precondition not fulfilled? the option shall be hidden
|
|
elseif(sb_v.o_hide_when_prerequisites_not_met == "true") then
|
|
-- show nothing; t_alt remains nil
|
|
t = nil
|
|
-- precondition not fulfilled? the option shall be greyed out
|
|
-- default to greyed out (this option cannot be selected)
|
|
elseif(sb_v.o_grey_when_prerequisites_not_met == "true") then
|
|
local text = sb_v.o_text_when_prerequisites_not_met
|
|
if(not(text) or text == "") then
|
|
text = t or yl_speak_up.message_button_option_prerequisites_not_met_default
|
|
end
|
|
t = nil
|
|
-- replace $NPC_NAME$ etc.
|
|
t_alt = minetest.formspec_escape(yl_speak_up.replace_vars_in_text(
|
|
text, dialog, pname))
|
|
elseif(sb_v.o_grey_when_prerequisites_not_met == "false"
|
|
and sb_v.o_text_when_prerequisites_not_met ~= "") then
|
|
-- show in normal coor
|
|
t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text(
|
|
sb_v.o_text_when_prerequisites_not_met, dialog, pname))
|
|
end
|
|
if(t or t_alt) then
|
|
-- actually show the button
|
|
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
|
|
"button_" .. oid,
|
|
t,
|
|
t,
|
|
(t and not(t_alt)),
|
|
t_alt,
|
|
nil, pname_for_old_fs)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- add a "I want to give you something" button to the first dialog if the NPC accepts items
|
|
if(active_dialog and active_dialog.d_sort and tonumber(active_dialog.d_sort) == 0) then
|
|
local offer_item_add_text = ""
|
|
if(edit_mode) then
|
|
offer_item_add_text = minetest.formspec_escape("[dialog d_got_item] -> ")
|
|
end
|
|
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
|
|
"player_offers_item",
|
|
"If you want to give something (items) to this NPC\n"..
|
|
"- either because he requested it or as a present -\n"..
|
|
"click here. The NPC will return items he doesn't want.",
|
|
offer_item_add_text.."I want to give you something.",
|
|
-- show this in edit mode and when the NPC actually accepts items
|
|
(edit_mode or dialog.n_dialogs["d_got_item"]), nil, nil, pname_for_old_fs)
|
|
end
|
|
|
|
|
|
-- If in edit mode, add new menu entries: "add new options", "end edit mode" and what else is needed.
|
|
if(edit_mode) then
|
|
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
|
|
"add_option",
|
|
-- chat option: "Add a new answer/option to this dialog."
|
|
"Adds a new option to this dialog. You can delete options via the option edit menu.",
|
|
"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
|
|
(anz_options < yl_speak_up.max_number_of_options_per_dialog),
|
|
"Maximum number of allowed answers/options reached. No further options/answers "..
|
|
"can be added.", nil, pname_for_old_fs)
|
|
|
|
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
|
|
"delete_this_empty_dialog",
|
|
-- chat option: "Delete this 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.",
|
|
"Delete this empty dialog.",
|
|
(active_dialog and active_dialog.d_text == "" and anz_options == 0),
|
|
-- (but only show this option if the dialog is empty)
|
|
"If you want to delete this dialog, you need to delete all options and its "..
|
|
"text first.", nil, pname_for_old_fs)
|
|
|
|
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
|
|
"show_what_points_to_this_dialog",
|
|
-- chat option: "Show what points to this dialog."
|
|
"Show which other dialog options or failed actions\n"..
|
|
"or effects lead the player to this dialog here.",
|
|
"Show what points to this dialog.",
|
|
-- there is no alternate text to show
|
|
true, nil, nil, pname_for_old_fs)
|
|
|
|
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
|
|
"make_first_option",
|
|
-- chat option: "Make this dialog the first one shown when starting to talk."
|
|
"The NPC has to start with one dialog when he is right-clicked. "..
|
|
"Make this dialog the one shown.",
|
|
"Make this dialog the first one shown when starting a conversation.",
|
|
(active_dialog and active_dialog.d_sort and tonumber(active_dialog.d_sort) ~= 0),
|
|
-- (but only show this option if it's not already the first one)
|
|
"This dialog will be shown whenever a conversation is started.", nil,pname_for_old_fs)
|
|
|
|
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
|
|
"show_inventory",
|
|
"Access and manage the inventory of the NPC. This is used for adding trade "..
|
|
"items, getting collected payments and managing quest items.",
|
|
"Show your inventory (only accessible to owner)!",
|
|
true, nil, nil, pname_for_old_fs)
|
|
|
|
|
|
-- chat option: Mute/Unmute NPC
|
|
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
|
|
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
|
|
"mute_npc",
|
|
-- chat option: mute the 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.",
|
|
"State: Not muted. Stop talking to other players while I give you new orders.",
|
|
(luaentity and luaentity.yl_speak_up.talk), nil, nil, pname_for_old_fs)
|
|
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
|
|
"un_mute_npc",
|
|
-- unmute the 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.",
|
|
"State: You are currently muted. Talk to anyone again who wants to talk to you.",
|
|
-- the NPC has to be there
|
|
(luaentity and not(luaentity.yl_speak_up.talk)), nil, nil, pname_for_old_fs)
|
|
|
|
|
|
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
|
|
"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.",
|
|
-- chat option:"That was all. I'm finished with giving you new orders. Remember them!"
|
|
"That was all. I'm finished with giving you new orders. Remember them!",
|
|
true, nil, true, pname_for_old_fs) -- is button_exit
|
|
|
|
-- 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
|
|
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
|
|
"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.",
|
|
-- chat option: "I am your owner. I have new orders for you.
|
|
"I am your owner. I have new orders for you.",
|
|
true, nil, true, pname_for_old_fs) -- is button_exit
|
|
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
|
|
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
|
|
"show_trade_list",
|
|
"Show a list of trades the NPC has to offer.",
|
|
"Let's trade!",
|
|
(has_trades), nil, nil, pname_for_old_fs)
|
|
end
|
|
|
|
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
|
|
"button_exit",
|
|
yl_speak_up.message_button_option_exit,
|
|
yl_speak_up.message_button_option_exit,
|
|
true, nil, true, pname_for_old_fs) -- button_exit
|
|
|
|
if(fs_version > 2) then
|
|
table.insert(formspec, "scroll_container_end[]")
|
|
else
|
|
table.insert(formspec, "container_end[]")
|
|
end
|
|
table.insert(formspec, "container_end[]")
|
|
return table.concat(formspec, "")
|
|
end
|
|
|