forked from Sokomine/yl_speak_up
437 lines
17 KiB
Lua
437 lines
17 KiB
Lua
-- the formspec menu used for talking to the NPC can also used for
|
|
-- the trading formspec and similar things
|
|
|
|
-- the chat command npc_talk_style defined in register_once.lua
|
|
-- can be used to switch formspec style.
|
|
-- change the formspec style used in fs_decorated.lua;
|
|
--minetest.register_chatcommand( 'npc_talk_style', {
|
|
-- description = "This command sets your formspec version "..
|
|
-- "for the yl_speak_up NPC to value <version>.\n"..
|
|
-- " Version 1: For very old clients. Not recommended.\n"..
|
|
-- " Version 2: Adds extra scroll buttons. Perhaps you like this more.\n"..
|
|
-- " Version 3: Default version.",
|
|
-- privs = {},
|
|
yl_speak_up.command_npc_talk_style = function(pname, param)
|
|
-- set a default value
|
|
if(not(yl_speak_up.fs_version[pname])) then
|
|
yl_speak_up.fs_version[pname] = 3
|
|
end
|
|
if(param and param == "1") then
|
|
yl_speak_up.fs_version[pname] = 1
|
|
elseif(param and param == "2") then
|
|
yl_speak_up.fs_version[pname] = 2
|
|
elseif(param and param == "3") then
|
|
yl_speak_up.fs_version[pname] = 3
|
|
else
|
|
minetest.chat_send_player(pname, "This command sets your formspec version "..
|
|
"for the yl_speak_up NPC to value <version>.\n"..
|
|
" Version 1: For very old clients. Not recommended.\n"..
|
|
" Version 2: Adds extra scroll buttons.\n"..
|
|
" Version 3: Default (recommended) version.")
|
|
end
|
|
minetest.chat_send_player(pname, "Your formspec version for the yl_speak_up NPC "..
|
|
"has been set to version "..tostring(yl_speak_up.fs_version[pname])..
|
|
" for this session.")
|
|
end
|
|
|
|
|
|
-- helper function for yl_speak_up.show_fs_decorated
|
|
yl_speak_up.calculate_portrait = function(pname, n_id)
|
|
|
|
local mesh = yl_speak_up.get_mesh(pname)
|
|
-- which texture from the textures list are we talking about?
|
|
-- this depends on the model!
|
|
if(not(mesh)
|
|
or not(yl_speak_up.mesh_data[mesh])
|
|
or not(yl_speak_up.mesh_data[mesh].texture_index)) then
|
|
return ""
|
|
end
|
|
local texture_index = yl_speak_up.mesh_data[mesh].texture_index
|
|
local tex = yl_speak_up.speak_to[pname].textures
|
|
if(not(tex) or not(tex[texture_index])) then
|
|
return ""
|
|
end
|
|
return "[combine:8x8:-8,-8=" .. tex[texture_index] .. ":-40,-8=" .. tex[texture_index]
|
|
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, anz_lines)
|
|
-- the player is using a new enough version for scroll_container to work
|
|
if(not(pname)) then
|
|
return true
|
|
end
|
|
if(not(anz_lines) or anz_lines < 1) then
|
|
anz_lines = 1
|
|
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
|
|
if(not(counter)) then
|
|
counter = 1
|
|
end
|
|
yl_speak_up.speak_to[pname].counter = counter + anz_lines
|
|
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(..)
|
|
-- and yl_speak_up.show_fs_decorated
|
|
-- (optional) anz_lines: trade offers take up more room
|
|
-- (optional) multi_line_content: for trade offers; draw them directly
|
|
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, anz_lines, multi_line_content)
|
|
if(not(anz_lines) or anz_lines < 1) then
|
|
anz_lines = 1
|
|
end
|
|
-- 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,"..tostring(0.9*anz_lines)..";"
|
|
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, anz_lines))) then
|
|
return h
|
|
end
|
|
-- there has to be more room for the up and down arrows
|
|
button_dimensions = "1.2,"..(h+1)..";52.3,"..tostring(0.9*anz_lines)..";"
|
|
label_start_pos = "1.4"
|
|
end
|
|
h = h + anz_lines
|
|
if(multi_line_content) then
|
|
table.insert(formspec, "container[1.2,"..(h+1 - anz_lines).."]")
|
|
table.insert(formspec, multi_line_content)
|
|
table.insert(formspec, "container_end[]")
|
|
elseif(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(..)
|
|
-- and yl_speak_up.show_fs_decorated
|
|
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.."]")
|
|
|
|
-- make sure the lines in the mouseover text don't get too long
|
|
-- each paragraph has to be split seperately so that the old newlines are kept
|
|
local paragraphs = string.split(tooltip, "\n", true, -1, false)
|
|
for i, p in ipairs(paragraphs) do
|
|
paragraphs[i] = minetest.wrap_text(paragraphs[i], 100, false)
|
|
end
|
|
tooltip = table.concat(paragraphs, "\n")
|
|
table.insert(formspec, "tooltip["..tostring(element_name)..";"..tostring(tooltip).."]")
|
|
end
|
|
|
|
|
|
-- in formspec versions lower than 3, scrolling has to be handled diffrently;
|
|
-- this updates:
|
|
-- yl_speak_up.fs_version[pname]
|
|
-- yl_speak_up.speak_to[pname].counter
|
|
yl_speak_up.get_pname_for_old_fs = function(pname)
|
|
-- depending on formspec version, diffrent formspec elements are available
|
|
local fs_version = yl_speak_up.fs_version[pname]
|
|
if(not(fs_version)) then
|
|
local formspec_v = minetest.get_player_information(pname).formspec_version
|
|
local protocol_v = minetest.get_player_information(pname).protocol_version
|
|
|
|
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
|
|
yl_speak_up.fs_version[pname] = fs_version
|
|
end
|
|
if(fs_version <= 2) then
|
|
-- old formspec versions need to remember somewhere extern how far the player scrolled
|
|
yl_speak_up.speak_to[pname].counter = 1
|
|
return pname
|
|
end
|
|
return nil
|
|
end
|
|
|
|
|
|
|
|
-- display the window with the text the NPC is saying
|
|
-- Note: In edit mode, and if there is a dialog selected, the necessary
|
|
-- elements for editing said text are done in the calling function.
|
|
yl_speak_up.show_fs_npc_text = function(pname, formspec, dialog, alternate_text, active_dialog, fs_version)
|
|
if(alternate_text and active_dialog and active_dialog.d_text) then
|
|
alternate_text = string.gsub(alternate_text, "%$TEXT%$", active_dialog.d_text)
|
|
elseif(active_dialog and active_dialog.d_text) then
|
|
alternate_text = active_dialog.d_text
|
|
end
|
|
-- replace $NPC_NAME$ etc.
|
|
local t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text(
|
|
alternate_text, dialog, pname))
|
|
-- t = "Visits to this dialog: "..tostring(active_dialog.visits).."\n"..t
|
|
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
|
|
return formspec
|
|
end
|
|
|
|
|
|
|
|
yl_speak_up.show_fs_decorated = function(pname, npc_text_already_printed, h,
|
|
alternate_text,
|
|
add_this_to_left_window,
|
|
add_this_to_bottom_window,
|
|
active_dialog,
|
|
h)
|
|
if(not(pname)) then
|
|
return ""
|
|
end
|
|
-- which NPC is the player talking to?
|
|
local n_id = yl_speak_up.speak_to[pname].n_id
|
|
local dialog = yl_speak_up.speak_to[pname].dialog
|
|
if(n_id and dialog and not(dialog.n_npc)) then
|
|
dialog.n_npc = n_id
|
|
end
|
|
if(n_id and dialog and not(dialog.n_description)) then
|
|
dialog.n_description = "- no description -"
|
|
end
|
|
-- do we have all the necessary data?
|
|
if(not(n_id) or not(dialog) or not(dialog.n_npc)) then
|
|
return "size[6,2]"..
|
|
"label[0.2,0.5;Ups! This NPC lacks ID or name.]"..
|
|
"button_exit[2,1.5;1,0.9;exit;Exit]"
|
|
end
|
|
|
|
-- depending on formspec version, diffrent formspec elements are available
|
|
local fs_version = yl_speak_up.fs_version[pname]
|
|
|
|
local portrait = yl_speak_up.calculate_portrait(pname, n_id)
|
|
|
|
local formspec = {}
|
|
|
|
-- 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
|
|
|
|
formspec = {
|
|
"size[58,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;55.6,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[54.75,23.75;1,1;yl_speak_up_bg_dialog_tr.png]",
|
|
"image[54.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[54.75,24.75;1,6;yl_speak_up_bg_dialog_hr.png]",
|
|
"image[0.75,23.75;54,1;yl_speak_up_bg_dialog_vt.png]",
|
|
"image[0.75,30.75;54,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, "style[button_start_edit_mode,show_log,add_option,"..
|
|
"delete_this_empty_dialog,show_what_points_to_this_dialog,"..
|
|
"make_first_option,turn_into_a_start_dialog,mute_npc,"..
|
|
"un_mute_npc,button_end_edit_mode,show_inventory,order_follow,"..
|
|
"button_edit_basics,"..
|
|
"order_stand,order_wander,order_custom;"..
|
|
"bgcolor=#FF4444;textcolor=white]")
|
|
-- 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
|
|
|
|
-- display the window with the text the NPC is saying
|
|
-- Note: In edit mode, and if there is a dialog selected, the necessary
|
|
-- elements for editing said text are done in the calling function.
|
|
if(not(npc_text_already_printed) or not(dialog) or not(dialog.n_dialogs)) then
|
|
yl_speak_up.show_fs_npc_text(pname, formspec, dialog, alternate_text, active_dialog, fs_version)
|
|
end
|
|
|
|
-- add custom things (mostly for editing a dialog) to the window shown left
|
|
table.insert(formspec, add_this_to_left_window)
|
|
|
|
local pname_for_old_fs = nil
|
|
|
|
-- is there any need to allow scrolling?
|
|
local allow_scrolling = true
|
|
-- in case of older formspec versions: it already does look pretty ugly; there
|
|
-- is no point in checking there if we can scroll or not
|
|
if(fs_version > 2 and h + 1 < yl_speak_up.max_number_of_buttons) then
|
|
allow_scrolling = false
|
|
end
|
|
|
|
if(allow_scrolling and fs_version < 3) then
|
|
table.insert(formspec, "style_type[button;bgcolor=#FFFFFF]")
|
|
table.insert(formspec, "background[45,19.5;9.5,5;yl_speak_up_bg_dialog.png;false]")
|
|
table.insert(formspec, "box[45.1,19.6;9.3,4.8;#BBBBBB]")
|
|
table.insert(formspec, "image_button[45.5,20;4,4;gui_furnace_arrow_bg.png;button_down;Up]")
|
|
table.insert(formspec, "image_button[50,20;4,4;gui_furnace_arrow_bg.png^[transformR180;button_up;Down]")
|
|
table.insert(formspec, "style_type[button;bgcolor=#a37e45]")
|
|
end
|
|
|
|
if(allow_scrolling and fs_version > 2) then
|
|
local max_scroll = math.ceil(h - yl_speak_up.max_number_of_buttons) + 1
|
|
-- table.insert(formspec, "scrollbar[0.2,24.2;0.2,7;vertical;scr0;0]")
|
|
table.insert(formspec, "scrollbaroptions[min=0;max=")
|
|
table.insert(formspec, tostring(max_scroll*10))
|
|
table.insert(formspec, ";thumbsize=")
|
|
table.insert(formspec, tostring(math.ceil(
|
|
yl_speak_up.max_number_of_buttons /
|
|
(yl_speak_up.max_number_of_buttons + max_scroll)*max_scroll*10)))
|
|
table.insert(formspec, ";smallstep=10")
|
|
table.insert(formspec, ";largestep=")
|
|
table.insert(formspec, tostring(yl_speak_up.max_number_of_buttons*10))
|
|
table.insert(formspec, "]")
|
|
table.insert(formspec, "scrollbar[54.2,24.2;1.2,7.2;vertical;scr0;1]")
|
|
table.insert(formspec, "scroll_container[-0.2,24;54.2,7;scr0;vertical;0.1]")
|
|
|
|
elseif(allow_scrolling) then
|
|
if(fs_version < 2) then
|
|
-- if the player has an older formspec version
|
|
-- (the NPC itself is not relevant, and players reading the NPC logfile don't
|
|
-- need this information)
|
|
yl_speak_up.log_change(pname, nil,
|
|
"User " .. pname .. " talked to NPC ID n_" .. n_id ..
|
|
" with an old formspec version!", "info")
|
|
table.insert(formspec,
|
|
"box[0.3,20;19,2.6;red]"..
|
|
"label[0.7,20.3;"..yl_speak_up.text_version_warning.."]")
|
|
-- TODO delete these obsolete buttons
|
|
-- -- 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,23.1;6,0.9;button_down;^ Scroll Up ^]")
|
|
-- table.insert(formspec, "button[49,31.5;6,0.9;button_up;v Scroll Down v]")
|
|
-- table.insert(formspec, "button[1,23.1;6,0.9;button_down;^ Scroll Up ^]")
|
|
-- table.insert(formspec, "button[1,31.5;6,0.9;button_up;v Scroll Down v]")
|
|
end
|
|
table.insert(formspec, "container[0,24]")
|
|
-- TODO delete these obsolete buttons
|
|
-- 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
|
|
else
|
|
table.insert(formspec, "container[0,24]")
|
|
end
|
|
|
|
table.insert(formspec, add_this_to_bottom_window)
|
|
|
|
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(allow_scrolling and 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
|
|
|
|
|
|
yl_speak_up.show_fs_simple_deco = function(size_x, size_y)
|
|
local x_i = tostring(size_x - 1.5) -- "18.5"
|
|
local x_a = tostring(size_x - 0.75) -- "19.25"
|
|
local y_i = tostring(size_y - 1.25) --1.5) -- "21.5"
|
|
local y_a = tostring(size_y - 0.5) --0.75) -- "22.25"
|
|
local x_L = tostring(size_x + 0.8)
|
|
local y_L = tostring(size_y + 0.8)
|
|
-- formspecs that are not very high look odd otherwise
|
|
if(size_y < 6) then
|
|
y_L = y_L - 0.8
|
|
end
|
|
return "size["..tostring(size_x)..","..tostring(size_y).."]"..
|
|
"bgcolor[#00000000;false]"..
|
|
"style_type[button;bgcolor=#a37e45]"..
|
|
"style_type[button_exit;bgcolor=#a37e45]"..
|
|
"background[0,0;"..tostring(size_x)..","..tostring(size_y+0.25)..
|
|
";yl_speak_up_bg_dialog.png;false]"..
|
|
"image[-0.25,-0.25;1,1;yl_speak_up_bg_dialog_tl.png]"..
|
|
"image[-0.25,"..y_a..";1,1;yl_speak_up_bg_dialog_bl.png]"..
|
|
"image["..x_a..",-0.25;1,1;yl_speak_up_bg_dialog_tr.png]"..
|
|
"image["..x_a..","..y_a..";1,1;yl_speak_up_bg_dialog_br.png]"..
|
|
"image[-0.25,0.5;1,"..y_L..";yl_speak_up_bg_dialog_hl.png]"..
|
|
"image["..x_a..",0.5;1,"..y_L..";yl_speak_up_bg_dialog_hr.png]"..
|
|
"image[0.5,-0.25;"..x_L..",1;yl_speak_up_bg_dialog_vt.png]"..
|
|
"image[0.5,"..y_a..";"..x_L..",1;yl_speak_up_bg_dialog_vb.png]"
|
|
end
|