split mod into core (this mod) and editor (npc_talk_edit mod)

This commit is contained in:
Sokomine 2024-03-22 22:33:27 +01:00
parent e262b53a04
commit 5f2917df01
37 changed files with 9 additions and 10947 deletions

View File

@ -1,434 +0,0 @@
-- helper function for yl_speak_up.handle_input_fs_edit_option_related
-- (handle editing of alternate texts that are shown instead of the normal dialog)
yl_speak_up.handle_edit_actions_alternate_text = function(
player, pname, n_id, d_id, o_id, x_id, id_prefix,
formspec_input_to, data, fields, tmp_data_cache)
local dialog = yl_speak_up.speak_to[pname].dialog
if(not(dialog)
or not(dialog.n_dialogs)
or not(dialog.n_dialogs[ d_id ])
or not(dialog.n_dialogs[ d_id ].d_options)
or not(dialog.n_dialogs[ d_id ].d_options[ o_id ])) then
return
end
-- edit_dialog_options: these first two buttons can only be pressed in this dialog
-- action failed: want to edit the text that is shown when switching to the next dialog?
if(fields.button_edit_action_failed_dialog) then
-- the target effect is the (failed) action
local target_action = {}
local actions = dialog.n_dialogs[ d_id ].d_options[ o_id ].actions
if(actions) then
for a_id, a in pairs(actions) do
if(a and a.a_id) then
target_action = a
end
end
end
if(not(target_action)) then
return
end
-- remember what we're working at
yl_speak_up.speak_to[pname].edit_alternate_text_for = target_action
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:"..formspec_input_to,
formspec = yl_speak_up.extend_fs_edit_dialog_modification(
dialog, target_action.a_on_failure, target_action.alternate_text,
"if the action \""..tostring(target_action.a_id)..
"\" of option \""..tostring(o_id)..
"\" of dialog \""..tostring(d_id)..
"\" failed because the player did something wrong")
})
-- showing new formspec - the calling function shall return as well
return true
-- action was successful: want to edit the text that is shown when switching to the next dialog?
elseif(fields.button_edit_action_success_dialog) then
-- the target effect is the "dialog" effect
local target_effect = {}
local results = dialog.n_dialogs[ d_id ].d_options[ o_id ].o_results
if(results) then
for r_id, r in pairs(results) do
if(r and r.r_type and r.r_type == "dialog") then
target_effect = r
end
end
end
if(not(target_effect)) then
return
end
-- remember what we're working at
yl_speak_up.speak_to[pname].edit_alternate_text_for = target_effect
-- this only happens in edit_options_dialog; log it directly
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:"..formspec_input_to,
formspec = yl_speak_up.extend_fs_edit_dialog_modification(
dialog, target_effect.r_value, target_effect.alternate_text,
"if the action "..
"of option \""..tostring(o_id)..
"\" of dialog \""..tostring(d_id)..
"\" was successful - or if there was no action")
})
-- showing new formspec - the calling function shall return as well
return true
-- in edit action dialog: edit alternate text for a failed action
elseif(fields.button_edit_action_on_failure_text_change) then
local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
local failure_id = ""
-- action is beeing edited; data.action_failure_dialog points to an index
if(data and data.action_failure_dialog) then
failure_id = sorted_dialog_list[ data.action_failure_dialog ]
end
-- remember what we edit
data.x_id = x_id
data.id_prefix = id_prefix
yl_speak_up.speak_to[pname].edit_alternate_text_for = data
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:"..formspec_input_to,
formspec = yl_speak_up.extend_fs_edit_dialog_modification(
dialog, failure_id, data.alternate_text,
"if the action \""..tostring(x_id)..
"\" of option \""..tostring(o_id)..
"\" of dialog \""..tostring(d_id)..
"\" failed because the player did something wrong")
})
-- showing new formspec - the calling function shall return as well
return true
-- edit alternate text for an on_failure effect
elseif(fields.button_edit_effect_on_failure_text_change) then
-- remember what we edit
data.x_id = x_id
data.id_prefix = id_prefix
yl_speak_up.speak_to[pname].edit_alternate_text_for = data
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:"..formspec_input_to,
formspec = yl_speak_up.extend_fs_edit_dialog_modification(
dialog, data.on_failure, data.alternate_text,
"if the effect \""..tostring(x_id)..
"\" of option \""..tostring(o_id)..
"\" of dialog \""..tostring(d_id)..
"\" failed to execute correctly")
})
-- showing new formspec - the calling function shall return as well
return true
-- edit alternate text for when the player has failed to do the action too many times
elseif(fields.button_edit_limit_action_failed_repeat) then
local timer_name = "timer_on_failure_"..tostring(d_id).."_"..tostring(o_id)
local timer_data = yl_speak_up.get_variable_metadata( timer_name, "parameter", true)
local alternate_text = yl_speak_up.standard_text_if_action_failed_too_often
if(timer_data and timer_data["alternate_text"]) then
alternate_text = timer_data["alternate_text"]
end
-- remember what we're working at
yl_speak_up.speak_to[pname].edit_alternate_text_for = "timer_on_failure"
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:"..formspec_input_to,
formspec = yl_speak_up.extend_fs_edit_dialog_modification(
dialog, d_id, alternate_text,
"if the player failed to complete the action "..
"\" of option \""..tostring(o_id)..
"\" of dialog \""..tostring(d_id)..
"\" too many times",
true) -- forbid_turn_into_new_dialog
})
-- showing new formspec - the calling function shall return as well
return true
-- edit alternate text whent he player has to wait a bit until he's allowed to repeat the
-- action (to avoid i.e. unlimited quest item handout)
elseif(fields.button_edit_limit_action_success_repeat) then
local timer_name = "timer_on_success_"..tostring(d_id).."_"..tostring(o_id)
local timer_data = yl_speak_up.get_variable_metadata( timer_name, "parameter", true)
local alternate_text = yl_speak_up.standard_text_if_action_repeated_too_soon
if(timer_data and timer_data["alternate_text"]) then
alternate_text = timer_data["alternate_text"]
end
-- remember what we're working at
yl_speak_up.speak_to[pname].edit_alternate_text_for = "timer_on_success"
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:"..formspec_input_to,
formspec = yl_speak_up.extend_fs_edit_dialog_modification(
dialog, d_id, alternate_text,
"if the player has complete the action "..
"\" of option \""..tostring(o_id)..
"\" of dialog \""..tostring(d_id)..
"\" just not long enough ago",
true) -- forbid_turn_into_new_dialog
})
-- showing new formspec - the calling function shall return as well
return true
-- save the changes
elseif(fields.save_dialog_modification) then
local old_text = "-none-"
local target_element = yl_speak_up.speak_to[pname].edit_alternate_text_for
if(target_element
and (target_element == "timer_on_failure" or target_element == "timer_on_success")) then
-- we're changing a timer (both can be handled the same way here)
local timer_name = target_element.."_"..tostring(d_id).."_"..tostring(o_id)
local timer_data = yl_speak_up.get_variable_metadata( timer_name, "parameter", true)
local alternate_text = yl_speak_up.standard_text_if_action_failed_too_often
if(target_element == "timer_on_success") then
alternate_text = yl_speak_up.standard_text_if_action_repeated_too_soon
end
if(timer_data and timer_data["alternate_text"]) then
alternate_text = timer_data["alternate_text"]
end
-- store the modified alternate text
if(fields.d_text_new and fields.d_text_new ~= ""
and fields.d_text_new ~= alternate_text) then
-- make sure the variable exists
if(yl_speak_up.add_time_based_variable(timer_name)) then
yl_speak_up.set_variable_metadata(timer_name, nil, "parameter",
"alternate_text", fields.d_text_new)
-- log the change
yl_speak_up.log_change(pname, n_id,
"Dialog "..d_id..", option "..tostring(o_id)..
": The text displayed for "..tostring(target_element)..
" was changed from "..
"["..tostring(alternate_text).."] to ["..
tostring(fields.d_text_new).."].")
end
end
elseif(target_element) then
data = target_element
id_prefix = "a_"
if(target_element.r_id) then
id_prefix = "r_"
end
old_text = target_element.alternate_text
else
old_text = data.alternate_text
end
if(data and fields.d_text_new and fields.d_text_new ~= "$TEXT$"
and fields.d_text_new ~= data.alternate_text) then
-- store modification
-- not necessary for edit_option_dialog
if(tmp_data_cache) then
data.alternate_text = fields.d_text_new
yl_speak_up.speak_to[pname][ tmp_data_cache ] = data
else
target_element.alternate_text = fields.d_text_new
end
if(id_prefix == "r_") then
local failure_id = data.on_failure
-- effect is beeing edited; data.on_failure contains the dialog name
if(data and data.on_failure) then
failure_id = data.on_failure
-- edit_option_dialog: data.r_value contains the dialog name
elseif(target_element and target_element.r_value) then
failure_id = target_element.r_value
end
-- record the change
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": The text displayed for dialog "..
tostring(failure_id).." when selecting option "..
tostring(o_id).." in dialog "..tostring( d_id )..
" and effect "..tostring(x_id).." failed "..
" was changed from "..
"["..tostring(old_text).."] to ["..tostring(fields.d_text_new).."].")
elseif(id_prefix == "a_") then
local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
local failure_id = ""
-- action is beeing edited; data.action_failure_dialog points to an index
if(data and data.action_failure_dialog) then
failure_id = sorted_dialog_list[ data.action_failure_dialog ]
-- edit_option_dialog: data.a_on_failure contains the dialog name
elseif(target_element and target_element.a_on_failure) then
failure_id = target_element.a_on_failure
end
-- record the change
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": The text displayed for dialog "..
tostring(failure_id).." when the action "..
tostring(x_id).." of option "..
tostring( o_id ).." in dialog "..tostring( d_id )..
" failed, was changed from "..
"["..tostring(old_text).."] to ["..tostring(fields.d_text_new).."].")
end
-- saved; finished editing
yl_speak_up.speak_to[pname].edit_alternate_text_for = nil
end
-- turn this alternate answer into a new dialog
elseif(fields.turn_alternate_text_into_new_dialog) then
local target_element = yl_speak_up.speak_to[pname].edit_alternate_text_for
if(target_element) then
data = target_element
if(data.id_prefix and data.x_id) then
id_prefix = data.id_prefix
x_id = data.x_id
else
id_prefix = "a_"
x_id = target_element.a_id
if(target_element.r_id) then
id_prefix = "r_"
x_id = target_element.r_id
end
end
end
-- create the new dialog
local new_dialog_id = yl_speak_up.add_new_dialog(dialog, pname, nil)
-- set the text (the previous alternate text)
dialog.n_dialogs[ new_dialog_id ].d_text = data.alternate_text
-- edit option: effect dialog - this is the normal progression from this dialog to the next
if( data.r_id and data.r_type and data.r_type == "dialog") then
data.r_value = new_dialog_id
data.alternate_text = nil
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": The alternate text for effect "..tostring(x_id)..
" (dialog) of option "..tostring(o_id).." was turned into the new dialog "..
tostring(new_dialog_id).." (edit option).")
-- edit option: the action failed
elseif(data.a_id and data.a_on_failure) then
data.a_on_failure = new_dialog_id
data.alternate_text = nil
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": The alternate text for action "..tostring(data.a_id)..
" of option "..tostring(o_id).." was turned into the new dialog "..
tostring(new_dialog_id).." (edit option).")
-- edit action: the action failed
elseif(data.what and data.what == 6 and data.action_failure_dialog) then
local sorted_dialog_list = yl_speak_up.sort_keys(dialog.n_dialogs)
data.action_failure_dialog = math.max(1,
table.indexof(sorted_dialog_list, new_dialog_id))
data.a_on_failure = new_dialog_id
data.alternate_text = nil
-- make sure its stored correctly
dialog.n_dialogs[d_id].d_options[o_id].actions[x_id].a_on_failure = new_dialog_id
dialog.n_dialogs[d_id].d_options[o_id].actions[x_id].alternate_text = nil
yl_speak_up.speak_to[pname][ tmp_data_cache ] = data
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": The alternate text for action "..tostring(x_id)..
" of option "..tostring(o_id).." was turned into the new dialog "..
tostring(new_dialog_id).." (edit action).")
-- edit effect: on_failure - the previous effect failed
elseif(data.what and data.what == 5 and data.on_failure) then
data.on_failure = new_dialog_id
data.alternate_text = nil
-- make sure its stored correctly
dialog.n_dialogs[d_id].d_options[o_id].o_results[x_id].on_failure = new_dialog_id
dialog.n_dialogs[d_id].d_options[o_id].o_results[x_id].alternate_text = nil
yl_speak_up.speak_to[pname][ tmp_data_cache ] = data
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": The alternate text for effect "..tostring(x_id)..
" of option "..tostring(o_id).." was turned into the new dialog "..
tostring(new_dialog_id).." (edit effect).")
end
end
end
yl_speak_up.show_colored_dialog_text = function(dialog, data, d_id, hypertext_pos,
alternate_label_text, postfix, button_name)
if(not(data)) then
return ""
end
-- if(math.random(1,2)==1) then data.alternate_text = "This is an alternate text.\n$TEXT$" end
-- slightly red in order to indicate that this is an on_failure dialog
local color = "776666"
-- ..except for normal redirecting to the next dialog with the dialog effect
-- (slightly yellow there)
if(data.r_id and data.r_type and data.r_type == "dialog") then
color = "777766"
end
local add_info_alternate_text = ""
local text = ""
if(dialog and dialog.n_dialogs and dialog.n_dialogs[ d_id ]) then
text = dialog.n_dialogs[ d_id ].d_text
end
if(d_id == "d_got_item") then
color = "777777"
text = "[This dialog shall only have automatic options. The text is therefore irrelevant.]"
end
if(d_id == "d_end") then
color = "777777"
text = "[The NPC will end this conversation.]"
end
if(not(text)) then
text = "[ERROR: No text!]"
end
if(data and data.alternate_text and data.alternate_text ~= "") then
add_info_alternate_text = alternate_label_text
-- replace $TEXT$ with the normal dialog text and make the new text yellow
text = "<style color=#FFFF00>"..
string.gsub(
data.alternate_text,
"%$TEXT%$",
"<style color=#FFFFFF>"..text.."</style>")..
"</style>"
-- slightly blue in order to indicate that this is a modified text
color = "333366"
end
-- fallback
if(not(text)) then
text = "ERROR: No dialog text found for dialog \""..tostring(d_id).."\"!"
end
-- display the variables in orange
text = yl_speak_up.replace_vars_in_text(text,
-- fake dialog; just adds the colors
-- also MY_NAME..but we can easily replace just one
{ n_npc = "<style color=#FF8800>$NPC_NAME$</style>",
npc_owner = "<style color=#FF8800>$OWNER_NAME$</style>"},
-- pname
"<style color=#FF8800>$PLAYER_NAME$</style>")
local edit_button = ""
-- if there is the possibility that an alternate text may be displayed: allow to edit it
-- and calculate the position of the button from the hypertext_pos position and size
if(button_name and button_name ~= "") then
local parts = string.split(hypertext_pos, ";")
local start = string.split(parts[1], ",")
local size = string.split(parts[2], ",")
edit_button = "button_exit["..
tostring(tonumber(start[1]) + tonumber(size[1]) - 3.5)..","..
tostring(tonumber(start[2]) + tonumber(size[2]) - 0.9)..";"..
"3.0,0.7;"..button_name..";Edit this text]"
end
return add_info_alternate_text..
postfix..
"hypertext["..hypertext_pos..";<global background=#"..color.."><normal>"..
minetest.formspec_escape(text or "?")..
"\n</normal>]"..
-- display the edit button *inside*/on top of the hypertext field
edit_button
end
-- this allows to edit modifications of a dialog that are applied when a given option
-- is choosen - i.e. when the NPC wants to answer some questions - but those answers
-- do not warrant their own dialog
yl_speak_up.extend_fs_edit_dialog_modification = function(dialog, d_id, alternate_dialog_text, explanation,
forbid_turn_into_new_dialog)
local nd = "button[9.0,12.3;6,0.7;turn_alternate_text_into_new_dialog;Turn this into a new dialog]"
if(forbid_turn_into_new_dialog) then
nd = ""
end
return table.concat({"size[20,13.5]",
"label[6.0,0.5;Edit alternate text]",
"label[0.2,1.0;The alternate text which you can edit here will be shown instead of "..
"the normal text of the dialog \"", tostring(d_id), "\" - but *only*\n",
tostring(explanation or "- missing explanation -"), ".]",
"label[0.2,2.3;This is the normal text of dialog \"",
minetest.formspec_escape(tostring(d_id)), "\", shown for reference:]",
yl_speak_up.show_colored_dialog_text(
dialog,
{r_id = "", r_type = "dialog"},
d_id,
"1.2,2.6;18.0,4.0;d_text_orig",
"", -- no modifications possible at this step
"",
""), -- no edit button here as this text cannot be changed here
"label[0.2,7.3;Enter the alternate text here. $TEXT$ will be replaced with the normal "..
"dialog text above:]",
"textarea[1.2,7.6;18.0,4.5;d_text_new;;",
minetest.formspec_escape(alternate_dialog_text or "$TEXT$"), "]",
"button[3.0,12.3;1,0.7;back_from_edit_dialog_modification;Abort]",
"button[6.0,12.3;1,0.7;save_dialog_modification;Save]",
nd
}, "")
end

View File

@ -1,270 +0,0 @@
-- returns a table with helpful information *if* the player is working on a quest;
-- else error_msg is set
yl_speak_up.player_is_working_on_quest = function(player)
if(not(player)) then
return
end
local t = {}
t.pname = player:get_player_name()
if(not(t.pname)) then
return {error_msg = "Player not found."}
end
if(not(yl_speak_up.speak_to or not(yl_speak_up.speak_to[t.pname]))) then
return {error_msg = "Player not working on a quest."}
end
t.q_id = yl_speak_up.speak_to[t.pname].q_id
if(not(t.q_id) or not(yl_speak_up.quests) or not(yl_speak_up.quests[t.q_id])) then
return {error_msg = "No quest selected or quest not found."}
end
t.quest = yl_speak_up.quests[t.q_id]
if(not(t.quest.step_data) or type(t.quest.step_data) ~= "table") then
yl_speak_up.quests[t.q_id].step_data = {}
end
-- TODO: check if the player has access to that data
t.step_data = yl_speak_up.quests[t.q_id].step_data
t.current_step = yl_speak_up.speak_to[t.pname].quest_step
-- check here if the step exists
if(t.current_step and not(t.step_data[t.current_step])) then
yl_speak_up.speak_to[t.pname].quest_step = nil
t.current_step = nil
end
-- t contains pname, q_id, quest, step_data and current_step - or error_msg
return t
end
-- show the error message created above
yl_speak_up.build_fs_quest_edit_error = function(error_msg, back_button_name)
return "size[10,3]"..
"label[0.2,0.5;Error:]"..
"label[0.5,1.0;"..minetest.colorize("#FFFF00",
minetest.formspec_escape(
minetest.wrap_text(tostring(error_msg), 80)))..
"]button[3.5,2.0;2,0.9;"..tostring(back_button_name)..";Back]"
end
-- for which other quest steps is this_step needed for?
yl_speak_up.quest_step_required_for = function(step_data, this_step)
-- find out the next quest step
local required_for = {}
for s, d in pairs(step_data) do
if(s and d and d.one_step_required and type(d.one_step_required) == "table"
and table.indexof(d.one_step_required, this_step) ~= -1) then
table.insert(required_for, s)
end
if(s and d and d.all_steps_required and type(d.all_steps_required) == "table"
and table.indexof(d.all_steps_required, this_step) ~= -1) then
table.insert(required_for, s)
end
end
table.sort(required_for)
return required_for
end
-- sorts quest steps into lists: start, middle, end, unconnected
yl_speak_up.quest_step_get_start_end_unconnected_lists = function(step_data)
local start_steps = {}
local end_steps = {}
local unconnected_steps = {}
-- construct tables of *candidates* for start/end steps first
for s, d in pairs(step_data) do
if(#d.one_step_required == 0 and #d.all_steps_required == 0) then
start_steps[s] = true
end
end_steps[s] = true
end
for s, d in pairs(step_data) do
-- anything that is required somewhere cannot be an end step
for i, s2 in ipairs(d.one_step_required or {}) do
end_steps[s2] = nil
end
for i, s2 in ipairs(d.all_steps_required or {}) do
end_steps[s2] = nil
end
end
local lists = {}
lists.start_steps = {}
lists.end_steps = {}
lists.unconnected_steps = {}
lists.middle_steps = {}
for s, d in pairs(step_data) do
-- if it's both a start and end step, then it's an unconnected step
if(start_steps[s] and end_steps[s]) then
table.insert(lists.unconnected_steps, s)
elseif(start_steps[s]) then
table.insert(lists.start_steps, s)
elseif(end_steps[s]) then
table.insert(lists.end_steps, s)
else
table.insert(lists.middle_steps, s)
end
end
return lists
end
-- some lists are offered in diffrent formspecs for selection;
-- this function will display the right quest step if possible
-- res needs to be yl_speak_up.player_is_working_on_quest(player)
yl_speak_up.handle_input_routing_show_a_quest_step = function(player, formname, fields, back_field_name, res)
if(not(player) or not(fields) or (fields and fields.back) or not(res)) then
return false
end
if(res.error_msg) then
yl_speak_up.show_fs(player, "msg", {
input_to = formname,
formspec = yl_speak_up.build_fs_quest_edit_error(error_msg, back_field_name)
})
return true
end
local step_data = res.step_data or {}
-- which quest step to show next? (if any)
local show_step = ""
-- was a quest step selected from the start/end/unconnected lists?
local list = {}
local field_name = ""
local row_offset = 0
if( fields.select_from_start_steps and fields.select_from_start_steps ~= "") then
-- selected a start quest step
list = yl_speak_up.quest_step_get_start_end_unconnected_lists(step_data).start_steps
field_name = "select_from_start_steps"
elseif(fields.select_from_end_steps and fields.select_from_end_steps ~= "") then
-- selected an end quest step
list = yl_speak_up.quest_step_get_start_end_unconnected_lists(step_data).end_steps
field_name = "select_from_end_steps"
elseif(fields.select_from_unconnected_steps and fields.select_from_unconnected_steps ~= "") then
-- selected an unconnected/unused quest step
list = yl_speak_up.quest_step_get_start_end_unconnected_lists(step_data).unconnected_steps
field_name = "select_from_unconnected_steps"
elseif(res.current_step and step_data[res.current_step] and fields.one_step_required) then
list = step_data[res.current_step].one_step_required
field_name = "one_step_required"
elseif(res.current_step and step_data[res.current_step] and fields.all_steps_required) then
list = step_data[res.current_step].all_steps_required
field_name = "all_steps_required"
elseif(res.current_step and step_data[res.current_step] and fields.next_steps_show) then
list = yl_speak_up.quest_step_required_for(step_data, res.current_step)
field_name = "next_steps_show"
elseif(fields.add_from_available) then
-- selected a quest step from the list of available steps offered
list = yl_speak_up.speak_to[res.pname].available_quest_steps or {}
field_name = "add_from_available"
-- this table has a header
row_offset = 1
-- show prev logical step
elseif(fields.show_prev_step and res.current_step and step_data[res.current_step]) then
if( #step_data[res.current_step].one_step_required > 0) then
show_step = step_data[res.current_step].one_step_required[1]
elseif(#step_data[res.current_step].all_steps_required > 0) then
show_step = step_data[res.current_step].all_steps_required[1]
end
-- show next logical step
elseif(fields.show_next_step) then
local list = yl_speak_up.quest_step_required_for(res.step_data, res.current_step)
if(list and #list > 0) then
show_step = list[1]
end
end
if(list and field_name) then
local selected = minetest.explode_table_event(fields[field_name])
-- if a table uses a header, row_offset will be 1; else 0
if(selected and selected.row and selected.row > row_offset and selected.row <= #list + row_offset) then
show_step = list[selected.row - row_offset]
end
end
-- actually show the selected quest step
if(show_step and show_step ~= "") then
yl_speak_up.speak_to[res.pname].quest_step = show_step
yl_speak_up.show_fs(player, "manage_quest_steps", show_step)
return true
-- show the entire list
elseif(fields.show_step_list) then
yl_speak_up.speak_to[res.pname].tmp_index_general = -1
yl_speak_up.speak_to[res.pname].quest_step = nil
yl_speak_up.show_fs(player, "manage_quest_steps", nil)
return true
end
return false
end
-- describe a location where a quest step can be set; also used by yl_speak_up.fs_manage_quest_steps
yl_speak_up.quest_step_show_where_set = function(pname, formspec, label, n_id, d_id, o_id, box_color, nr)
if(not(pname)) then
return
end
-- what are we talking about?
local dialog = nil
if(yl_speak_up.speak_to[pname] and yl_speak_up.speak_to[pname].n_id == n_id) then
dialog = yl_speak_up.speak_to[pname].dialog
else
dialog = yl_speak_up.load_dialog(n_id, false)
end
local name_txt = "- ? -"
local dialog_txt = "- ? -"
local option_txt = "- ? -"
if(yl_speak_up.check_if_dialog_has_option(dialog, d_id, o_id)) then
dialog_txt = dialog.n_dialogs[d_id].d_text or "- ? -"
option_txt = dialog.n_dialogs[d_id].d_options[o_id].o_text_when_prerequisites_met or "- ? -"
name_txt = (dialog.n_npc or "- ? -")
if(dialog.n_description and dialog.n_description ~= "") then
name_txt = name_txt..", "..tostring(dialog.n_description)
end
end
-- are we dealing with an NPC?
local id_label = "the block at position "
if(n_id and string.sub(n_id, 1, 2) == "n_") then
id_label = "NPC "
end
if(box_color) then
name_txt = name_txt.." ["..tostring(n_id).."]"
table.insert(formspec, "label[0.2,0.2;")
if(nr) then
table.insert(formspec, tostring(nr)..". ")
end
table.insert(formspec, id_label)
table.insert(formspec, minetest.colorize("#AAAAFF", minetest.formspec_escape(name_txt)))
table.insert(formspec, " says in dialog ")
table.insert(formspec, minetest.colorize("#AAAAFF", minetest.formspec_escape(d_id)..":]"))
table.insert(formspec, "]")
table.insert(formspec, "box[1.0,0.4;16,1.8;")
table.insert(formspec, box_color)
table.insert(formspec, "]")
table.insert(formspec, "textarea[1.0,0.4;16,1.8;;;")
table.insert(formspec, minetest.formspec_escape(dialog_txt))
table.insert(formspec, "]")
table.insert(formspec, "label[1.0,2.4;Answer ")
table.insert(formspec, minetest.colorize("#AAAAFF", minetest.formspec_escape(o_id..": ")))
table.insert(formspec, minetest.colorize("#AAAAFF", minetest.formspec_escape(option_txt)))
table.insert(formspec, "]")
return
end
table.insert(formspec, "label[0.2,0;")
table.insert(formspec, label or "which will be set by ")
table.insert(formspec, id_label)
table.insert(formspec, minetest.colorize("#AAAAFF", minetest.formspec_escape(n_id)))
table.insert(formspec, ":]")
table.insert(formspec, "label[1.0,0.4;")
table.insert(formspec, minetest.colorize("#AAAAFF", minetest.formspec_escape(name_txt)))
table.insert(formspec, "]")
table.insert(formspec, "label[0.2,0.9;when answering to dialog ")
table.insert(formspec, minetest.colorize("#AAAAFF", minetest.formspec_escape(d_id)))
table.insert(formspec, ":]")
table.insert(formspec, "textarea[1.0,1.1;16,1.8;;;")
table.insert(formspec, minetest.formspec_escape(dialog_txt))
table.insert(formspec, "]")
table.insert(formspec, "label[0.2,3.2;with the following answer/option ")
table.insert(formspec, minetest.colorize("#AAAAFF", minetest.formspec_escape(o_id)))
table.insert(formspec, ":]")
table.insert(formspec, "label[1.0,3.6;")
table.insert(formspec, minetest.colorize("#AAAAFF", minetest.formspec_escape(option_txt)))
table.insert(formspec, "]")
end

View File

@ -1,318 +0,0 @@
-- helper function:
-- create a formspec dropdown list with player names (first entry: Add player) and
-- an option to delete players from that list
-- Note: With the what_is_the_list_about-parameter, it is possible to handle i.e. variables as well
yl_speak_up.create_dropdown_playerlist = function(player, pname,
table_of_names, index_selected,
start_x, start_y, stretch_x, h, dropdown_name, what_is_the_list_about, delete_button_text,
field_name_for_adding_player, explain_add_player,
field_name_for_deleting_player, explain_delete_player)
local text = "dropdown["..tostring(start_x)..","..tostring(start_y)..";"..
tostring(3.8 + stretch_x)..","..tostring(h)..";"..
tostring(dropdown_name)..";Add "..tostring(what_is_the_list_about)..":"
-- table_of_names is a table with the playernames as keys
-- we want to work with indices later on; in order to be able to do that reliably, we
-- need a defined order of names
local tmp_list = yl_speak_up.sort_keys(table_of_names, true)
for i, p in ipairs(tmp_list) do
text = text..","..minetest.formspec_escape(p)
end
-- has an entry been selected?
if(not(index_selected) or index_selected < 0 or index_selected > #tmp_list+1) then
index_selected = 1
end
text = text..";"..tostring(index_selected)..";]"
if(index_selected == 1) then
-- first index "Add player" selected? Then offer a field for entering the name
text = text.."field["..tostring(start_x + 4.0 + stretch_x)..","..tostring(start_y)..
";"..tostring(3.5 + stretch_x)..","..tostring(h)..";"..
tostring(field_name_for_adding_player)..";;]"..
"tooltip["..tostring(field_name_for_adding_player)..";"..
tostring(explain_add_player).."]"
else
text = text.."button["..tostring(start_x + 3.8 + stretch_x)..","..tostring(start_y)..
";"..tostring(3.4 + stretch_x)..","..tostring(h)..";"..
tostring(field_name_for_deleting_player)..";"..
tostring(delete_button_text).."]"..
"tooltip["..tostring(field_name_for_deleting_player)..";"..
tostring(explain_delete_player).."]"
end
return text
end
-- manages back, exit, prev, next, add_list_entry, del_entry_general
--
-- if a new entry is to be added, the following function that is passed as a parmeter
-- is called:
-- function_add_new_entry(pname, fields.add_entry_general)
-- expected return value: index of fields.add_entry_general in the new list
--
-- if an entry is to be deleted, the following function that is passed as a parameter
-- is called:
-- function_del_old_entry(pname, entry_name)
-- expected return value: text describing weather the removal worked or not
--
-- if any other fields are set that this function does not process, the following
-- function that is passed on as a parameter can be used:
-- function_input_check_fields(player, formname, fields, entry_name, list_of_entries)
-- expected return value: nil if the function found work; else entry_name
--
yl_speak_up.handle_input_fs_manage_general = function(player, formname, fields,
what_is_the_list_about, min_length, max_length, function_add_new_entry,
list_of_entries, function_del_old_entry, function_input_check_fields)
local pname = player:get_player_name()
local what = minetest.formspec_escape(what_is_the_list_about or "?")
local fs_name = formname
if(formname and string.sub(formname, 0, 12) == "yl_speak_up:") then
formname = string.sub(formname, 13)
end
if(fields and fields.back_from_msg) then
yl_speak_up.show_fs(player, formname, fields.stored_value_for_player)
return
end
-- leave this formspec
if(fields and (fields.quit or fields.exit or fields.back)) then
local last_fs = yl_speak_up.speak_to[pname][ "working_at" ]
local last_params = yl_speak_up.speak_to[pname][ "working_at_params" ]
yl_speak_up.speak_to[pname].tmp_index_general = nil
yl_speak_up.show_fs(player, last_fs, last_params)
return
-- add a new entry?
elseif(fields and fields.add_list_entry) then
local error_msg = ""
if(not(fields.add_entry_general) or fields.add_entry_general == ""
or fields.add_entry_general:trim() == "") then
error_msg = "Please enter the name of the "..what.." you want to create!"
-- limit names to something more sensible
elseif(string.len(fields.add_entry_general) > max_length) then
error_msg = "The name of your new "..what.." is too long.\n"..
"Only up to "..tostring(max_length).." characters are allowed."
elseif(string.len(fields.add_entry_general:trim()) < min_length) then
error_msg = "The name of your new "..what.." is too short.\n"..
"It has to be at least "..tostring(min_length).." characters long."
elseif(table.indexof(list_of_entries, fields.add_entry_general:trim()) > 0) then
error_msg = "A "..what.." with the name\n \""..
minetest.formspec_escape(fields.add_entry_general:trim())..
"\"\nexists already."
else
fields.add_entry_general = fields.add_entry_general:trim()
-- this depends on what is created
local res = function_add_new_entry(pname, fields.add_entry_general)
-- not really an error msg here - but fascilitates output
error_msg = "A new "..what.." named\n \""..
minetest.formspec_escape(fields.add_entry_general)..
"\"\nhas been created."
if(not(res) or (type(res) == "number" and res == -1)) then
error_msg = "Failed to create "..what.." named\n \""..
minetest.formspec_escape(fields.add_entry_general).."\"."
-- pass on any error messages
elseif(type(res) == "string") then
error_msg = res
else
-- select this new entry (add 1 because the first entry of our
-- list is adding a new entry)
yl_speak_up.speak_to[pname].tmp_index_general = res + 1
end
end
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:"..formname,
formspec = "size[10,2]"..
"label[0.2,0.0;"..error_msg.."]"..
"button[1.5,1.5;2,0.9;back_from_msg;Back]"})
return
-- scroll through the variables with prev/next buttons
elseif(fields and (fields["prev"] or fields["next"])) then
local index = yl_speak_up.speak_to[pname].tmp_index_general
if(not(index)) then
yl_speak_up.speak_to[pname].tmp_index_general = 1
elseif(fields["prev"] and index > 1) then
yl_speak_up.speak_to[pname].tmp_index_general = index - 1
elseif(fields["next"] and index <= #list_of_entries) then
yl_speak_up.speak_to[pname].tmp_index_general = index + 1
end
yl_speak_up.show_fs(player, formname, fields.stored_value_for_player)
return
end
-- an entry was selected in the dropdown list
if(fields and fields.list_of_entries and fields.list_of_entries ~= "") then
local index = table.indexof(list_of_entries, fields.list_of_entries)
-- show the "Add <entry>:" entry
if(fields.list_of_entries == "Add "..what..":") then
index = 0
yl_speak_up.speak_to[pname].tmp_index_general = 1
yl_speak_up.show_fs(player, formname, fields.stored_value_for_player)
return
end
if(index and index > -1) then
yl_speak_up.speak_to[pname].tmp_index_general = index + 1
end
end
local entry_name = list_of_entries[ yl_speak_up.speak_to[pname].tmp_index_general - 1]
-- delete entry
if(fields and ((fields.del_entry_general and fields.del_entry_general ~= ""))
and entry_name and entry_name ~= "") then
local text = function_del_old_entry(pname, entry_name)
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:"..formname,
formspec = "size[10,2]"..
"label[0.2,0.0;Trying to delete "..what.." \""..
minetest.formspec_escape(tostring(entry_name))..
"\":\n"..text.."]"..
"button[1.5,1.5;2,0.9;back_from_msg;Back]"})
return
-- maybe the custom function knows what to do with this
elseif(fields
and not(function_input_check_fields(player, formname, fields, entry_name, list_of_entries))) then
-- the function_input_check_fields managed to handle this input
return
-- an entry was selected in the dropdown list
elseif(entry_name and entry_name ~= "") then
-- show the same formspec again, with a diffrent variable selected
yl_speak_up.show_fs(player, formname)
return
end
-- try to go back to the last formspec shown before this one
if(not(yl_speak_up.speak_to[pname])) then
return
end
local last_fs = yl_speak_up.speak_to[pname][ "working_at" ]
local last_params = yl_speak_up.speak_to[pname][ "working_at_params" ]
yl_speak_up.show_fs(player, last_fs, last_params)
end
-- inserts buttons into formspec which allow to select previous/next entry, to go back,
-- create new entries, delete entries and select entries from a dropdown menu;
-- returns the currently selected entry or nil (=create new entry)
-- Note: Designed for a formspec of size "size[18,12]"
yl_speak_up.build_fs_manage_general = function(player, param,
formspec, list_of_entries,
text_add_new, tooltip_add_new,
what_is_the_list_about,
tooltip_add_entry_general, tooltip_del_entry_general,
optional_add_space)
if(not(optional_add_space)) then
optional_add_space = 0
end
local selected = nil
local pname = player:get_player_name()
-- the yl_speak_up.create_dropdown_playerlist function needs a table - not a list
local table_of_entries = {}
for i, k in ipairs(list_of_entries) do
table_of_entries[ k ] = true
end
-- "Add variable:" is currently selected
if(not(yl_speak_up.speak_to[pname].tmp_index_general)
or yl_speak_up.speak_to[pname].tmp_index_general == 1
or not(list_of_entries[ yl_speak_up.speak_to[pname].tmp_index_general - 1])) then
yl_speak_up.speak_to[pname].tmp_index_general = 1
table.insert(formspec, "button[")
table.insert(formspec, tostring(12.2 + 2 * optional_add_space))
table.insert(formspec, ",2.15;")
table.insert(formspec, tostring(2.5 + 2 * optional_add_space))
table.insert(formspec, ",0.6;add_list_entry;")
table.insert(formspec, minetest.formspec_escape(text_add_new))
table.insert(formspec, "]")
table.insert(formspec, "tooltip[add_list_entry;")
table.insert(formspec, minetest.formspec_escape(tooltip_add_new))
table.insert(formspec, "]")
else
-- index 1 is "Add variable:"
selected = list_of_entries[ yl_speak_up.speak_to[pname].tmp_index_general - 1]
end
if(yl_speak_up.speak_to[pname].tmp_index_general > 1) then
table.insert(formspec, "button[4.0,0.2;2.0,0.6;prev;< Prev]"..
"button[4.0,11.0;2.0,0.6;prev;< Prev]")
end
if(yl_speak_up.speak_to[pname].tmp_index_general <= #list_of_entries) then
table.insert(formspec, "button[12.0,0.2;2.0,0.6;next;Next >]"..
"button[12.0,11.0;2.0,0.6;next;Next >]")
end
table.insert(formspec, "button[0.0,0.2;2.0,0.6;back;Back]"..
"button[8.0,11.0;2.0,0.6;back;Back]")
local what = minetest.formspec_escape(what_is_the_list_about)
table.insert(formspec, "label[7.0,0.4;* Manage your ")
table.insert(formspec, what)
table.insert(formspec, "s *]")
table.insert(formspec, "label[0.2,2.45;Your ")
table.insert(formspec, what)
table.insert(formspec, ":]")
-- offer a dropdown list and a text input field for new varialbe names for adding
table.insert(formspec, yl_speak_up.create_dropdown_playerlist(
player, pname,
table_of_entries,
yl_speak_up.speak_to[pname].tmp_index_general,
2.6 + (optional_add_space), 2.15, 1.0, 0.6,
"list_of_entries",
what,
"Delete selected "..what,
"add_entry_general",
minetest.formspec_escape(tooltip_add_entry_general),
"del_entry_general",
minetest.formspec_escape(tooltip_del_entry_general)
))
-- either nil or the text of the selected entry
return selected
end
-- small helper function for the function below
yl_speak_up.get_sub_fs_colorize_table = function(formspec, table_specs, liste, color)
table.insert(formspec, "tablecolumns[color;text]table[")
table.insert(formspec, table_specs)
local tmp = {}
for k, v in pairs(liste) do
table.insert(tmp, color or "#FFFFFF")
table.insert(tmp, minetest.formspec_escape(v))
end
table.insert(formspec, table.concat(tmp, ","))
table.insert(formspec, ";]")
end
yl_speak_up.get_sub_fs_show_list_in_box = function(formspec,
label, field_name, liste, start_x, start_y, width, height, label_ident,
box_color, column_color,
tooltip_text, add_lines)
local dim_str = tostring(width)..","..tostring(height)
table.insert(formspec, "container[")
table.insert(formspec, tostring(start_x)..","..tostring(start_y)..";")
table.insert(formspec, dim_str)
table.insert(formspec, "]")
table.insert(formspec, "box[0,0;")
table.insert(formspec, dim_str)
table.insert(formspec, ";")
table.insert(formspec, box_color or "#666666")
table.insert(formspec, "]")
-- add buttons etc. first so that the label remains visible on top
if(add_lines) then
table.insert(formspec, add_lines)
end
table.insert(formspec, "label[")
table.insert(formspec, tostring(0.1 + label_ident))
table.insert(formspec, ",0.5;")
table.insert(formspec, label)
table.insert(formspec, "]")
yl_speak_up.get_sub_fs_colorize_table(formspec,
"0.1,0.7;"..tostring(width-0.2)..","..tostring(height-0.8)..";"..tostring(field_name)..";",
liste or {}, column_color)
if(tooltip_text and tooltip_text ~= "") then
table.insert(formspec, "tooltip[")
table.insert(formspec, field_name)
table.insert(formspec, ";")
table.insert(formspec, tooltip_text)
table.insert(formspec, "\n\nClick on an element to select it.")
table.insert(formspec, "]")
end
table.insert(formspec, "container_end[]")
end

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +0,0 @@
-- Implementation of chat commands that were registered in register_once
-- (here: only add those additional things needed for edit mode)
local old_command_npc_talk = yl_speak_up.command_npc_talk
yl_speak_up.command_npc_talk = function(pname, param)
if(not(pname)) then
return
end
-- activates edit mode when talking to an NPC; but only if the player can edit that NPC
if(param and param == "force_edit") then
-- implemented in functions.lua:
return yl_speak_up.command_npc_talk_force_edit(pname, rest)
end
-- not perfect - but at least some help
if(param
and (param == "help force_edit"
or param == "? force_edit"
or param == "force_edit help")) then
minetest.chat_send_player(pname,
"Toggles force edit mode. This is helpful if you cut yourself out "..
"of editing an NPC by breaking it. From now on all NPC you will talk to "..
"will already be in edit mode (provided you are allowed to edit them)."..
"\nIssuing the command again ends force edit mode.")
return
end
return old_command_npc_talk(pname, param)
end

View File

@ -1,24 +0,0 @@
-- allow to enter force edit mode (useful when an NPC was broken)
yl_speak_up.force_edit_mode = {}
-- command to enter force edit mode
yl_speak_up.command_npc_talk_force_edit = function(pname, param)
if(not(pname)) then
return
end
if(yl_speak_up.force_edit_mode[pname]) then
yl_speak_up.force_edit_mode[pname] = nil
minetest.chat_send_player(pname,
"Ending force edit mode for NPC. From now on talks "..
"will no longer start in edit mode.")
else
yl_speak_up.force_edit_mode[pname] = true
minetest.chat_send_player(pname,
"STARTING force edit mode for NPC. From now on talks "..
"with NPC will always start in edit mode provided "..
"you are allowed to edit this NPC.\n"..
"In order to end force edit mode, give the command "..
"/npc_talk_force_edit a second time.")
end
end

View File

@ -1,151 +0,0 @@
-- 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
-- (happens in fs/fs_talkdialog_in_edit_mode.lua)
-- 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 = {}
-- is the player in edit mode?
yl_speak_up.in_edit_mode = function(pname)
return pname
and yl_speak_up.edit_mode[pname]
and (yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id)
end
-- reset edit_mode when stopping to talk to an NPC
local old_reset_vars_for_player = yl_speak_up.reset_vars_for_player
yl_speak_up.reset_vars_for_player = function(pname, reset_fs_version)
yl_speak_up.edit_mode[pname] = nil
old_reset_vars_for_player(pname, reset_fs_version)
end
-- make sure generic dialogs are never included in edit_mode (because in edit mode we want to
-- edit this particular NPC without generic parts)
local old_load_dialog = yl_speak_up.load_dialog
yl_speak_up.load_dialog = function(n_id, player) -- returns the saved dialog
if(player and yl_speak_up.in_edit_mode(player:get_player_name())) then
return old_load_dialog(n_id, false)
end
return old_load_dialog(n_id, player)
end
-- in edit mode the dialog may be saved. visits to a particular dialog are of no intrest here
local old_count_visits_to_dialog = yl_speak_up.count_visits_to_dialog
yl_speak_up.count_visits_to_dialog = function(pname)
if(yl_speak_up.in_edit_mode(pname)) then
return
end
return old_count_visits_to_dialog(pname)
end
local modname = minetest.get_current_modname()
if(not(modname)) then
modname = "yl_speak_up"
end
-- TODO: adjust to new mod name and paths
local modpath = minetest.get_modpath(modname)..DIR_DELIM.."editor"..DIR_DELIM
-- this is a way to provide additional help if a mod adds further commands (like the editor)
yl_speak_up.add_to_command_help_text = yl_speak_up.add_to_command_help_text..
"\nAdditional commands provided by "..tostring(modname)..":\n"..
" force_edit forces edit mode for any NPC you talk to\n"
-- overrides of functions fo fs/fs_talkdialog.lua when in edit_mode (or for entering/leaving it)
dofile(modpath .. "fs/fs_talkdialog_edit_mode.lua")
-- edit preconditions (can be reached through edit options dialog)
dofile(modpath .. "fs/fs_edit_preconditions.lua")
-- edit actions (can be reached through edit options dialog)
dofile(modpath .. "fs/fs_edit_actions.lua")
-- edit effects (can be reached through edit options dialog)
dofile(modpath .. "fs/fs_edit_effects.lua")
-- edit options dialog (detailed configuration of options in edit mode)
dofile(modpath .. "fs/fs_edit_options_dialog.lua")
-- the player wants to change something regarding the dialog
dofile(modpath .. "edit_mode_apply_changes.lua")
-- handle page changes and asking for saving when in edit mode:
dofile(modpath .. "show_fs_in_edit_mode.lua")
-- ask if the player wants to save, discard or go back in edit mode
dofile(modpath .. "fs/fs_save_or_discard_or_back.lua")
-- the player wants to change something regarding the dialog
dofile(modpath .. "edit_mode_apply_changes.lua")
-- assign a quest step to a dialog option/answer
dofile(modpath .. "fs/fs_assign_quest_step.lua")
-- in edit_mode we need a more complex reaction to inventory changes
dofile(modpath .. "exec_actions_action_inv_changed.lua")
-- in edit_mode: effects are not executed
dofile(modpath .. "exec_all_relevant_effects.lua")
-- some helper functions for formatting text for a formspec talbe
dofile(modpath .. "print_as_table.lua")
-- create i.e. a dropdown list of player names
dofile(modpath .. "api/formspec_helpers.lua")
-- handle alternate text for dialogs
dofile(modpath .. "api/api_alternate_text.lua")
-- helpful for debugging the content of the created dialog structure
dofile(modpath .. "fs/fs_show_what_points_to_this_dialog.lua")
-- common functions for editing preconditions and effects
dofile(modpath .. "api/fs_edit_general.lua")
-- edit preconditions (can be reached through edit options dialog)
dofile(modpath .. "fs/fs_edit_preconditions.lua")
-- edit actions (can be reached through edit options dialog)
dofile(modpath .. "fs/fs_edit_actions.lua")
-- edit effects (can be reached through edit options dialog)
dofile(modpath .. "fs/fs_edit_effects.lua")
-- edit options dialog (detailed configuration of options in edit mode)
dofile(modpath .. "fs/fs_edit_options_dialog.lua")
dofile(modpath .. "fs/fs_initial_config_in_edit_mode.lua")
dofile(modpath .. "trade_in_edit_mode.lua")
dofile(modpath .. "fs/fs_add_trade_simple_in_edit_mode.lua")
-- handle back button diffrently when editing a trade as an action:
dofile(modpath .. "fs/fs_do_trade_simple_in_edit_mode.lua")
-- as the name says: list which npc acesses a variable how and in which context
dofile(modpath .. "fs/fs_get_list_of_usage_of_variable.lua")
-- show which values are stored for which player in a quest variable
dofile(modpath .. "fs/fs_show_all_var_values.lua")
-- manage quest variables: add, delete, manage access rights etc.
dofile(modpath .. "fs/fs_manage_variables.lua")
-- GUI for adding/editing quests
dofile(modpath .. "fs/fs_manage_quests.lua")
-- GUI for adding/editing quest steps for the quests
dofile(modpath .. "api/api_quest_steps.lua")
dofile(modpath .. "fs/fs_manage_quest_steps.lua")
-- used by the above
dofile(modpath .. "fs/fs_add_quest_steps.lua")
-- setting skin, wielded item etc.
dofile(modpath .. "fs/fs_fashion.lua")
dofile(modpath .. "fs/fs_fashion_extended.lua")
-- properties for NPC without specific dialogs that want to make use of
dofile(modpath .. "fs/fs_properties.lua")
-- /npc_talk force_edit (when talking to an NPC in the normal way fails):
dofile(modpath .. "command_force_edit_mode.lua")
-- add the force_edit option to the chat commands
dofile(modpath .. "chat_commands_in_edit_mode.lua")
-- creating and maintaining quests
dofile(modpath .. "fs/fs_quest_gui.lua")
-- take notes regarding what the NPC is for
dofile(modpath .. "fs/fs_notes.lua")

View File

@ -1,570 +0,0 @@
-- helper function for yl_speak_up.edit_mode_apply_changes;
-- makes sure the new dialog (and a result/effect "dialog" for each option) exist
yl_speak_up.prepare_new_dialog_for_option = function(dialog, pname, n_id, d_id, o_id,target_dialog,o_results)
-- this may also point to a new dialog
if(target_dialog == yl_speak_up.text_new_dialog_id) then
-- create a new dialog and show it as new target dialog - but do not display
-- this dialog directly (the player may follow the -> button)
target_dialog = yl_speak_up.add_new_dialog(dialog, pname, nil)
end
-- translate name into dialog id
target_dialog = yl_speak_up.d_name_to_d_id(dialog, target_dialog)
-- is there a result/effect of the type "dialog" already? else use a fallback
local result = {} --{r_value = "-default-"}
if(o_results) then
for kr, vr in pairs(o_results) do
if( vr.r_type == "dialog" ) then
result = vr
-- no problem - the right dialog is set already
if(result.r_value and result.r_value == target_dialog) then
return target_dialog
else
-- no need to search any further
break
end
end
end
end
local old_d = tostring(result.r_value or "-default-")
if(result.r_value and dialog.n_dialogs[result.r_value] and dialog.n_dialogs[result.r_value].d_name) then
old_d = old_d..":"..tostring(dialog.n_dialogs[result.r_value].d_name)
end
local new_d = tostring(target_dialog)
if(target_dialog and dialog.n_dialogs[target_dialog] and dialog.n_dialogs[target_dialog].d_name) then
new_d = new_d..":"..tostring(dialog.n_dialogs[target_dialog].d_name)
end
-- store that a new option has been added to this dialog
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": The target dialog for option "..
tostring(o_id).." was changed from "..
old_d.." to "..new_d..".")
-- does the result/effect of type "dialog" exist already? then we're done
if(result.r_type and result.r_type == "dialog") then
-- actually change the target dialog
result.r_value = target_dialog
return target_dialog
end
-- create a new result (first the id, then the actual result)
local future_r_id = yl_speak_up.add_new_result(dialog, d_id, o_id)
-- actually store the new result
dialog.n_dialogs[d_id].d_options[o_id].o_results[future_r_id] = {
r_id = future_r_id,
r_type = "dialog",
r_value = target_dialog}
return target_dialog
end
-- helper function for formspec "yl_speak_up:talk" *and* formspec "yl_speak_up:edit_option_dialog"
-- when a parameter was changed in edit mode;
-- this is called when the player is in edit_mode (editing the NPC);
-- the function checks if the player has changed any parameters
-- Parameters:
-- pname player name
-- fields the fields returned from the formspec
-- Returns:
-- result table with information about what was added
-- (for now, only result.show_next_option is of intrest in the option edit menu)
yl_speak_up.edit_mode_apply_changes = function(pname, fields)
local n_id = yl_speak_up.edit_mode[pname]
if(not(n_id) or not(yl_speak_up.speak_to[pname])) then
return
end
local d_id = yl_speak_up.speak_to[pname].d_id
local dialog = yl_speak_up.speak_to[pname].dialog
-- check if the player is allowed to edit this NPC
if(not(yl_speak_up.may_edit_npc(minetest.get_player_by_name(pname), n_id))) then
return
end
-- this way we can store the actual changes and present them to the player for saving
if(not(yl_speak_up.npc_was_changed[ n_id ])) then
yl_speak_up.npc_was_changed[ n_id ] = {}
end
-- nothing to do if that dialog does not exist
if(not(d_id) or not(dialog.n_dialogs) or not(dialog.n_dialogs[ d_id ])) then
return
end
-- allow owner to mute/unmute npc (would be bad if players can already see what is going
-- to happen while the owner creates a long quest)
-- mute/unmute gets logged in the function and does not need extra log entries
local obj = yl_speak_up.speak_to[pname].obj
if(fields.mute_npc and obj) then
yl_speak_up.set_muted(pname, obj, true)
elseif(fields.un_mute_npc and obj) then
yl_speak_up.set_muted(pname, obj, false)
end
-- changes to d_dynamic are *not* changed (the content of that dialog has to be provided
-- dynamicly by a function):
if(d_id == "d_dynamic") then
return
end
-- new options etc. may be added; store these IDs so that we can switch to the right target
local result = {}
-- make this the first dialog shown when starting a conversation
if(fields.make_first_option) then
-- check which dialog(s) previously had the highest priority and change thsoe
for k, v in pairs(dialog.n_dialogs) do
if(v and v.d_sort and (v.d_sort=="0" or v.d_sort==0)) then
-- try to derive a sensible future sort priority from the key:
-- here we make use of the d_<nr> pattern; but even if that fails to yield
-- a number, the sort function will later be able to deal with it anyway
local new_priority = string.sub(k, 3)
dialog.n_dialogs[ k ].d_sort = new_priority
end
end
-- actually make this the chat with the highest priority
dialog.n_dialogs[ d_id ].d_sort = "0"
-- this is not immediately saved, even though the changes to the previous dialog with
-- the highest priority cannot be automaticly undone (but as long as it is not saved,
-- it really does not matter; and when saving, the player has to take some care)
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": Turned into new start dialog.")
end
-- if it is *a* start dialog: buttons like give item to npc/trade/etc. will be shown
if(fields.turn_into_a_start_dialog) then
if(dialog.n_dialogs[ d_id ].is_a_start_dialog) then
-- no need to waste space...
dialog.n_dialogs[ d_id ].is_a_start_dialog = nil
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": Is no longer *a* start dialog (regarding buttons).")
else
dialog.n_dialogs[ d_id ].is_a_start_dialog = true
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": Turned into *a* start dialog (regarding buttons).")
end
end
-- detect changes to d_text: text of the dialog (what the npc is saying)
-- (only happens in dialog edit menu)
if(fields.d_text and dialog.n_dialogs[ d_id ].d_text ~= fields.d_text) then
-- store that there have been changes to this npc
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": d_text (what the NPC says) was changed from \""..
tostring( dialog.n_dialogs[ d_id ].d_text)..
"\" to \""..tostring(fields.d_text).."\".")
-- actually change the text - but do not save to disk yet
dialog.n_dialogs[ d_id ].d_text = fields.d_text
end
if(fields.d_name and fields.d_name ~= "" and dialog.n_dialogs[ d_id ].d_name ~= fields.d_name) then
if(fields.d_name ~= d_id
and not(yl_speak_up.is_special_dialog(d_id))) then
local err_msg = nil
-- check if there are no duplicate names
for k, v in pairs(dialog.n_dialogs) do
if(v and v.d_name and v.d_name == fields.d_name and k ~= d_id) then
err_msg = "Sorry. That name has already been used for dialog "..
tostring(k).."."
end
end
if(dialog.n_dialogs[fields.d_name]) then
err_msg = "Sorry. There is already a dialog with a dialog id of "..
tostring(fields.d_name).."."
elseif(yl_speak_up.is_special_dialog(fields.d_name)) then
err_msg = "Sorry. That is a special dialog ID. You cannot use it as "..
"a manually set name."
elseif(string.sub(fields.d_name, 1, 2) == "d_") then
err_msg = "Sorry. Names starting with \"d_\" are not allowed. They "..
"may later be needed for new dialogs."
end
-- TODO: check if the name is allowed (only normal chars, numbers and underscore)
if(err_msg) then
minetest.chat_send_player(pname, err_msg)
else
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": renamed from \""..
tostring(dialog.n_dialogs[ d_id ].d_name)..
"\" to \""..tostring(fields.d_name).."\".")
dialog.n_dialogs[ d_id ].d_name = fields.d_name
end
end
end
-- add a new option/answer
if(fields[ "add_option"]) then
local future_o_id = yl_speak_up.add_new_option(dialog, pname, nil, d_id, "", d_id)
if(not(future_o_id)) then
-- this is already checked earlier on and the button only shown if
-- options can be added; so this can reamin a chat message
minetest.chat_send_player(pname, "Sorry. Only "..
tostring(yl_speak_up.max_number_of_options_per_dialog)..
" options/answers are allowed per dialog.")
fields.add_option = nil
else
-- add_new_option has added a dialog result for us already - no need to do that again
-- if this is selected in the options edit menu, we want to move straight on to the new option
result["show_next_option"] = future_o_id
end
end
-- delete an option directly from the main fs_talkdialog
if(dialog.n_dialogs[d_id].d_options) then
for o_id, o_v in pairs(dialog.n_dialogs[d_id].d_options) do
if(o_id and fields["delete_option_"..o_id]) then
fields["del_option"] = true
fields.o_id = o_id
-- ..or move an option up by one in the list
elseif(o_id and fields["option_move_up_"..o_id]) then
fields["option_move_up"] = true
fields.o_id = o_id
-- ..or move an option down by one in the list
elseif(o_id and fields["option_move_down_"..o_id]) then
fields["option_move_down"] = true
fields.o_id = o_id
end
end
end
if(fields[ "del_option"] and fields.o_id and dialog.n_dialogs[d_id].d_options[fields.o_id]) then
local o_id = fields.o_id
-- which dialog to show instead of the deleted one?
local next_o_id = o_id
local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs[d_id].d_options, "o_sort")
for i, o in ipairs(sorted_list) do
if(o == o_id and sorted_list[ i+1 ]) then
next_o_id = sorted_list[ i+1 ]
elseif(o == o_id and sorted_list[ i-1 ]) then
next_o_id = sorted_list[ i-1 ]
end
end
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": Option "..tostring(o_id).." deleted.")
-- actually delete the dialog
dialog.n_dialogs[d_id].d_options[o_id] = nil
-- the current dialog is deleted; we need to show another one
result["show_next_option"] = next_o_id
-- after deleting the entry, all previous/further changes to it are kind of unintresting
return result
end
-- move an option up by one
local d_options = dialog.n_dialogs[d_id].d_options
if(fields[ "option_move_up"] and fields.o_id and d_options[fields.o_id]) then
local sorted_o_list = yl_speak_up.get_sorted_options(d_options, "o_sort")
local idx = table.indexof(sorted_o_list, fields.o_id)
if(idx > 1) then
-- swap the two positions
local tmp = dialog.n_dialogs[d_id].d_options[fields.o_id].o_sort
dialog.n_dialogs[d_id].d_options[fields.o_id].o_sort =
dialog.n_dialogs[d_id].d_options[sorted_o_list[idx - 1]].o_sort
dialog.n_dialogs[d_id].d_options[sorted_o_list[idx - 1]].o_sort = tmp
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": Option "..tostring(fields.o_id).." was moved up by one.")
end
-- ..or move the option down by one
elseif(fields[ "option_move_down"] and fields.o_id and d_options[fields.o_id]) then
local sorted_o_list = yl_speak_up.get_sorted_options(d_options, "o_sort")
local idx = table.indexof(sorted_o_list, fields.o_id)
if(idx > 0 and idx < #sorted_o_list) then
local tmp = dialog.n_dialogs[d_id].d_options[fields.o_id].o_sort
dialog.n_dialogs[d_id].d_options[fields.o_id].o_sort =
dialog.n_dialogs[d_id].d_options[sorted_o_list[idx + 1]].o_sort
dialog.n_dialogs[d_id].d_options[sorted_o_list[idx + 1]].o_sort = tmp
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": Option "..tostring(fields.o_id).." was moved down by one.")
end
end
-- ignore entries to o_sort if they are not a number
if(fields[ "edit_option_o_sort"]
and tonumber(fields[ "edit_option_o_sort"])
and fields.o_id and dialog.n_dialogs[d_id].d_options[fields.o_id]) then
local o_id = fields.o_id
local new_nr = tonumber(fields[ "edit_option_o_sort"])
local old_nr = tonumber(dialog.n_dialogs[d_id].d_options[o_id].o_sort)
-- if the nr is -1 (do not show) then we are done already: nothing to do
if(old_nr == new_nr) then
-- -1: do not list as option/answer (but still store and keep it)
elseif(new_nr == -1 and old_nr ~= -1) then
dialog.n_dialogs[d_id].d_options[o_id].o_sort = "-1"
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": Option "..tostring(o_id).." was set to -1 (do not list).")
else
-- get the old sorted list
local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs[d_id].d_options, "o_sort")
-- negative numbers are not shown
local entries_shown_list = {}
for i, o in ipairs(sorted_list) do
local n = tonumber(dialog.n_dialogs[d_id].d_options[o].o_sort)
if(n and n > 0 and o ~= o_id) then
table.insert(entries_shown_list, o)
end
end
-- insert the entry at the new position and let lua do the job
table.insert(entries_shown_list, new_nr, o_id)
-- take the indices from that new list as new sort values and store them;
-- this has the side effect that duplicate entries get sorted out as well
for i, o in ipairs(entries_shown_list) do
dialog.n_dialogs[d_id].d_options[o].o_sort = tostring(i)
end
-- store that there was a cahnge
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": Option "..tostring(o_id).." was moved to position "..
tostring(new_nr)..".")
end
end
-- changes to options are not possible if there are none
if(dialog.n_dialogs[ d_id ].d_options) then
-- detect changes to text_option_<o_id>: text for option <o_id>
for k, v in pairs(dialog.n_dialogs[ d_id ].d_options) do
if( fields[ "text_option_"..k ]
and fields[ "text_option_"..k ] ~= v.o_text_when_prerequisites_met ) then
-- store that there have been changes to this npc
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": The text for option "..tostring(k)..
" was changed from \""..tostring(v.o_text_when_prerequisites_met)..
"\" to \""..tostring(fields[ "text_option_"..k]).."\".")
-- actually change the text of the option
dialog.n_dialogs[ d_id ].d_options[ k ].o_text_when_prerequisites_met = fields[ "text_option_"..k ]
end
end
-- detect changes to d_id_<o_id>: target dialog for option <o_id>
for k, v in pairs(dialog.n_dialogs[ d_id ].d_options) do
if(fields[ "d_id_"..k ]) then
local new_target_dialog = yl_speak_up.prepare_new_dialog_for_option(
dialog, pname, n_id, d_id, k, fields[ "d_id_"..k ], v.o_results)
if(new_target_dialog ~= fields[ "d_id_"..k ]
and not( dialog.n_dialogs[new_target_dialog]
and dialog.n_dialogs[new_target_dialog].d_name
and dialog.n_dialogs[new_target_dialog].d_name == fields["d_id_"..k])
) then
fields[ "d_id_"..k ] = new_target_dialog
-- in options edit menu: show this update
result["show_next_option"] = k
end
end
end
end
-- add a new dialog; either via "+" button or "New dialog" in dialog dropdown menu
-- this has to be done after all the other changes because those (text changes etc.) still
-- apply to the *old* dialog
if(fields.show_new_dialog
or(fields["d_id"] and fields["d_id"] == yl_speak_up.text_new_dialog_id)) then
-- create the new dialog and make sure it gets shown
local d_id = yl_speak_up.add_new_dialog(dialog, pname, nil)
-- actually show the new dialog
fields["d_id"] = d_id
fields["show_new_dialog"] = nil
end
-- delete one empty dialog
if(fields.delete_this_empty_dialog) then
local anz_options = 0
-- we need to show a new dialog after this one was deleted
local new_dialog = d_id
local sorted_list = yl_speak_up.get_sorted_options(dialog.n_dialogs, "d_sort")
for i, k in ipairs(sorted_list) do
-- count the options of this dialog
if(k == d_id) then
if(dialog.n_dialogs[d_id].d_options) then
for o, w in pairs(dialog.n_dialogs[d_id].d_options) do
anz_options = anz_options + 1
end
end
if(sorted_list[i+1]) then
new_dialog = sorted_list[i+1]
elseif(sorted_list[i-1]) then
new_dialog = sorted_list[i-1]
end
end
end
-- there needs to be one dialog left after deleting this one,
-- (as there is always d_dynamic we need to leave *two* dialogs)
if(#sorted_list > 2
-- this dialog isn't allowed to hold any more options/answers
and anz_options == 0
-- we really found a new dialog to show
and new_dialog ~= d_id
-- and the text needs to be empty
and dialog.n_dialogs[ d_id ].d_text == "") then
-- actually delete this dialog
dialog.n_dialogs[ d_id ] = nil
-- ..and store it to disk
yl_speak_up.delete_dialog(n_id, d_id)
yl_speak_up.log_change(pname, n_id,
"Deleted dialog "..tostring(d_id)..".")
-- switch to another dialog (this one was deleted after all)
fields["d_id"] = new_dialog
fields["show_new_dialog"] = nil
else
-- deleting is only possible from the talk menu, and there the delete
-- button is only shown if the dialog can be deleted; so this can remain
-- a chat message
minetest.chat_send_player(pname, "Sorry. This dialog cannot be deleted (yet). "..
"It is either the only dialog left or has a non-empty text or has at "..
"least on remaining option/answer.")
end
end
-- not in options edit menu?
local o_id = fields.o_id
if(not(o_id)) then
return result
end
local d_option = dialog.n_dialogs[ d_id ].d_options[ o_id ]
-- change alternate text when preconditions are not met
-- (only happens in options edit menu)
if(fields.option_text_not_met and d_option
and d_option.o_text_when_prerequisites_not_met ~= fields.option_text_not_met) then
-- add change to changelog
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": The alternate text for option "..tostring(o_id)..
" was changed from \""..
tostring(d_option.o_text_when_prerequisites_not_met).."\" to \""..
tostring(fields.option_text_not_met).."\".")
-- actually change the text of the option
d_option.o_text_when_prerequisites_not_met = fields.option_text_not_met
end
-- toggle visit often/only *once*
if(d_option and fields.option_visits and fields.option_visits ~= "") then
local old_visit_mode = "often"
if(d_option.o_visit_only_once and d_option.o_visit_only_once == 1) then
old_visit_mode = "*once*"
end
if(fields.option_visits ~= old_visit_mode) then
if(fields.option_visits == "often") then
d_option.o_visit_only_once = 0
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": Option "..tostring(o_id)..
" can now be visited often/unlimited (default).")
elseif(fields.option_visits == "*once*") then
d_option.o_visit_only_once = 1
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": Option "..tostring(o_id)..
" can now be visited only *once* per talk.")
end
end
end
-- toggle autoselection/autoclick of an option
if(d_option and fields.option_autoanswer and fields.option_autoanswer ~= "") then
local old_answer_mode = "by clicking on it"
if(dialog.n_dialogs[ d_id ].o_random) then
old_answer_mode = "randomly"
elseif(d_option.o_autoanswer and d_option.o_autoanswer == 1) then
old_answer_mode = "automaticly"
end
if(fields.option_autoanswer ~= old_answer_mode) then
local new_answer_mode = ""
if(fields.option_autoanswer == "by clicking on it") then
d_option.o_autoanswer = nil
-- the dialog is no longer random
dialog.n_dialogs[ d_id ].o_random = nil
new_answer_mode = fields.option_autoanswer
elseif(fields.option_autoanswer == "automaticly") then
d_option.o_autoanswer = 1
-- the dialog is no longer random
dialog.n_dialogs[ d_id ].o_random = nil
new_answer_mode = fields.option_autoanswer
elseif(fields.option_autoanswer == "randomly") then
d_option.o_autoanswer = nil
-- the entire dialog needs to be set to randomly - not just this option
dialog.n_dialogs[ d_id ].o_random = 1
new_answer_mode = fields.option_autoanswer
end
if(new_answer_mode ~= "" and new_answer_mode ~= old_answer_mode) then
local random_was_changed = ""
if(new_answer_mode == "randomly" or old_answer_mode == "randomly") then
random_was_changed = " Note that changes to/from \"randomly\" "..
"affect the entire dialog!"
end
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": The modus for option "..tostring(o_id)..
" was changed from \""..old_answer_mode.."\" to \""..
new_answer_mode.."\"."..random_was_changed)
end
end
end
-- handle hide/grey out/show alternate answer
-- (only happens in options edit menu)
if(fields.hide_or_grey_or_alternate_answer and d_option) then
if(fields.hide_or_grey_or_alternate_answer == "..hide this answer."
and d_option.o_hide_when_prerequisites_not_met ~= "true") then
d_option.o_hide_when_prerequisites_not_met = "true"
d_option.o_grey_when_prerequisites_not_met = "false"
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": If precondition for option "..tostring(o_id)..
" is not met, hide option/answer.")
-- make sure we show this options update next
result["show_next_option"] = o_id
elseif(fields.hide_or_grey_or_alternate_answer == "..grey out the following answer:"
and d_option.o_grey_when_prerequisites_not_met ~= "true") then
d_option.o_hide_when_prerequisites_not_met = "false"
d_option.o_grey_when_prerequisites_not_met = "true"
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": If precondition for option "..tostring(o_id)..
" is not met, grey out option/answer.")
result["show_next_option"] = o_id
elseif(fields.hide_or_grey_or_alternate_answer == "..display the following alternate answer:"
and (d_option.o_hide_when_prerequisites_not_met ~= "false"
or d_option.o_grey_when_prerequisites_not_met) ~= "false") then
d_option.o_hide_when_prerequisites_not_met = "false"
d_option.o_grey_when_prerequisites_not_met = "false"
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": If precondition for option "..tostring(o_id)..
" is not met, show alternate option/answer.")
result["show_next_option"] = o_id
end
end
-- how many times can the player fail to execute the action successfully?
if(fields[ "timer_max_attempts_on_failure"]) then
local field_name = "timer_max_attempts_on_failure"
local timer_name = "timer_on_failure_"..tostring(d_id).."_"..tostring(o_id)
if(not(tonumber(fields[ field_name ]))) then
fields[ field_name ] = 0
end
-- make sure the variable exists
if(yl_speak_up.add_time_based_variable(timer_name)) then
yl_speak_up.set_variable_metadata(timer_name, nil, "parameter", "max_attempts",
fields[ field_name ])
end
end
-- ..and how long has the player to wait in order to try again?
if(fields[ "timer_max_seconds_on_failure"]) then
local field_name = "timer_max_seconds_on_failure"
local timer_name = "timer_on_failure_"..tostring(d_id).."_"..tostring(o_id)
if(not(tonumber(fields[ field_name ]))) then
fields[ field_name ] = 0
end
-- make sure the variable exists
if(yl_speak_up.add_time_based_variable(timer_name)) then
yl_speak_up.set_variable_metadata(timer_name, nil, "parameter", "duration",
fields[ field_name ])
end
end
if(fields[ "timer_max_seconds_on_success"]) then
local field_name = "timer_max_seconds_on_success"
local timer_name = "timer_on_success_"..tostring(d_id).."_"..tostring(o_id)
if(not(tonumber(fields[ field_name ]))) then
fields[ field_name ] = 0
end
-- make sure the variable exists
if(yl_speak_up.add_time_based_variable(timer_name)) then
yl_speak_up.set_variable_metadata(timer_name, nil, "parameter", "duration",
fields[ field_name ])
end
end
-- currently only contains result["show_new_option"] (which is needed for options edit menu)
return result
end
-- end of yl_speak_up.edit_mode_apply_changes

View File

@ -1,62 +0,0 @@
local old_action_inv_changed = yl_speak_up.action_inv_changed
-- monitor changes to the npc_gives and npc_wants slots (in particular when editing actions)
-- how: can be "put" or "take"
yl_speak_up.action_inv_changed = function(inv, listname, index, stack, player, how)
if(not(player)) then
return
end
local pname = player:get_player_name()
if(not(pname) or not(yl_speak_up.speak_to[pname])) then
return
end
local n_id = yl_speak_up.speak_to[pname].n_id
-- if not in edit mode: the player may just be normally interacting with the NPC;
-- nothing to do for us here (wait for the player to click on "save")
if(not(n_id) or yl_speak_up.edit_mode[pname] ~= n_id) then
return old_action_inv_changed(inv, listname, index, stack, player, how)
end
-- is the player in the process of editing an action of the npc_gives/npc_wants type?
local target_fs = "edit_actions"
local data = yl_speak_up.speak_to[pname][ "tmp_action" ]
if(not(data) or (data.what ~= 4 and data.what ~= 5)) then
-- we are editing an action
if(data) then
return
end
-- it might be a precondition
data = yl_speak_up.speak_to[pname][ "tmp_prereq" ]
if(not(data) or (data.what ~= 8)) then
return
end
target_fs = "edit_preconditions"
end
-- "The NPC gives something to the player (i.e. a quest item).", -- 4
-- "The player is expected to give something to the NPC (i.e. a quest item).", -- 5
if(how == "put") then
data.item_node_name = stack:get_name().." "..stack:get_count()
local meta = stack:get_meta()
if(meta and meta:get_string("description")) then
-- try to reconstruct $PLAYER_NAME$ (may not always work)
local item_was_for = meta:get_string("yl_speak_up:quest_item_for")
local new_desc = meta:get_string("description")
if(item_was_for and item_was_for ~= "") then
new_desc = string.gsub(new_desc, item_was_for, "$PLAYER_NAME$")
end
data.item_desc = new_desc
end
if(meta and meta:get_string("yl_speak_up:quest_id")) then
data.item_quest_id = meta:get_string("yl_speak_up:quest_id")
end
elseif(how == "take" and data.what == 4) then
data.item_desc = "- no item set -"
data.item_node_name = ""
elseif(how == "take" and data.what == 5) then
data.item_desc = "- no item set -"
data.item_node_name = ""
end
-- show the updated formspec to the player
yl_speak_up.show_fs(player, target_fs, nil)
-- no need to check anything more here; the real checks need to be done
-- when the player presses the save/store/execute button
end

View File

@ -1,9 +0,0 @@
local old_execute_all_relevant_effects = yl_speak_up.execute_all_relevant_effects
yl_speak_up.execute_all_relevant_effects = function(player, effects, o_id, action_was_successful, d_option,
dry_run_no_exec) -- dry_run_no_exec for edit_mode
-- if in edit mode: do a dry run - do *not* execute the effects
local edit_mode = (player and yl_speak_up.in_edit_mode(player:get_player_name()))
-- we pass this as an additional parameter so that it doesn't have to be re-evaluated for each effect
return old_execute_all_relevant_effects(player, effects, o_id, action_was_successful, d_option, edit_mode)
end

View File

@ -1,763 +0,0 @@
-- helper function to make unintresting zeros in tables less visible
local grey_if_zero = function(fs, n)
if(n and n == 0) then
table.insert(fs, "#444444")
else
table.insert(fs, "#FFFFFF")
end
table.insert(fs, minetest.formspec_escape(n))
end
-- small helper function
local show_error_fs = function(player, text)
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:add_quest_steps",
formspec = yl_speak_up.build_fs_quest_edit_error(text, "back_from_error_msg")})
end
-- find out in how many quest steps this NPC or location is used;
-- ID can either be n_<ID> or a location p_(x,y,z)
yl_speak_up.count_used_in_quest_steps = function(id, step_data)
local used = 0
for s, d in pairs(step_data or {}) do
for loc_id, loc in pairs(d.where or {}) do
if(loc and loc.n_id and loc.n_id == id) then
used = used + 1
end
end
end
return used
end
-- This order imposed here on the quest steps is the one in which the
-- quest steps have to be solved - as far as we can tell (the quest
-- may be in the process of beeing created and not logicly complete yet).
yl_speak_up.get_sorted_quest_step_list_by_prev_step = function(pname, q_id)
if(not(q_id) or not(yl_speak_up.quests[q_id]) or not(yl_speak_up.quests[q_id].step_data)) then
return {}
end
-- for now: sort alphabeticly
local step_data = yl_speak_up.quests[q_id].step_data
local liste = {}
for k, v in pairs(step_data) do
table.insert(liste, k)
end
table.sort(liste)
return liste
end
--[[ still belongs to the above function
-- add back links (or rather: forward links to quest steps that get enabled)
local connected = table.copy(step_data)
for k, v in pairs(connected) do
-- will store the inverse data
connected[k].inv_one_step_required = {}
connected[k].inv_all_steps_require = {}
end
for k, v in pairs(connected) do
if(k and v and v.one_step_required) then
for i, s in ipairs(v.one_step_required) do
table.insert(connected[s].inv_one_step_required, k)
end
end
if(k and v and v.all_steps_required) then
for i, s in ipairs(v.all_steps_required) do
table.insert(connected[s].inv_all_steps_required, k)
end
end
end
-- get those quest steps that are not connected and not used yet
local liste = {}
for k, v in pairs(connected) do
if(k and v
and #v.one_step_required == 0 and #v.all_steps_required == 0
and #v.inv_one_step_required == 0 and #v.inv_all_steps_required == 0) then
table.insert(liste, k)
end
end
-- sort alphabeticly
table.sort(liste)
-- remove those entries from our connection table (they're not connected anyway);
-- we have already added them to the beginning of the list
for i, v in ipairs(liste) do
connected[v] = nil
end
return liste
end
--]]
-- helper function: find out if a quest step is required by other quest steps
yl_speak_up.quest_step_get_required_for_steps = function(step_data)
local required_for_steps = {}
for s, d in pairs(step_data) do
required_for_steps[s] = {}
end
for s, d in pairs(step_data) do
if(s and d and d.one_step_required and type(d.one_step_required) == "table") then
for i, s2 in ipairs(d.one_step_required) do
table.insert(required_for_steps[s2], s)
end
end
if(s and d and d.all_steps_required and type(d.all_steps_required) == "table") then
for i, s2 in ipairs(d.all_steps_required) do
table.insert(required_for_steps[s2], s)
end
end
end
return required_for_steps
end
yl_speak_up.input_fs_add_quest_steps = function(player, formname, fields)
if(not(fields) or not(player)) then
return
end
local res = yl_speak_up.player_is_working_on_quest(player)
if(res.error_msg) then
return show_error_fs(player, res.error_msg)
end
local pname = res.pname
local q_id = res.q_id
local current_step = res.current_step
local step_data = res.step_data
local quest = res.quest
if(fields.back_from_error_msg) then
yl_speak_up.show_fs(player, "add_quest_steps")
return
end
local mode = yl_speak_up.speak_to[pname].quest_step_mode
if(fields.back) then
-- go back to quest overview
if(mode and (mode == "manage_quest_npcs" or mode == "manage_quest_locations")) then
return yl_speak_up.show_fs(player, "manage_quests")
end
return yl_speak_up.show_fs(player, "manage_quest_steps", current_step)
end
-- has a quest step be selected?
local work_step = nil
if(fields.add_element and fields.add_element_name) then
if( mode and mode == "manage_quest_npcs") then
-- manually entered an NPC ID
local npc_id = fields.add_element_name or ""
-- just check if it is *potentially* an NPC ID; that way NPC the quest
-- creator has no write access to can be added
if(string.sub(npc_id, 1, 2) ~= "n_"
or not(tonumber(string.sub(npc_id, 3)))) then
return show_error_fs(player, "This is not an NPC ID. They have the form n_<id>.")
end
-- only npcs that are not yet added (and we store IDs without n_ prefix)
local id = tonumber(string.sub(npc_id, 3))
if(id and table.indexof(res.quest.npcs or {}, id) == -1) then
table.insert(yl_speak_up.quests[q_id].npcs, id)
yl_speak_up.save_quest(q_id)
end
return yl_speak_up.show_fs(player, "add_quest_steps")
elseif(mode and mode == "manage_quest_locations") then
-- manually entered a quest location
local location_id = fields.add_element_name or ""
local d = yl_speak_up.player_vars["$NPC_META_DATA$"][location_id]
local error_msg = ""
-- the owner is not checked; that way, locations can be added where the
-- quest onwer does not (yet) have write access
if(string.sub(location_id, 1, 1) ~= "p") then
error_msg = "This is not a location ID."
elseif(not(d)) then
error_msg = "Location not found."
end
if(error_msg ~= "") then
return show_error_fs(player, error_msg)
end
-- only locations that are not yet added
if(table.indexof(res.quest.locations or {}, location_id) == -1) then
table.insert(yl_speak_up.quests[q_id].locations, location_id)
yl_speak_up.save_quest(q_id)
end
return yl_speak_up.show_fs(player, "add_quest_steps")
end
-- create a new quest step
local new_step = fields.add_element_name:trim()
-- a new one shall be created
local msg = yl_speak_up.quest_step_add_quest_step(pname, q_id, new_step)
if(msg ~= "OK") then
return show_error_fs(player, msg)
end
-- this will also be set if the quest step exists already; this is fine so far
work_step = new_step
elseif(fields.add_from_available
and yl_speak_up.speak_to[pname].list_available) then
-- selected a quest step from the list of available steps offered
local liste = yl_speak_up.speak_to[pname].list_available
local selected = minetest.explode_table_event(fields.add_from_available)
if(selected and selected.row and selected.row > 1 and selected.row <= #liste + 1) then
work_step = liste[selected.row - 1]
end
elseif(fields.add_to_npc_list
and yl_speak_up.speak_to[pname].list_available) then
-- selected an NPC from the list of available NPC offered
local liste = yl_speak_up.speak_to[pname].list_available
local selected = minetest.explode_table_event(fields.add_to_npc_list)
if(selected and selected.row and selected.row > 1 and selected.row <= #liste + 1) then
local npc_id = liste[selected.row - 1]
if(table.indexof(res.quest.npcs or {}, npc_id) == -1) then
table.insert(yl_speak_up.quests[q_id].npcs, npc_id)
yl_speak_up.save_quest(q_id)
end
end
return yl_speak_up.show_fs(player, "add_quest_steps")
elseif(fields.add_to_location_list
and yl_speak_up.speak_to[pname].list_available) then
-- selected a location from the list of available locations offered
local liste = yl_speak_up.speak_to[pname].list_available
local selected = minetest.explode_table_event(fields.add_to_location_list)
if(selected and selected.row and selected.row > 1 and selected.row <= #liste + 1) then
local location_id = liste[selected.row - 1]
if(table.indexof(res.quest.locations or {}, location_id) == -1) then
table.insert(yl_speak_up.quests[q_id].locations, location_id)
yl_speak_up.save_quest(q_id)
end
end
return yl_speak_up.show_fs(player, "add_quest_steps")
elseif(fields.delete_from_one_step_required and current_step and step_data[current_step]) then
-- remove a quest step from the list (from one step required)
local selected = minetest.explode_table_event(fields.delete_from_one_step_required)
local liste = (step_data[current_step].one_step_required or {})
if(selected and selected.row and selected.row > 1 and selected.row <= #liste + 1) then
table.remove(yl_speak_up.quests[q_id].step_data[current_step].one_step_required, selected.row-1)
end
yl_speak_up.save_quest(q_id)
return yl_speak_up.show_fs(player, "add_quest_steps")
elseif(fields.delete_from_all_steps_required and current_step and step_data[current_step]) then
-- remove a quest step from the lists (from all steps required)
local selected = minetest.explode_table_event(fields.delete_from_all_steps_required)
local liste = (step_data[current_step].all_steps_required or {})
if(selected and selected.row and selected.row > 1 and selected.row <= #liste + 1) then
table.remove(yl_speak_up.quests[q_id].step_data[current_step].all_steps_required, selected.row-1)
end
yl_speak_up.save_quest(q_id)
return yl_speak_up.show_fs(player, "add_quest_steps")
elseif(fields.delete_from_npc_list) then
-- remove an NPC from the list of contributors
local selected = minetest.explode_table_event(fields.delete_from_npc_list)
local liste = (res.quest.npcs or {})
if(selected and selected.row and selected.row > 1 and selected.row <= #liste + 1) then
-- *can* it be removed, or is it needed somewhere?
local full_id = "n_"..tostring(liste[selected.row - 1])
if(yl_speak_up.count_used_in_quest_steps(full_id, step_data) > 0) then
return show_error_fs(player, "This NPC is needed for setting a quest step.")
end
table.remove(yl_speak_up.quests[q_id].npcs, selected.row - 1)
end
yl_speak_up.save_quest(q_id)
return yl_speak_up.show_fs(player, "add_quest_steps")
elseif(fields.delete_from_location_list) then
-- remove a location from the list of contributors
local selected = minetest.explode_table_event(fields.delete_from_location_list)
local liste = (res.quest.locations or {})
if(selected and selected.row and selected.row > 1 and selected.row <= #liste + 1) then
-- *can* it be removed, or is it needed somewhere?
local full_id = liste[selected.row - 1]
if(yl_speak_up.count_used_in_quest_steps(full_id, step_data) > 0) then
return show_error_fs(player, "This location is needed for setting a quest step.")
end
table.remove(yl_speak_up.quests[q_id].locations, selected.row - 1)
end
yl_speak_up.save_quest(q_id)
return yl_speak_up.show_fs(player, "add_quest_steps")
end
if(not(work_step)) then
return -- TODO
elseif(mode == "embedded_select") then
yl_speak_up.speak_to[pname].quest_step = work_step
return yl_speak_up.show_fs(player, "manage_quest_steps", work_step)
elseif(mode == "assign_quest_step") then
-- TODO: what if there's already a step assigned?
-- actually add the step
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
-- this saves the quest data as well if needed
local msg = yl_speak_up.quest_step_add_where(pname, q_id, work_step,
{n_id = n_id, d_id = d_id, o_id = o_id})
if(msg ~= "OK") then
return show_error_fs(player, msg)
end
if(not(n_id)) then
return show_error_fs(player, "NPC or location not found.")
end
-- store the new connection in the NPC file itself (do not load generic dialogs)
local dialog = yl_speak_up.load_dialog(n_id, false)
if(yl_speak_up.check_if_dialog_has_option(dialog, d_id, o_id)) then
-- ok - the tables exist, so we can store the connection
dialog.n_dialogs[d_id].d_options[o_id].quest_id = quest.var_name
dialog.n_dialogs[d_id].d_options[o_id].quest_step = work_step
-- write it back to disc
yl_speak_up.save_dialog(n_id, dialog)
else
return show_error_fs(player, "Failed to save this quest step for this NPC.")
end
-- the player is working on the NPC - thus, the NPC may be in a modified stage
-- that hasn't been written to disc yet, and we need to adjust this stage as well
dialog = yl_speak_up.speak_to[pname].dialog
if(yl_speak_up.check_if_dialog_has_option(dialog, d_id, o_id)) then
-- ok - the tables exist, so we can store the connection
dialog.n_dialogs[d_id].d_options[o_id].quest_id = quest.var_name
dialog.n_dialogs[d_id].d_options[o_id].quest_step = work_step
yl_speak_up.speak_to[pname].dialog = dialog
else
return show_error_fs(player, "Failed to update NPC.")
end
-- show the newly created or selected step
yl_speak_up.speak_to[pname].quest_step = work_step
-- log the change
yl_speak_up.log_change(pname, n_id,
"Dialog "..tostring(d_id)..": Option "..tostring(o_id)..
" will now set quest step \""..
tostring(dialog.n_dialogs[d_id].d_options[o_id].quest_step)..
"\" of quest \""..
tostring(dialog.n_dialogs[d_id].d_options[o_id].quest_id).."\".")
return yl_speak_up.show_fs(player, "manage_quest_steps", work_step)
elseif(not(current_step) or not(step_data[current_step])) then
return yl_speak_up.show_fs(player, "manage_quests")
end
local required_for_steps = yl_speak_up.quest_step_get_required_for_steps(step_data)
-- make sure we have a sane data structure
for i, s in ipairs({current_step, work_step}) do
if(s and yl_speak_up.quests[q_id].step_data[s]) then
if(not(yl_speak_up.quests[q_id].step_data[s].one_step_required)) then
yl_speak_up.quests[q_id].step_data[s].one_step_required = {}
end
if(not(yl_speak_up.quests[q_id].step_data[s].all_steps_required)) then
yl_speak_up.quests[q_id].step_data[s].all_steps_required = {}
end
end
end
-- actually do the work
if(mode == "add_to_one_needed") then
table.insert(yl_speak_up.quests[q_id].step_data[current_step].one_step_required, work_step)
elseif(mode == "add_to_all_needed") then
table.insert(yl_speak_up.quests[q_id].step_data[current_step].all_steps_required, work_step)
elseif(mode == "insert_after_prev_step") then
-- the work_step requires what the current step used to require
if(#step_data[current_step].one_step_required == 1) then
-- a clear insert is possible
yl_speak_up.quests[q_id].step_data[work_step].one_step_required = {
step_data[current_step].one_step_required[1]}
yl_speak_up.quests[q_id].step_data[current_step].one_step_required[1] = work_step
else
-- no useful information on what the new work_step ought to depend on;
-- we just insert the new step at the first place
table.insert(yl_speak_up.quests[q_id].step_data[current_step].one_step_required,
1, work_step)
end
return yl_speak_up.show_fs(player, "manage_quest_steps", work_step)
elseif(mode == "insert_before_next_step") then
-- the work_step requires the current_step
table.insert(yl_speak_up.quests[q_id].step_data[work_step].one_step_required, 1, current_step)
-- the current step has exactly one successor? then we adjust that one
if(#required_for_steps[current_step] == 1) then
local next_step = required_for_steps[current_step][1]
local i = table.indexof(step_data[next_step].one_step_required, current_step)
local a = table.indexof(step_data[next_step].all_steps_required, current_step)
if(i > -1) then
-- is it in one_step_required? -> replace current_step with work_step
yl_speak_up.quests[q_id].step_data[next_step].one_step_required[i] = work_step
elseif(a > -1) then
-- or in all_steps_required? -> replace current_step with work_step
yl_speak_up.quests[q_id].step_data[next_step].all_steps_required[i] =work_step
end
end
return yl_speak_up.show_fs(player, "manage_quest_steps", work_step)
end
yl_speak_up.save_quest(q_id)
return yl_speak_up.show_fs(player, "add_quest_steps")
end
-- small helper function for yl_speak_up.get_fs_add_quest_steps;
-- lists all the quest steps found in liste in the order they occour there
yl_speak_up.quest_step_list_show_table = function(formspec, table_specs, liste, data, required_for_steps)
table.insert(formspec, "tablecolumns["..
"color;text,align=right;".. -- #d.one_step_required
"color;text,align=right;".. -- #d.all_steps_required
"color;text,align=right;".. -- #required_for_steps (quest steps that need this one)
"color;text,align=right;".. -- #where (locations/NPC that *set* this quest step)
"color;text,align=left".. -- name of quest step
"]table[")
table.insert(formspec, table_specs)
table.insert(formspec,"#FFFFFF,(O),#FFFFFF,(A),#FFFFFF,(U),#FFFFFF,(L),#FFFFFF,Name of step:,")
local tmp = {}
for i, s in ipairs(liste or {}) do
local d = data[s]
if(not(d.one_step_required) or type(d.one_step_required) ~= "table") then
d.one_step_required = {}
end
grey_if_zero(tmp, #d.one_step_required)
if(not(d.all_steps_required) or type(d.all_steps_required) ~= "table") then
d.all_steps_required = {}
end
grey_if_zero(tmp, #d.all_steps_required)
if(not(required_for_steps[s])) then
required_for_steps[s] = {}
end
grey_if_zero(tmp, #required_for_steps[s])
if(not(d.where) or type(d.where) ~= "table") then
d.where = {}
end
local anz_where = 0
for k, v in pairs(d.where) do
anz_where = anz_where + 1
end
grey_if_zero(tmp, anz_where)
table.insert(tmp, "#AAFFAA")
table.insert(tmp, minetest.formspec_escape(s))
end
table.insert(formspec, table.concat(tmp, ","))
table.insert(formspec, ";]")
end
-- returns list of NPCs that pname can edit and that are not yet part of quest_npc_list
yl_speak_up.quest_get_npc_candidate_list = function(pname, quest_npc_liste)
-- build a list of candidates
local npc_list = {}
for k, v in pairs(yl_speak_up.npc_list) do
-- only NPC that are not already added
if(table.indexof(quest_npc_liste or {}, k) == -1
-- and only those that the player can edit
and (v.owner == pname or (v.may_edit and v.may_edit[pname]))) then
table.insert(npc_list, k)
end
end
table.sort(npc_list)
return npc_list
end
-- lists npc that are either already added or could be added
-- can also handle locations
yl_speak_up.quest_npc_show_table = function(formspec, table_specs, liste, step_data, is_location_list)
table.insert(formspec, "tablecolumns["..
"color;text,align=right;".. -- used in this many quest steps
"color;text,align=left;".. -- n_id (number, for NPC) or p_(-185,3,-146) (for locations)
"color;text,align=left;".. -- owner
"color;text,align=left".. -- name of NPC
"]table[")
table.insert(formspec, table_specs)
if(is_location_list) then
table.insert(formspec,"#FFFFFF,Used:,#FFFFFF,PositionID:,#FFFFFF,Name")
else
table.insert(formspec,"#FFFFFF,Used:,#FFFFFF,n_id:,#FFFFFF,Name")
end
table.insert(formspec, minetest.formspec_escape(","))
table.insert(formspec, " description:,#FFFFFF,Owner:,")
local tmp = {}
for i, n_id in ipairs(liste or {}) do
local full_id = n_id
if(not(is_location_list)) then
full_id = "n_"..tostring(n_id)
end
grey_if_zero(tmp, yl_speak_up.count_used_in_quest_steps(full_id, step_data))
-- the n_id of the NPC
table.insert(tmp, "#AAFFAA")
if(is_location_list) then
-- this already encodes the position but contains , and ()
table.insert(tmp, minetest.formspec_escape(n_id))
else
table.insert(tmp, "n_"..minetest.formspec_escape(n_id))
end
-- get information from the NPC list (see fs_npc_list.lua)
local owner = "- ? -"
local name = "- ? -"
if(yl_speak_up.npc_list[n_id]) then
local npc = yl_speak_up.npc_list[n_id]
owner = npc.owner
name = (npc.name or name)
if(npc.desc and npc.desc ~= "") then
name = name..', '..(npc.desc or "")
end
end
-- name and description of the NPC
table.insert(tmp, "#AAFFAA")
table.insert(tmp, minetest.formspec_escape(name))
-- owner of the NPC
table.insert(tmp, "#AAFFAA")
table.insert(tmp, minetest.formspec_escape(owner))
end
table.insert(formspec, table.concat(tmp, ","))
table.insert(formspec, ";]")
end
-- returns list of locations that pname can edit and that are not yet part of quest_location_list
yl_speak_up.quest_get_location_candidate_list = function(pname, quest_location_liste)
-- build a list of candidates of locations
local location_list = {}
for n_id, v in pairs(yl_speak_up.player_vars["$NPC_META_DATA$"] or {}) do
-- TODO: better detection would be helpful
if(string.sub(n_id, 1, 1) == "p"
-- only locations that are not yet added
and table.indexof(quest_location_liste or {}, n_id) == -1
-- and only those that the player can edit
and (v.owner == pname or (v.may_edit and v.may_edit[pname]))) then
table.insert(location_list, n_id)
end
end
table.sort(location_list)
return location_list
end
-- param is unused
yl_speak_up.get_fs_add_quest_steps = function(player, param)
local res = yl_speak_up.player_is_working_on_quest(player)
if(res.error_msg) then
return yl_speak_up.build_fs_quest_edit_error(res.error_msg, "back")
end
local pname = res.pname
local step_data = res.step_data or {}
-- find out if a quest step is required by other quest steps
local required_for_steps = yl_speak_up.quest_step_get_required_for_steps(step_data)
local current_step = nil
local this_step_data = nil
if(pname and yl_speak_up.speak_to[pname] and yl_speak_up.speak_to[pname].quest_step) then
current_step = yl_speak_up.speak_to[pname].quest_step
this_step_data = step_data[current_step]
end
local mode = ""
if(param) then
mode = param
yl_speak_up.speak_to[pname].quest_step_mode = param
elseif(pname and yl_speak_up.speak_to[pname] and yl_speak_up.speak_to[pname].quest_step_mode) then
mode = yl_speak_up.speak_to[pname].quest_step_mode
end
local add_what = "Add a new quest step named:"
if(mode == "manage_quest_npcs") then
add_what = "Add the NPC with n_<id>:"
elseif(mode == "manage_quest_locations") then
add_what = "Add a location by entering its ID directly:"
end
local formspec = {}
if(mode and mode == "embedded_select") then
table.insert(formspec, "size[30,12]container[6,0;18.5,12]")
current_step = nil
else
table.insert(formspec, "size[18.5,17.3]")
end
-- add back button
table.insert(formspec, "button[8,0;2,0.7;back;Back]")
-- show which quest we're working at
table.insert(formspec, "label[0.2,1.0;Quest ID:]")
table.insert(formspec, "label[3.0,1.0;")
table.insert(formspec, minetest.formspec_escape(res.q_id))
table.insert(formspec, "]")
table.insert(formspec, "label[0.2,1.5;Quest name:]")
table.insert(formspec, "label[3.0,1.5;")
table.insert(formspec, minetest.formspec_escape(res.quest.name or "- unknown -"))
table.insert(formspec, "]")
-- add new quest step
table.insert(formspec, "label[0.2,2.2;")
table.insert(formspec, add_what)
table.insert(formspec, "]")
table.insert(formspec, "button[16.1,2.4;1.2,0.7;add_element;Add]")
table.insert(formspec, "field[1.0,2.4;15,0.7;add_element_name;;]")
local y_pos = 3.3
if(current_step and mode == "insert_after_prev_step") then
local prev_step = "-"
if(this_step_data and this_step_data.one_step_required and #this_step_data.one_step_required > 0) then
prev_step = this_step_data.one_step_required[1]
end
table.insert(formspec, "label[0.2,3.3;between the previous step:]")
table.insert(formspec, "label[1.0,3.7;")
table.insert(formspec, minetest.colorize("#AAFFAA", minetest.formspec_escape(prev_step)))
table.insert(formspec, "]")
table.insert(formspec, "label[0.2,4.1;and the currently selected step:]")
table.insert(formspec, "label[1.0,4.5;")
table.insert(formspec, minetest.colorize("#FFFF00", minetest.formspec_escape(current_step)))
table.insert(formspec, "]")
y_pos = 5.3
elseif(current_step and mode == "insert_before_next_step") then
local next_step = "-"
if(current_step and required_for_steps[current_step] and #required_for_steps[current_step] > 0) then
next_step = required_for_steps[current_step][1]
end
table.insert(formspec, "label[0.2,3.3;between the currently selected step:]")
table.insert(formspec, "label[1.0,3.7;")
table.insert(formspec, minetest.colorize("#FFFF00", minetest.formspec_escape(current_step)))
table.insert(formspec, "]")
table.insert(formspec, "label[0.2,4.1;and the next step:]")
table.insert(formspec, "label[1.0,4.5;")
table.insert(formspec, minetest.colorize("#AAFFAA", minetest.formspec_escape(next_step)))
table.insert(formspec, "]")
y_pos = 5.3
elseif(current_step and mode == "add_to_one_needed") then
table.insert(formspec, "label[0.2,3.3;as a requirement to the currently selected step:]")
table.insert(formspec, "label[1.0,3.7;")
table.insert(formspec, minetest.colorize("#FFFF00", minetest.formspec_escape(current_step)))
table.insert(formspec, "]")
table.insert(formspec, "label[0.2,4.1;so that "..
minetest.colorize("#9999FF", "at least one")..
" of these requirements is fulfilled:]")
yl_speak_up.quest_step_list_show_table(formspec,
"0.2,4.3;17.0,3.0;delete_from_one_step_required;",
step_data[current_step].one_step_required,
step_data, required_for_steps)
table.insert(formspec, "label[0.2,7.5;(Click on an entry to delete it from the list above.)]")
y_pos = 8.3
elseif(current_step and mode == "add_to_all_needed") then
table.insert(formspec, "label[0.2,3.3;as a requirement to the currently selected step:]")
table.insert(formspec, "label[1.0,3.7;")
table.insert(formspec, minetest.colorize("#FFFF00", minetest.formspec_escape(current_step)))
table.insert(formspec, "]")
table.insert(formspec, "label[0.2,4.1;so that "..
minetest.colorize("#9999FF", "all")..
" of these requirements are fulfilled:]")
yl_speak_up.quest_step_list_show_table(formspec,
"0.2,4.3;17.0,3.0;delete_from_all_steps_required;",
step_data[current_step].all_steps_required,
step_data, required_for_steps)
table.insert(formspec, "label[0.2,7.5;(Click on an entry to delete it from the list above.)]")
y_pos = 8.3
-- add a quest step to an NPC or location
elseif(mode == "assign_quest_step") then
table.insert(formspec, "container[0,3.3;17,4]")
-- what are we talking about?
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
-- describe where in the dialog of the NPC or location this quest step shall be set
yl_speak_up.quest_step_show_where_set(pname, formspec, "which will be set by ", n_id, d_id, o_id, nil, nil)
table.insert(formspec, "container_end[]")
y_pos = 7.8
-- which NPC may contribute to the quest?
elseif(mode == "manage_quest_npcs") then
table.insert(formspec, "container[0,3.3;18,6]")
table.insert(formspec, "label[0.2,0;so that the NPC "..
minetest.colorize("#9999FF", "may contribute")..
" to the quest like these NPC:]")
yl_speak_up.quest_npc_show_table(formspec,
"0.2,0.2;17.0,3.0;delete_from_npc_list;",
res.quest.npcs or {},
step_data, false)
table.insert(formspec, "label[0.2,3.4;(Click on an entry to delete it from the list above.)]")
local available_npcs = yl_speak_up.quest_get_npc_candidate_list(pname, res.quest.npcs or {})
yl_speak_up.speak_to[pname].list_available = available_npcs
table.insert(formspec, "label[0.2,4.4;or select an NPC from the list below:]")
yl_speak_up.quest_npc_show_table(formspec,
"0.2,4.6;17.0,6.0;add_to_npc_list;",
available_npcs or {}, step_data, false)
table.insert(formspec, "label[0.2,10.8;Used: Shows in how many quest steps this NPC is used.]")
table.insert(formspec, "container_end[]")
return table.concat(formspec, "")
-- which locations may contribute to the quest?
elseif(mode == "manage_quest_locations") then
table.insert(formspec, "container[0,3.3;18,6]")
table.insert(formspec, "label[0.2,0;so that the location "..
minetest.colorize("#9999FF", "may contribute")..
" to the quest like these locations:]")
yl_speak_up.quest_npc_show_table(formspec,
"0.2,0.2;17.0,3.0;delete_from_location_list;",
res.quest.locations or {},
step_data, true)
table.insert(formspec, "label[0.2,3.4;(Click on an entry to delete it from the list above.)]")
local available_locations = yl_speak_up.quest_get_location_candidate_list(pname, res.quest.locations or {})
yl_speak_up.speak_to[pname].list_available = available_locations
table.insert(formspec, "label[0.2,4.4;or select a location from the list below:]")
yl_speak_up.quest_npc_show_table(formspec,
"0.2,4.6;17.0,6.0;add_to_location_list;",
available_locations or {}, step_data, true)
table.insert(formspec, "label[0.2,10.8;Used: Shows in how many quest steps this location is used.]")
table.insert(formspec, "container_end[]")
return table.concat(formspec, "")
end
-- some quest steps may not be available/may not make sense
local not_available = {}
if(current_step and step_data[current_step] and (not(mode) or mode ~= "assign_quest_step")) then
-- steps that are already required
for i, s in ipairs(step_data[current_step].one_step_required or {}) do
not_available[s] = true
end
for i, s in ipairs(step_data[current_step].all_steps_required or {}) do
not_available[s] = true
end
-- steps that directly require this quest step here
for i, s in ipairs(required_for_steps[current_step] or {}) do
not_available[s] = true
end
end
if(current_step and (not(mode) or mode ~= "assign_quest_step")) then
not_available[current_step] = true
end
-- build a list of candidates
local available_steps = {}
for k, v in pairs(step_data) do
if(not(not_available[k])) then
table.insert(available_steps, k)
end
end
table.sort(available_steps)
yl_speak_up.speak_to[pname].list_available = available_steps
table.insert(formspec, "container[0,")
table.insert(formspec, tostring(y_pos))
table.insert(formspec, ";30,20]")
table.insert(formspec, "label[0.2,0;or select an existing quest step from the list below")
if(mode and mode == "embedded_select") then
table.insert(formspec, minetest.colorize("#9999FF", " to display the step")..":]")
else
table.insert(formspec, ":]")
end
yl_speak_up.quest_step_list_show_table(formspec,
"0.2,0.2;17,6.0;add_from_available;",
available_steps,
step_data, required_for_steps)
table.insert(formspec, "label[0.2,6.5;Legend: The numbers show the amount of quest steps...\n"..
"\t(O) from which (o)ne needs to be achieved for this quest step\n"..
"\t(A) that (a)ll need to be achieved for this quest step\n"..
"\t(U) that require/(u)se this quest step in some form\n"..
"\t(L) Number of locations (npc/places) that "..
minetest.colorize("#9999FF", "set").." this quest step]")
table.insert(formspec, "container_end[]")
return table.concat(formspec, "")
end
yl_speak_up.register_fs("add_quest_steps",
yl_speak_up.input_fs_add_quest_steps,
-- param is unused here
yl_speak_up.get_fs_add_quest_steps,
-- no special formspec version required
nil
)

View File

@ -1,31 +0,0 @@
-- override fs/fs_add_trade_simple.lua:
-- (this is kept here as it is trade related and does not change the formspec as such)
local old_input_add_trade_simple = yl_speak_up.input_add_trade_simple
yl_speak_up.input_add_trade_simple = function(player, formname, fields, input_to)
if(not(player)) then
return 0
end
local pname = player:get_player_name()
input_to = "add_trade_simple"
-- are we editing an action of the type trade?
if( yl_speak_up.speak_to[pname][ "tmp_action" ]
and yl_speak_up.speak_to[pname][ "tmp_action" ].what == 3
and yl_speak_up.in_edit_mode(pname)
and yl_speak_up.edit_mode[pname] == n_id) then
input_to = "edit_actions"
end
return old_input_add_trade_simple(player, formname, fields, input_to)
end
yl_speak_up.register_fs("add_trade_simple",
-- the input function is a new one now
yl_speak_up.input_add_trade_simple,
-- the get_fs function stays the same
yl_speak_up.get_fs_add_trade_simple_wrapper,
-- force formspec version 1 (not changed):
1
)

View File

@ -1,343 +0,0 @@
-- assign a quest step to a dialog option/answe
-- This is the formspec where this is handled.
-- small helper function
local show_error_fs = function(player, text)
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:assign_quest_step",
formspec = yl_speak_up.build_fs_quest_edit_error(text, "back_from_error_msg")})
end
-- small helper function
yl_speak_up.get_choose_until_step_list = function(pname, q_id, quest_step)
local choose_until_step = {
" - the player restarts the quest -",
" - this quest step -",
" - the quest step immediately following this one -"
}
local cdata = yl_speak_up.quests[q_id].step_data[quest_step]
for step_name, d in pairs(yl_speak_up.quests[q_id].step_data or {}) do
if(step_name ~= quest_step
and cdata
-- exclude steps that quest_step depends on
and table.indexof(cdata.one_step_required or {}, step_name) == -1
and table.indexof(cdata.all_steps_required or {}, step_name) == -1) then
table.insert(choose_until_step, minetest.formspec_escape(step_name))
end
end
return choose_until_step
end
yl_speak_up.input_fs_assign_quest_step = function(player, formname, fields)
if(not(player)) then
return ""
end
local pname = player:get_player_name()
-- what are we talking about?
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
if(not(n_id) or yl_speak_up.edit_mode[pname] ~= n_id) then
return
end
local dialog = yl_speak_up.speak_to[pname].dialog
if(not(yl_speak_up.check_if_dialog_has_option(dialog, d_id, o_id))) then
return
end
-- go back to edit options field
if((fields and fields.quit)
or (fields and fields.back and fields.back ~= "")) then
yl_speak_up.show_fs(player, "edit_option_dialog",
{n_id = n_id, d_id = d_id, o_id = o_id})
return
elseif(fields and fields.back_from_error_msg) then
yl_speak_up.show_fs(player, "assign_quest_step", nil)
return
-- show manage quests formspec
elseif(fields and fields.manage_quests) then
-- store information so that the back button can work
yl_speak_up.speak_to[pname][ "working_at" ] = "assign_quest_step"
yl_speak_up.show_fs(player, "manage_quests", nil)
return
elseif(fields and fields.show_quest) then
local q_id = yl_speak_up.speak_to[pname].q_id
local quest_var_name = nil
if(q_id and yl_speak_up.quests[q_id]) then
quest_var_name = yl_speak_up.strip_pname_from_var(yl_speak_up.quests[q_id].var_name, pname)
end
yl_speak_up.show_fs(player, "manage_quests", quest_var_name)
return
elseif(fields and fields.select_quest_id and fields.select_quest_id ~= "") then
local parts = string.split(fields.select_quest_id, " ")
if(parts and parts[1] and yl_speak_up.quests[parts[1]]) then
-- TODO: check if the player has access rights to that quest
-- TODO: check if the NPC has been added to that quest
yl_speak_up.speak_to[pname].q_id = parts[1]
yl_speak_up.show_fs(player, "add_quest_steps", "assign_quest_step")
return
end
elseif(fields and fields.show_step) then
yl_speak_up.show_fs(player, "manage_quest_steps", yl_speak_up.speak_to[pname].quest_step)
return
elseif(fields and fields.change_show_until) then
yl_speak_up.show_fs(player, "assign_quest_step", "change_show_until")
return
elseif(fields and fields.store_show_until and fields.select_show_until) then
local res = yl_speak_up.player_is_working_on_quest(player)
if(res.error_msg) then
return yl_speak_up.build_fs_quest_edit_error(res.error_msg, "back")
end
local choose_until_step = yl_speak_up.get_choose_until_step_list(
res.pname, res.q_id, yl_speak_up.speak_to[pname].quest_step)
local index = table.indexof(choose_until_step, fields.select_show_until or "")
if(index ~= -1) then
local dialog = yl_speak_up.speak_to[pname].dialog
if(not(yl_speak_up.check_if_dialog_has_option(dialog, d_id, o_id))) then
return
end
table.insert(yl_speak_up.npc_was_changed[ n_id ],
tostring(d_id).." "..tostring(o_id)..
" quest step will be offered until player reached step \""..
tostring(fields.select_show_until).."\".")
if(fields.select_show_until == " - the player restarts the quest -") then
fields.select_show_until = nil
elseif(fields.select_show_until == " - this quest step -") then
fields.select_show_until = yl_speak_up.speak_to[pname].quest_step
elseif(fields.select_show_until == " - the quest step immediately following this one -") then
fields.select_show_until = " next_step"
end
dialog.n_dialogs[d_id].d_options[o_id].quest_show_until = fields.select_show_until
end
yl_speak_up.show_fs(player, "assign_quest_step")
return
elseif(fields.delete_assignment and fields.delete_assignment ~= "") then
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
-- load the dialog (which may be diffrent from what the player is working on)
local stored_dialog = yl_speak_up.load_dialog(n_id, false)
-- check if the dialog exists
if(not(yl_speak_up.check_if_dialog_has_option(stored_dialog, d_id, o_id))) then
return show_error_fs(player, "Dialog or option not found.")
end
local quest_step_name = dialog.n_dialogs[d_id].d_options[o_id].quest_step
local var_name = dialog.n_dialogs[d_id].d_options[o_id].quest_id
local q_id = yl_speak_up.get_quest_id_by_var_name(var_name, pname)
-- we will ignore the return value so that this connection can be deleted even if
-- something went wrong (quest deleted, no write access to quest etc.)
local msg = yl_speak_up.quest_step_del_where(pname, q_id, quest_step_name,
{n_id = n_id, d_id = d_id, o_id = o_id})
if(not(n_id)) then
return show_error_fs(player, "NPC or location not found.")
end
-- log the change
yl_speak_up.log_change(pname, n_id,
"Dialog "..tostring(d_id)..": Option "..tostring(o_id)..
" no longer sets quest step \""..
tostring(dialog.n_dialogs[d_id].d_options[o_id].quest_step)..
"\" of quest \""..
tostring(dialog.n_dialogs[d_id].d_options[o_id].quest_id).."\".")
-- we have updated the quest step data - we need to update the NPC as well
local dialog = yl_speak_up.load_dialog(n_id, false)
if(yl_speak_up.check_if_dialog_has_option(dialog, d_id, o_id)) then
-- ok - the tables exist, so we can delete the connection
-- (if it doesn't exist there's no need to change the stored NPC)
dialog.n_dialogs[d_id].d_options[o_id].quest_id = nil
dialog.n_dialogs[d_id].d_options[o_id].quest_step = nil
-- write it back to disc
yl_speak_up.save_dialog(n_id, dialog)
end
-- the player is working on the NPC - thus, the NPC may be in a modified stage
-- that hasn't been written to disc yet, and we need to adjust this stage as well
dialog = yl_speak_up.speak_to[pname].dialog
-- delete the connection
dialog.n_dialogs[d_id].d_options[o_id].quest_id = nil
dialog.n_dialogs[d_id].d_options[o_id].quest_step = nil
yl_speak_up.show_fs(player, "edit_option_dialog",
{n_id = n_id, d_id = d_id, o_id = o_id})
return
elseif(not(fields) or not(fields.save)) then
return
end
-- actually store the data
local error_msg = ""
-- check if the quest exists
local quest_list = yl_speak_up.get_sorted_quest_list(pname)
local idx = table.indexof(quest_list, fields.quest_id or "")
if(not(fields.quest_id) or fields.quest_id == "" or idx < 1) then
error_msg = "Quest not found."
elseif(not(fields.quest_step)
or string.len(fields.quest_step) < 1
or string.len(fields.quest_step) > 80) then
error_msg = "The name of the quest step has to be between\n"..
"1 and 80 characters long."
end
if(error_msg ~= "") then
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:assign_quest_step",
formspec = "size[9,2]"..
"label[0.2,0.5;Error: "..minetest.formspec_escape(error_msg).."]"..
"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
return
end
-- we identify quests by their var_name - not by their q_id
-- (makes it easier to transfer quests from one server to another later)
dialog.n_dialogs[d_id].d_options[o_id].quest_id = yl_speak_up.add_pname_to_var(fields.quest_id, pname)
dialog.n_dialogs[d_id].d_options[o_id].quest_step = fields.quest_step
if(not(yl_speak_up.npc_was_changed[ n_id ])) then
yl_speak_up.npc_was_changed[ n_id ] = {}
end
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..d_id..": Option "..tostring(o_id)..
" has been set as quest step \""..
tostring(fields.quest_step).."\" for quest \""..tostring(fields.quest_id).."\".")
yl_speak_up.show_fs(player, "edit_option_dialog",
{n_id = n_id, d_id = d_id, o_id = o_id})
end
yl_speak_up.get_fs_assign_quest_step = function(player, param)
if(not(player)) 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 o_id = yl_speak_up.speak_to[pname].o_id
-- this only works in edit mode
if(not(n_id) or yl_speak_up.edit_mode[pname] ~= n_id) then
return "size[1,1]label[0,0;You cannot edit this NPC.]"
end
local dialog = yl_speak_up.speak_to[pname].dialog
if(not(yl_speak_up.check_if_dialog_has_option(dialog, d_id, o_id))) then
return "size[4,1]label[0,0;Dialog option does not exist.]"
end
local d_option = dialog.n_dialogs[d_id].d_options[o_id]
local quest_id = d_option.quest_id or ""
local quest_step = d_option.quest_step or ""
local quest_show_until = d_option.quest_show_until or " - the player restarts the quest -"
if(quest_show_until == quest_step) then
quest_show_until = " - this quest step -"
-- players cannot create quest steps with leading blanks
elseif(quest_show_until == " next_step") then
quest_show_until = " - the quest step immediately following this one -"
end
-- has a quest been selected?
local q_id = yl_speak_up.get_quest_id_by_var_name(quest_id, pname)
if(not(q_id)) then
local npc_id = tonumber(string.sub(n_id, 3))
local quest_list = {}
for id, d in pairs(yl_speak_up.quests) do
if(table.indexof(d.npcs or {}, npc_id) ~= -1) then
table.insert(quest_list, minetest.formspec_escape(
tostring(id).." "..tostring(d.name)))
end
end
if(#quest_list < 1) then
return "size[14,4]"..
"label[4.0,0.5;Using this option/answer shall be a quest step.]"..
"label[0.2,1.4;"..
"This NPC "..tostring(n_id).." has not been added to a quest yet.\n"..
"Please add him to the NPC list of one of your quests first!\n"..
"Search the \"Edit\" button for \"NPC that (may) participate:\" "..
"while viewing the desired quest.]"..
"button[0.2,3.0;3.6,0.8;manage_quests;Manage Quests]"..
"button[9.5,3.0;4.0,0.8;back;Back to edit option "..tostring(o_id).."]"
end
local selected = 1
-- local quest_list = yl_speak_up.get_sorted_quest_list(pname)
-- for i, v in ipairs(quest_list) do
-- quest_list[i] = minetest.formspec_escape(v)
-- if(quest_id and v == quest_id) then
-- selected = i
-- end
-- end
return "size[14,4]"..
"label[4.0,0.5;Using this option/answer shall be a quest step.]"..
"label[0.2,1.4;Select a quest:]"..
"dropdown[4.0,1.0;9.5,0.8;select_quest_id;"..
table.concat(quest_list, ',')..";"..
tostring(selected)..",]"..
"label[0.2,2.1;If you want to use a quest not mentionned here or a new one, "..
"click on \"Manage Quests\"\n"..
"and search the \"Edit\" button for \"NPC that (may) participate:\".]"..
"button[0.2,3.0;3.6,0.8;manage_quests;Manage Quests]"..
"button[9.5,3.0;4.0,0.8;back;Back to edit option "..tostring(o_id).."]"
end
-- this is the currently relevant quest
yl_speak_up.speak_to[pname].q_id = q_id
yl_speak_up.speak_to[pname].quest_step = quest_step
local choose_until_step = {}
local show_until = ""
if(param and param == "change_show_until") then
local choose_until_step = yl_speak_up.get_choose_until_step_list(pname, q_id, quest_step)
local index = table.indexof(choose_until_step, quest_show_until or "")
if(index == -1) then
index = 1
end
show_until = "dropdown[3.0,5.1;10,0.5;select_show_until;"..
table.concat(choose_until_step, ",")..";"..tostring(index)..";]"..
"button[13.5,4.8;4.3,0.8;store_show_until;Store]"
else
show_until = "label[3.0,5.4;"..minetest.colorize("#AAFFAA",
minetest.formspec_escape(quest_show_until)).."]"..
"button[13.5,4.8;4.3,0.8;change_show_until;Edit]"
end
local formspec = {
"size[18,7]"..
"label[3.0,0.5;Using this option/answer shall be a quest step.]"..
"label[0.2,1.4;Quest ID:]"..
"label[0.2,1.9;Quest name:]"..
"label[0.2,3.4;quest step:]"..
"button[13.5,3.1;4.3,0.8;show_step;Show this quest step]"..
"label[0.2,3.9;"..
"This quest step will be set for the player after the effects of "..
"the dialog option are executed.]"..
"label[0.2,4.9;"..
"This dialog option will be shown until the player has reached the "..
"following quest step:]"..
"button[0.2,0.2;2.0,0.8;delete_assignment;Delete]"..
"tooltip[delete_assignment;"..
"Delete the assignment of this quest step to this dialog option.\n"..
"Neither the quest step nor the dialog option will be deleted.\n"..
"Just their connection. Afterwards, you can assign a new or\n"..
"diffrent quest step to the dialog option.]"..
"button[13.5,6.0;4.3,0.8;manage_quests;Manage quests]"
}
table.insert(formspec, "label[3.0,1.4;")
table.insert(formspec, minetest.formspec_escape(q_id))
table.insert(formspec, "]")
table.insert(formspec, "label[3.0,1.9;")
table.insert(formspec, minetest.formspec_escape(yl_speak_up.quests[q_id].name or "- ? -"))
table.insert(formspec, "]")
table.insert(formspec, "button[13.5,1.0;4.3,0.8;show_quest;Show this quest ")
table.insert(formspec, tostring(q_id))
table.insert(formspec, "]")
table.insert(formspec, "label[0.2,2.9;This option here (")
table.insert(formspec, tostring(o_id))
table.insert(formspec, ") will be available once the player has reached all required ".. "quest steps for the following]")
table.insert(formspec, "label[3.0,3.4;")
table.insert(formspec, minetest.colorize("#FFFF00", minetest.formspec_escape(quest_step)))
table.insert(formspec, "]")
table.insert(formspec, "button[6.0,6.0;6.0,0.8;back;Back to edit option ")
table.insert(formspec, tostring(o_id))
table.insert(formspec, "]")
table.insert(formspec, show_until)
return table.concat(formspec, " ")
end
yl_speak_up.register_fs("assign_quest_step",
yl_speak_up.input_fs_assign_quest_step,
yl_speak_up.get_fs_assign_quest_step,
-- no special formspec required:
nil
)

View File

@ -1,54 +0,0 @@
-- spimple trading: one item(stack) for another item(stack)
-- (in edit_mode it's a bit diffrent)
-- if in edit mode: go back to the edit_options dialog
local old_input_do_trade_simple = yl_speak_up.input_do_trade_simple
yl_speak_up.input_do_trade_simple = function(player, formname, fields)
if(not(player)) then
return 0
end
local pname = player:get_player_name()
-- which trade are we talking about?
local trade = yl_speak_up.trade[pname]
local n_id = yl_speak_up.speak_to[pname].n_id
-- if in edit mode: go back to the edit options dialog
if(fields.back_to_edit_options
and n_id and yl_speak_up.in_edit_mode(pname)) then
local dialog = yl_speak_up.speak_to[pname].dialog
local tr = dialog.trades[ trade.trade_id ]
if(tr) then
-- done trading
yl_speak_up.speak_to[pname].target_d_id = nil
yl_speak_up.speak_to[pname].trade_id = nil
-- go to the edit options dialog
yl_speak_up.show_fs(player, "edit_option_dialog",
{n_id = n_id, d_id = tr.d_id, o_id = tr.o_id})
return
end
end
-- can the player edit this trade?
if(fields.edit_trade_simple
and n_id and yl_speak_up.in_edit_mode(pname)) then
-- force edit mode for this trade
trade.edit_trade = true
yl_speak_up.trade[pname] = trade
end
return old_input_do_trade_simple(player, formname, fields)
end
yl_speak_up.register_fs("do_trade_simple",
-- new version implemented here:
yl_speak_up.input_do_trade_simple,
-- this is just the old function:
yl_speak_up.get_fs_do_trade_simple_wrapper,
-- force formspec version 1:
1
)

View File

@ -1,133 +0,0 @@
-- This file contains what is necessary to add/edit an action.
--
-- Which diffrent types of actions are available?
-- -> The following fields are part of an action:
-- a_id the ID/key of the action
-- a_type selected from values_what
-- a_value used to store the subtype of a_type
--
-- no action (none): nothing to do.
--
-- a trade ("trade"):
-- a_buy what the NPC sells (itemstack)
-- a_pay what the NPC wants as payment (itemstack)
--
-- giving and taking of items ("npc_gives" and "npc_wants"):
-- a_on_failure if the action fails, go to this dialog
-- a_value itemstack of the given/wanted item in string form
-- a_item_desc the description the NPC shall set for that itemstack
-- (so that the player can distinguish it from other
-- itemstacks with the same items)
-- a_item_quest_id Special ID to make sure that it is really the *right*
-- item and not just something the player faked with an
-- engraving table or something similar
--
-- the player has to enter text ("text_input"):
-- a_value the expected answer the player has to enter
-- a_question the question the NPC shall ask the player (so that the
-- player can know which answer is expected here)
--
-- Show something custom (has to be provided by the server). (=call a function) ("evaluate"):
-- a_value the name of the function that is to be called
-- a_param1 the first paramter (optional; depends on function)
-- ..
-- a_param9 the 9th parameter (optional; depends on function)
--
-- a general, more complex formspec-basted puzzle ("puzzle"): not supported
-- (custom may be more helpful)
--
--
-- Note: Trades are not stored as actions - they are stored in
-- dialog.trades[ trade_id ] with <trade_id> == "<d_id> <o_id>"
--
-- some helper lists for creating the formspecs and evaulating
-- the player's answers:
-- general direction of what could make up an action
local check_what = {
"- please select -",
"No action (default).", -- 2
"Normal trade - one item(stack) for another item(stack).", -- 3
"The NPC gives something to the player (i.e. a quest item).", -- 4
"The player is expected to give something to the NPC (i.e. a quest item).", -- 5
"The player has to manually enter a password or passphrase or some other text.", -- 6
"Show something custom (has to be provided by the server).",
-- "The player has to move virtual items in a virtual inventory to the right position.", -- 8
}
-- how to store these as a_type in the action:
local values_what = {"", "none", "trade", "npc_gives", "npc_wants", "text_input", "evaluate", "puzzle"}
-- returns a human-readable text as description of the action
-- (as shown in the edit options dialog and in the edit effect formspec)
yl_speak_up.show_action = function(a)
if(not(a.a_type) or a.a_type == "" or a.a_type == "none") then
return "(nothing): Nothing to do. No action."
elseif(a.a_type == "trade") then
return "NPC sells \""..table.concat(a.a_buy, ";").."\" for \""..
table.concat(a.a_pay, ";").."\"."
elseif(a.a_type == "npc_gives") then
return "The NPC gives \""..tostring(a.a_item_desc or "- default description -")..
"\" (\""..tostring(a.a_value or "- ? -").."\") "..
"with ID \""..tostring(a.a_item_quest_id or "- no special ID -").."\"."
elseif(a.a_type == "npc_wants") then
return "The NPC wants \""..tostring(a.a_item_desc or "- default description -")..
"\" (\""..tostring(a.a_value or "- ? -").."\") "..
"with ID \""..tostring(a.a_item_quest_id or "- no special ID -").."\"."
elseif(a.a_type == "text_input") then
return "Q: \""..tostring(a.a_question).."\" A:\""..tostring(a.a_value).."\"."
elseif(a.a_type == "evaluate") then
local str = ""
for i = 1, 9 do
str = str..tostring(a["a_param"..tostring(i)])
if(i < 9) then
str = str..","
end
end
return "FUNCTION["..tostring(a.a_value).."]("..str..")"
-- puzzle is unused; better do that via custom
-- elseif(a.a_type == "puzzle") then
-- return "puzzle:"
end
-- fallback
return tostring(a.a_value)
end
-- these are only wrapper functions for those in fs_edit_general.lua
yl_speak_up.input_edit_actions = function(player, formname, fields)
return yl_speak_up.handle_input_fs_edit_option_related(player, formname, fields,
"a_", "actions", yl_speak_up.max_actions,
"(A)ctions", "tmp_action",
nil, -- unused - no block operations
values_what, {}, {}, {}, {},
check_what, {}, {}, {}, {},
nil, -- no variables
"edit_actions"
)
end
yl_speak_up.get_fs_edit_actions = function(player, table_click_result)
return yl_speak_up.build_fs_edit_option_related(player, table_click_result,
"a_", "actions", yl_speak_up.max_actions,
"(A)ctions", "tmp_action",
"What do you want to happen in this (A)ction?",
values_what, {}, {}, {}, {},
check_what, {}, {}, {}, {},
nil, -- no variables
yl_speak_up.show_action,
"table_of_elements",
nil, nil, nil, -- no variable handling here
nil -- nothing block-related to do here
)
end
yl_speak_up.register_fs("edit_actions",
yl_speak_up.input_edit_actions,
yl_speak_up.get_fs_edit_actions,
-- no special formspec required:
nil
)

View File

@ -1,352 +0,0 @@
-- This file contains what is necessary to add/edit an effect.
--
-- Which diffrent types of effects are available?
-- -> The following fields are part of an effect/result:
-- r_id the ID/key of the effect/result
-- r_type selected from values_what; the staffs allow to use other
-- types like "function" or "give_item" etc. - but that is not
-- supported here (cannot be edited or created; only be shown)
-- r_value used to store the subtype of r_type
--
-- a state/variable ("state"):
-- r_variable name of a variable the player has *write* access to;
-- dropdown list with allowed options
-- r_operator selected from values_operator
-- r_var_cmp_value can be set freely by the player (the variable will be
-- set to this value)
--
-- the value of a property of the NPC (for generic NPC) ("property"):
-- r_value name of the property that is to be changed
-- r_operator how shall the property be changed?
-- r_var_cmp_value the new value (or increment/decrement) for this property
--
-- something that has to be calculated or evaluated (=call a function) ("evaluate"):
-- r_value the name of the function that is to be called
-- r_param1 the first paramter (optional; depends on function)
-- ..
-- r_param9 the 9th parameter (optional; depends on function)
--
-- a block in the world ("block"):
-- r_pos a position in the world; determined by asking the player
-- to punch the block
-- r_node (follows from r_pos)
-- r_param2 (follows from r_pos)
--
-- place an item into the inventory of a block (i.e. a chest; "put_into_block_inv"):
-- r_pos the position of the target block
-- r_inv_list_name the inventory list where the item shall be moved to (often "main")
-- r_itemstack the itemstack that is to be moved
--
-- take item out of the inventory of a block (i.e. a chest; "take_from_block_inv");
-- same as "put_into_block_inv"
--
-- accept items the player has given to the NPC ("deal_with_offered_item"):
-- r_value subtype; one of yl_speak_up.dropdown_values_deal_with_offered_item
--
-- a craft receipe ("craft"):
-- r_value the expected craft result
-- r_craft_grid array containing the stacks in the 9 craft grid fields in string form
--
-- on_failure ("on_failure"):
-- r_value alternate target dialog if the previous *effect* failed
--
-- chat_all ("chat_all"):
-- r_value chat message sent to all players
--
--
-- give item to player ("give_item"): requires yl_speak_up.npc_privs_priv priv
-- r_value the itemstack that shall be added to the player's inventory
--
-- take item from player's inventory ("take_item"): requires yl_speak_up.npc_privs_priv priv
-- r_value the itemstack that will be removed from the player's inventory
--
-- move the player to a position ("move"): requires yl_speak_up.npc_privs_priv priv
-- r_value the position where the player shall be moved to
--
-- execute lua code ("function"): requires npc_master priv
-- r_value the lua code that shall be executed
--
-- Unlike in preconditions, trade (the trade action already happened) and
-- inventory actions are not supported as effects.
--
-- some helper lists for creating the formspecs and evaulating
-- the player's answers:
-- general direction of what could make up an effect
local check_what = {
"- please select -",
"an internal state (i.e. of a quest)", -- 2
"the value of a property of the NPC (for generic NPC)", -- property
"something that has to be calculated or evaluated (=call a function)", -- evaluate
"a block somewhere", -- 3
"put item from the NPC's inventory into a chest etc.", -- 4
"take item from a chest etc. and put it into the NPC's inventory",
-- 5
"an item the player offered to the NPC",
"NPC crafts something", -- 6
"go to other dialog if the previous effect failed", -- 7
"send a chat message to all players", -- 8
"give item (created out of thin air) to player (requires "..
tostring(yl_speak_up.npc_privs_priv).." priv)", -- 9
"take item from player and destroy it (requires "..
tostring(yl_speak_up.npc_privs_priv).." priv)", -- 10
"move the player to a given position (requires "..
tostring(yl_speak_up.npc_privs_priv).." priv)", -- 11
"execute Lua code (requires npc_master priv)", -- 12
}
-- how to store these as r_type in the precondition:
local values_what = {"", "state",
"property", "evaluate", "block",
-- interact with the inventory of blocks on the map
"put_into_block_inv", "take_from_block_inv",
-- the player gave an item to the NPC; now deal with it somehow
"deal_with_offered_item",
-- crafting, handling failure, send chat message to all
"craft", "on_failure", "chat_all",
-- the following require the yl_speak_up.npc_privs_priv priv:
"give_item", "take_item", "move",
-- the following require the npc_master priv:
"function",
}
-- unlike in the preconditions, the "I cannot punch it" option is
-- not offered here - because the player (and later the NPC) needs
-- to be able to build at this position
local check_block = {
"- please select -", -- 1
"If there is air: Place a block so that it looks like now.", -- 2
"If there is a block: Dig it.", -- 3
"Punch the block.", -- 4
"Right-click the block.", -- 5
}
-- how to store these as p_value (the actual node data gets stored as p_node, p_param2 and p_pos):
local values_block = {"", "place", "dig", "punch", "right-click"}
-- comparison operators for variables
local check_operator = {
"- please select -", -- 1
"new value:", -- 2
"discard/unset/forget", -- 3
"current time", -- 4
"quest step completed:", -- 5
minetest.formspec_escape("max(current, new_value)"), -- 6
minetest.formspec_escape("min(current, new_value)"), -- 7
"increment by:", -- 8
"decrement by:", -- 9
}
-- how to store these as r_value (the actual variable is stored in r_variable, and the value in r_new_value):
local values_operator = {"", "set_to", "unset", "set_to_current_time",
"quest_step", "maximum", "minimum", "increment", "decrement"}
-- get the list of variables the player has *write* access to
yl_speak_up.get_sorted_player_var_list_write_access = function(pname)
local var_list = {}
-- some values - like hour of day or HP of the player - can be read in
-- a precondition but not be modified
-- get the list of variables the player can *write*
local tmp = yl_speak_up.get_quest_variables_with_write_access(pname)
-- sort that list (the dropdown formspec element returns just an index)
table.sort(tmp)
for i, v in ipairs(tmp) do
table.insert(var_list, v)
end
return var_list
end
-- helper function for yl_speak_up.show_effect
-- used by "state" and "property"
yl_speak_up.show_effect_with_operator = function(r, var_name)
if(not(r.r_operator)) then
return "Error: Operator not defined."
elseif(r.r_operator == "set_to") then
return "set "..var_name.." to value \""..
tostring(r.r_var_cmp_value).."\""
elseif(r.r_operator == "unset") then
return "discard "..var_name.." (unset)"
elseif(r.r_operator == "set_to_current_time") then
return "set "..var_name.." to the current time"
elseif(r.r_operator == "quest_step") then
return "store that the player has completed quest step \""..
tostring(r.r_var_cmp_value).."\""
elseif(r.r_operator == "maximum") then
return "set "..var_name.." to value \""..
tostring(r.r_var_cmp_value).."\" if its current value is larger than that"
elseif(r.r_operator == "minimum") then
return "set "..var_name.." to value \""..
tostring(r.r_var_cmp_value).."\" if its current value is lower than that"
elseif(r.r_operator == "increment") then
return "increment the value of "..var_name.." by \""..
tostring(r.r_var_cmp_value).."\""
elseif(r.r_operator == "decrement") then
return "decrement the value of "..var_name.." by \""..
tostring(r.r_var_cmp_value).."\""
else
return "ERROR: Wrong operator \""..tostring(r.r_operator).."\" for "..var_name
end
end
-- returns a human-readable text as description of the effects
-- (as shown in the edit options dialog and in the edit effect formspec)
yl_speak_up.show_effect = function(r, pname)
if(not(r.r_type) or r.r_type == "") then
return "(nothing): Nothing to do. No effect."
elseif(r.r_type == "give_item") then
return "give_item: Add \""..tostring(r.r_value).."\" to the player's inventory."
elseif(r.r_type == "take_item") then
return "take_item: Take \""..tostring(r.r_value).."\" from the player's inventory."
elseif(r.r_type == "move") then
return "move: Move the player to "..tostring(r.r_value).."."
elseif(r.r_type == "function") then
return "function: execute \""..tostring(r.r_value).."\"."
elseif(r.r_type == "trade") then
return "trade: obsolete (now defined as an action)"
elseif(r.r_type == "dialog") then
return "Switch to dialog \""..tostring(r.r_value).."\"."
elseif(r.r_type == "state") then
local var_name = "VARIABLE[ - ? - ]"
if(r.r_variable) then
var_name = "VARIABLE[ "..tostring(
yl_speak_up.strip_pname_from_var(r.r_variable, pname)).." ]"
end
return yl_speak_up.show_effect_with_operator(r, var_name)
-- the value of a property of the NPC (for generic NPC) ("property"):
elseif(r.r_type == "property") then
local var_name = "PROPERTY[ "..tostring(r.r_value or "- ? -").." ]"
return yl_speak_up.show_effect_with_operator(r, var_name)
-- something that has to be calculated or evaluated (=call a function) ("evaluate"):
elseif(r.r_type == "evaluate") then
local str = ""
for i = 1, 9 do
str = str..tostring(r["r_param"..tostring(i)])
if(i < 9) then
str = str..","
end
end
return "FUNCTION["..tostring(r.r_value).."]("..str..")"
elseif(r.r_type == "block") then
if(not(r.r_pos) or type(r.r_pos) ~= "table"
or not(r.r_pos.x) or not(r.r_pos.y) or not(r.r_pos.z)) then
return "ERROR: r.r_pos is "..minetest.serialize(r.r_pos)
-- we don't check here yet which node is actually there - that will be done upon execution
elseif(yl_speak_up.check_blacklisted(r.r_value, r.r_node, r.r_node)) then
return "ERROR: Blocks of type \""..tostring(r.r_node).."\" do not allow "..
"interaction of type \""..tostring(r.r_value).."\" for NPC."
elseif(r.r_value == "place") then
return "Place \""..tostring(r.r_node).."\" with param2: "..tostring(r.r_param2)..
" at "..minetest.pos_to_string(r.r_pos).."."
elseif(r.r_value == "dig") then
return "Dig the block at "..minetest.pos_to_string(r.r_pos).."."
elseif(r.r_value == "punch") then
return "Punch the block at "..minetest.pos_to_string(r.r_pos).."."
elseif(r.r_value == "right-click") then
return "Right-click the block at "..minetest.pos_to_string(r.r_pos).."."
else
return "ERROR: Don't know what to do with the block at "..
minetest.pos_to_string(r.r_pos)..": \""..tostring(r.r_value).."\"?"
end
elseif(r.r_type == "craft") then
-- this is only shown in the edit options menu and when editing an effect;
-- we can afford a bit of calculation here (it's not a precondtion...)
if(not(r.r_value) or not(r.r_craft_grid)) then
return "ERROR: Crafting not configured correctly."
end
local craft_str = "Craft \""..tostring(r.r_value).."\" from "..
table.concat(r.r_craft_grid, ", ").."."
-- check here if the craft receipe is broken
local input = {}
input.items = {}
for i, v in ipairs(r.r_craft_grid) do
input.items[ i ] = ItemStack(v or "")
end
input.method = "normal" -- normal crafting; no cooking or fuel or the like
input.width = 3
local output, decremented_input = minetest.get_craft_result(input)
if(output.item:is_empty()) then
return "Error: Recipe changed! No output for "..craft_str
end
-- the craft receipe may have changed in the meantime and yield a diffrent result
local expected_stack = ItemStack(r.r_value)
if(output.item:get_name() ~= expected_stack:get_name()
or output.item:get_count() ~= expected_stack:get_count()) then
return "Error: Amount of output changed! "..craft_str
end
return craft_str
elseif(r.r_type == "on_failure") then
return "If the *previous* effect failed, go to dialog \""..tostring(r.r_value).. "\"."
elseif(r.r_type == "chat_all") then
return "Send chat message: \""..tostring(r.r_value).."\""
elseif(r.r_type == "put_into_block_inv") then
if(not(r.r_pos) or type(r.r_pos) ~= "table"
or not(r.r_pos.x) or not(r.r_pos.y) or not(r.r_pos.z)) then
return "ERROR: r.r_pos is "..minetest.serialize(r.r_pos)
end
return "Put item \""..tostring(r.r_itemstack).."\" from NPC inv into block at "..
minetest.pos_to_string(r.r_pos)..
" in inventory list \""..tostring(r.r_inv_list_name).."\"."
elseif(r.r_type == "take_from_block_inv") then
if(not(r.r_pos) or type(r.r_pos) ~= "table"
or not(r.r_pos.x) or not(r.r_pos.y) or not(r.r_pos.z)) then
return "ERROR: r.r_pos is "..minetest.serialize(r.r_pos)
end
return "Take item \""..tostring(r.r_itemstack).."\" from block at "..
minetest.pos_to_string(r.r_pos)..
" out of inventory list \""..tostring(r.r_inv_list_name)..
"\" and put it into the NPC's inventory."
elseif(r.r_type == "deal_with_offered_item") then
local nr = 1
if(r.r_value) then
nr = math.max(1, table.indexof(yl_speak_up.dropdown_values_deal_with_offered_item,
r.r_value))
return yl_speak_up.dropdown_list_deal_with_offered_item[ nr ]
end
return "ERROR: Missing subtype r.r_value: \""..tostring(r.r_value).."\""
end
-- fallback
return tostring(r.r_value)
end
-- these are only wrapper functions for those in fs_edit_general.lua
yl_speak_up.input_edit_effects = function(player, formname, fields)
return yl_speak_up.handle_input_fs_edit_option_related(player, formname, fields,
"r_", "o_results", yl_speak_up.max_result_effects,
"(Ef)fect", "tmp_result",
"Please punch the block you want to manipulate in your effect!",
values_what, values_operator, values_block, {}, {},
check_what, check_operator, check_block, {}, {},
-- player variables with write access
yl_speak_up.get_sorted_player_var_list_write_access,
"edit_effects"
)
end
yl_speak_up.get_fs_edit_effects = function(player, table_click_result)
return yl_speak_up.build_fs_edit_option_related(player, table_click_result,
"r_", "o_results", yl_speak_up.max_result_effects,
"(Ef)fect", "tmp_result",
"What do you want to change with this effect?",
values_what, values_operator, values_block, {}, {},
check_what, check_operator, check_block, {}, {},
-- player variables with write access
yl_speak_up.get_sorted_player_var_list_write_access,
yl_speak_up.show_effect,
"table_of_elements",
"Change the value of the following variable:", "Set variable to:", "New value:",
"The NPC shall do something to the block at the following position:"
)
end
yl_speak_up.register_fs("edit_effects",
yl_speak_up.input_edit_effects,
yl_speak_up.get_fs_edit_effects,
-- no special formspec required:
nil
)

View File

@ -1,853 +0,0 @@
-- helper function; used by
-- * yl_speak_up.get_fs_edit_option_dialog and
-- * yl_speak_up.get_fs_edit_trade_limit
yl_speak_up.get_list_of_effects_and_target_dialog_and_effect = function(dialog, results, pname, target_dialog, target_effect)
local list_of_effects = ""
local count_effects = 0
if(results) then
local sorted_key_list = yl_speak_up.sort_keys(results)
for i, k in ipairs(sorted_key_list) do
local v = results[ k ]
if v.r_type == "dialog" and (dialog.n_dialogs[v.r_value] ~= nil or v.r_value == "d_end" or v.r_value == "d_got_item") then
list_of_effects = list_of_effects..
minetest.formspec_escape(v.r_id)..",#999999,"..
minetest.formspec_escape(v.r_type)..","..
minetest.formspec_escape(
yl_speak_up.show_effect(v, pname))..","
-- there may be more than one in the data structure
target_dialog = v.r_value
target_effect = v
elseif v.r_type ~= "dialog" then
list_of_effects = list_of_effects..
minetest.formspec_escape(v.r_id)..",#FFFF00,"..
minetest.formspec_escape(v.r_type)..","..
minetest.formspec_escape(
yl_speak_up.show_effect(v, pname))..","
end
count_effects = count_effects + 1
end
end
if(count_effects < yl_speak_up.max_result_effects) then
list_of_effects = list_of_effects..",#00FF00,add,Add a new (Ef)fect"
else
list_of_effects = list_of_effects..",#AAAAAA,-,"..
"Maximum amount of allowed (Ef)fects per option reached!"
end
return {list = list_of_effects, target_dialog = target_dialog, target_effect = target_effect}
end
-- process input from formspec created in get_fs_edit_option_dialog(..)
yl_speak_up.input_edit_option_dialog = function(player, formname, fields)
if formname ~= "yl_speak_up:edit_option_dialog" then
return
end
local pname = player:get_player_name()
-- Is the player working on this particular npc?
local edit_mode = (yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id)
if(not(edit_mode)) then
return
end
local n_id = yl_speak_up.speak_to[pname].n_id
local d_id = yl_speak_up.speak_to[pname].d_id
local o_id = yl_speak_up.speak_to[pname].o_id
local dialog = yl_speak_up.speak_to[pname].dialog
local n_dialog = dialog.n_dialogs[d_id]
if(not(n_dialog) or not(n_dialog.d_options)) then
return
end
local d_option = n_dialog.d_options[o_id]
if(not(d_option)) then
return
end
if(fields.assign_quest_step and fields.assign_quest_step ~= "") then
yl_speak_up.show_fs(player, "assign_quest_step",
{n_id = n_id, d_id = d_id, o_id = o_id})
return
end
if(fields.switch_tab and fields.switch_tab == "2") then
yl_speak_up.show_fs(player, "edit_option_dialog",
{n_id = n_id, d_id = d_id, o_id = o_id,
caller="show_if_action_failed"})
return
elseif(fields.switch_tab and fields.switch_tab == "1") then
yl_speak_up.show_fs(player, "edit_option_dialog",
{n_id = n_id, d_id = d_id, o_id = o_id,
caller="show_if_action_succeeded"})
return
elseif(fields.switch_tab and fields.switch_tab == "3") then
yl_speak_up.show_fs(player, "edit_option_dialog",
{n_id = n_id, d_id = d_id, o_id = o_id,
caller="show_tab_limit_guessing"})
return
elseif(fields.switch_tab and fields.switch_tab == "4") then
yl_speak_up.show_fs(player, "edit_option_dialog",
{n_id = n_id, d_id = d_id, o_id = o_id,
caller="show_tab_limit_repeating"})
return
end
-- this menu is specific to an option for a dialog; if no dialog is selected, we really
-- can't know what to do
if(not(o_id) and d_id) then
yl_speak_up.show_fs(player, "talk", {n_id = n_id, d_id = d_id})
elseif(not(d_id)) then
return
end
-- backwards compatibility to when this was a hidden field
fields.o_id = o_id
-- handles changes to o_text_when_prerequisites_met, target dialog, adding of a new dialog
local result = yl_speak_up.edit_mode_apply_changes(pname, fields)
-- if a new option was added or the target dialog of this one changed, display the right new option
if(result and result["show_next_option"] and n_dialog.d_options[result["show_next_option"]]) then
yl_speak_up.show_fs(player, "edit_option_dialog",
{n_id = n_id, d_id = d_id, o_id = result["show_next_option"],
caller="show_next_option"})
return
end
if(fields.save_option) then
yl_speak_up.show_fs(player, "edit_option_dialog",
{n_id = n_id, d_id = d_id, o_id = o_id, caller="save_option"})
return
end
-- want to edit the text that is shown when switching to the next dialog?
if(fields.button_edit_action_failed_dialog
or fields.button_edit_action_success_dialog
or fields.save_dialog_modification
or fields.button_edit_limit_action_failed_repeat
or fields.button_edit_limit_action_success_repeat
or fields.turn_alternate_text_into_new_dialog) then
if( yl_speak_up.handle_edit_actions_alternate_text(
-- x_id, id_prefix, target_element and tmp_data_cache are nil here
player, pname, n_id, d_id, o_id, nil, nil,
"edit_option_dialog", nil, fields, nil)) then
-- the function above showed a formspec already
return
else
yl_speak_up.show_fs(player, "edit_option_dialog",
{n_id = n_id, d_id = d_id, o_id = o_id,
caller="back_from_edit_dialog_modifications"})
return
end
elseif(fields.back_from_edit_dialog_modification) then
-- no longer working on an alternate text
yl_speak_up.speak_to[pname].edit_alternate_text_for = nil
yl_speak_up.show_fs(player, "edit_option_dialog",
{n_id = n_id, d_id = d_id, o_id = o_id, caller="back_from_edit_dialog_modifications"})
return
end
-- back to the main dialog window?
-- (this also happens when the last option was deleted)
if(fields.show_current_dialog or fields.quit or fields.button_exit or not(d_option) or fields.del_option) then
yl_speak_up.show_fs(player, "talk", {n_id = n_id, d_id = d_id})
return
end
-- the player wants to see the previous option/answer
if(fields.edit_option_prev) then
-- sort all options by o_sort
local sorted_list = yl_speak_up.get_sorted_options(n_dialog.d_options, "o_sort")
local o_found = o_id
for i, o in ipairs(sorted_list) do
if(o == o_id and sorted_list[ i-1]) then
o_found = sorted_list[ i-1 ]
end
end
-- show that dialog; fallback: show the same (o_id) again
yl_speak_up.show_fs(player, "edit_option_dialog",
{n_id = n_id, d_id = d_id, o_id = o_found, caller="prev option"})
return
-- the player wants to see the next option/answer
elseif(fields.edit_option_next) then
-- sort all options by o_sort
local sorted_list = yl_speak_up.get_sorted_options(n_dialog.d_options, "o_sort")
local o_found = o_id
for i, o in ipairs(sorted_list) do
if(o == o_id and sorted_list[ i+1 ]) then
o_found = sorted_list[ i+1 ]
end
end
-- show that dialog; fallback: show the same (o_id) again
yl_speak_up.show_fs(player, "edit_option_dialog",
{n_id = n_id, d_id = d_id, o_id = o_found, caller="next option"})
return
-- the player clicked on a precondition
elseif(fields.table_of_preconditions) then
yl_speak_up.show_fs(player, "edit_preconditions", fields.table_of_preconditions)
return
-- the player clicked on an action
elseif(fields.table_of_actions) then
yl_speak_up.show_fs(player, "edit_actions", fields.table_of_actions)
return
-- the player clicked on an effect
elseif(fields.table_of_effects) then
yl_speak_up.show_fs(player, "edit_effects", fields.table_of_effects)
return
end
-- if ESC is pressed or anything else unpredicted happens: go back to the main dialog edit window
-- reason: don't loose any unsaved changes to the dialog
yl_speak_up.show_fs(player, "talk", {n_id = n_id, d_id = d_id})
end
-- edit options (not via staff but via the "I am your owner" dialog)
yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id, caller)
-- n_id, d_id and o_id have already been checked when this function is called
local pname = player:get_player_name()
local dialog = yl_speak_up.speak_to[pname].dialog
local n_dialog = dialog.n_dialogs[d_id]
-- currently no trade running (we're editing options)
yl_speak_up.trade[pname] = nil
yl_speak_up.speak_to[pname].trade_id = nil
if(not(n_dialog) or not(n_dialog.d_options) or not(n_dialog.d_options[o_id])) then
return "size[6,2]"..
"label[0.2,0.5;Ups! Option "..minetest.formspec_escape(tostring(o_id))..
" does not exist.]"..
"button_exit[2,1.5;1,0.9;exit;Exit]"
end
local d_option = n_dialog.d_options[o_id]
-- if it is a quest step, then show that; else allow creating a quest step
local quest_step_text = "button[15.4,0.1;6.0,0.9;assign_quest_step;Turn this into a quest step]"
if( d_option.quest_id and d_option.quest_id ~= ""
and d_option.quest_step and d_option.quest_step ~= "") then
local q_id = ""
local quest_name = "["..tostring(d_option.quest_id).."] - unknown quest -"
for q_id, data in pairs(yl_speak_up.quests) do
if(data and data.var_name == d_option.quest_id) then
quest_name = "["..tostring(q_id)..": "..
tostring(yl_speak_up.strip_pname_from_var(data.var_name, pname))..
"] "..
tostring(data.name)
end
end
quest_step_text = table.concat({"box[4.9,0.0;14.0,1.1;#BB77BB]",
"label[0.2,0.3;This is quest step:]",
"label[5.0,0.3;",
minetest.colorize("#00FFFF",
minetest.formspec_escape(d_option.quest_step)),
"]",
"label[0.2,0.8;of the quest:]",
"label[5.0,0.8;",
minetest.colorize("#CCCCFF",
minetest.formspec_escape(quest_name)),
"]",
"button[19.4,0.1;2.0,0.9;assign_quest_step;Change]"
}, "")
end
-- offer the correct preselection for hidden/grey/show text
local alternate_answer_option = "3"
if(d_option.o_hide_when_prerequisites_not_met == "true") then
alternate_answer_option = "1"
elseif(d_option.o_grey_when_prerequisites_not_met == "true") then
alternate_answer_option = "2"
end
local answer_mode = 0
-- shall this option be choosen automaticly?
if(d_option.o_autoanswer and d_option.o_autoanswer == 1) then
answer_mode = 1
-- or randomly?
elseif(n_dialog.o_random) then
answer_mode = 2
end
-- show an option only once
local visit_only_once = 0
if(d_option.o_visit_only_once and d_option.o_visit_only_once == 1) then
visit_only_once = 1
end
local answer_text =
-- answer of the player (the actual option)
"container[0.0,8.3]"..
"label[0.2,0.0;..the player may answer with this text"..
minetest.formspec_escape(" [dialog option \""..tostring(o_id).."\"]:").."]"..
"dropdown[13.3,-0.4;2.5,0.7;option_visits;"..
"often,*once*;"..tostring(visit_only_once + 1)..";]"..
"tooltip[option_visits;\"often\" allows to select this option whenever the\n"..
"\tpreconditions are fulfilled.\n"..
"\"*once*\" greys out the option after it has been selected\n"..
"\tone time successfully.\n"..
"Useful for visually marking options as read for the player.\n"..
"Talking to the NPC anew resets this option and it can be selected again.]"..
"dropdown[16.0,-0.4;5.3,0.7;option_autoanswer;"..
"by clicking on it,automaticly,randomly;"..tostring(answer_mode+1)..";]"
-- (automaticly *by fulfilling the prerequirements*)
if(d_id == "d_got_item" or d_id == "d_trade") then
answer_mode = 1
d_option.o_autoanswer = 1
answer_text =
"container[0.0,8.3]"..
"label[0.2,0.0;..this option will be selected automaticly.]"
end
if(answer_mode == 0 and (d_id ~= "d_got_item" and d_id ~= "d_trade")) then
answer_text = table.concat({answer_text,
"label[1.2,0.8;A:]",
"field[1.7,0.3;19.6,0.9;text_option_",
minetest.formspec_escape(o_id),
";;",
minetest.formspec_escape(d_option.o_text_when_prerequisites_met),
"]",
"tooltip[option_text_met;This is the answer the player may choose if the "..
"preconditions are all fulfilled.]",
-- dropdown for selecting weather to show the alternate answer or not
"label[0.2,1.7;..but if at least one pre(C)ondition is not fulfilled, then...]",
"dropdown[12.0,1.3;9.3,0.7;hide_or_grey_or_alternate_answer;",
"..hide this answer.,",
"..grey out the following answer:,",
"..display the following alternate answer:;",
alternate_answer_option,
";]",
-- alternate answer
"label[1.2,2.5;A:]",
"field[1.7,2.0;19.6,0.9;option_text_not_met;;",
minetest.formspec_escape(d_option.o_text_when_prerequisites_not_met),
"]",
"tooltip[option_text_not_met;This is the answer the player may choose if the "..
"preconditions are NOT all fulfilled.]",
"container_end[]"
}, "")
elseif(answer_mode == 1) then
answer_text = answer_text..
"label[1.2,0.8;This option will not be shown but will be selected automaticly if all "..
"prerequirements are fullfilled.]"..
"label[1.2,1.4;The remaining options of this dialog will in this case not be evaluated.]"..
"label[1.2,2.0;The NPC will proceed as if this option was choosen manually.]"..
"label[1.2,2.6;"
if(d_id == "d_got_item") then
answer_text = answer_text..
"Note: This is used here to process items that the player gave to the NPC."
elseif(d_id == "d_trade") then
answer_text = answer_text..
"Note: This is useful for refilling stock by crafting new things when "..
"necessary, or for getting\nsupplies from a storage, or for storing "..
"traded goods in external storage chests."
else
answer_text = answer_text..
"This is i.e. useful for offering a diffrent start dialog depending on the "..
"player's progress in a quest."
end
answer_text = answer_text .. "]container_end[]"
elseif(answer_mode == 2) then
answer_text = answer_text..
"label[1.2,0.8;One option of the dialog - for example this one - will be selected randomly.]"..
"label[1.2,1.4;The other options of this dialog will be set to random as well.]"..
"label[1.2,2.0;The NPC will proceed as if this dialog was choosen manually.]"..
"label[1.2,2.6;Useful for small talk for generic NPC but usually not for quests.]"..
"container_end[]"
end
-- remember which option we are working at (better than a hidden field)
yl_speak_up.speak_to[pname].o_id = o_id
-- are there any preconditions?
local list_of_preconditions = ""
local prereq = d_option.o_prerequisites
local count_prereq = 0
if(prereq) then
local sorted_key_list = yl_speak_up.sort_keys(prereq)
for i, k in ipairs(sorted_key_list) do
local v = prereq[ k ]
list_of_preconditions = list_of_preconditions..
minetest.formspec_escape(v.p_id)..",#FFFF00,"..
minetest.formspec_escape(v.p_type)..","..
minetest.formspec_escape(
yl_speak_up.show_precondition(v, pname))..","
count_prereq = count_prereq + 1
end
end
if(count_prereq < yl_speak_up.max_prerequirements) then
list_of_preconditions = list_of_preconditions..",#00FF00,add,Add a new pre(C)ondition"
else
list_of_preconditions = list_of_preconditions..",#AAAAAA,-,"..
"Maximum amount of pre(C)onditions per option reached!"
end
-- build action list the same way as list of preconditions and effects
local list_of_actions = ""
local actions = d_option.actions
local count_actions = 0
local action_data = nil
-- if autoanswer or random is choosen, then there can be no action
if(answer_mode == 1 or answer_mode == 2) then
actions = nil
count_actions = 0
caller = ""
end
if(actions) then
local sorted_key_list = yl_speak_up.sort_keys(actions)
for i, k in ipairs(sorted_key_list) do
local v = actions[ k ]
list_of_actions = list_of_actions..
minetest.formspec_escape(v.a_id)..",#FFFF00,"..
minetest.formspec_escape(v.a_type)..","..
minetest.formspec_escape(
yl_speak_up.show_action(v))..","
count_actions = count_actions + 1
action_data = v
end
end
if(count_actions < yl_speak_up.max_actions) then
list_of_actions = list_of_actions..",#00FF00,add,Add a new (A)ction"
else
list_of_actions = list_of_actions..",#AAAAAA,-,"..
"Maximum amount of (A)ctions per option reached!"
end
-- list of (A)ctions (there can only be one per option; i.e. a trade)
local action_list_text =
"container[0.0,12.0]"..
"label[0.2,0.0;When this answer has been selected, start the following (A)ction:]"..
"tablecolumns[text;color,span=1;text;text]"
if(answer_mode == 1) then
action_list_text = action_list_text..
"label[1.2,0.6;No actions are executed because this option here is automaticly selected.]"..
"container_end[]"
elseif(answer_mode == 2) then
action_list_text = action_list_text..
"label[1.2,0.6;No actions are executed because this option here is selected randomly.]"..
"container_end[]"
else
action_list_text = action_list_text..
"table[1.2,0.3;20.2,0.7;table_of_actions;"..
list_of_actions..";0]"..
"container_end[]"
end
-- find the right target dialog for this option (if it exists)
local target_dialog = nil
-- which effect holds the information about the target dialog?
-- set this to a fallback for yl_speak_up.show_colored_dialog_text
local target_effect = {r_id = "-?-", r_type = "dialog"}
-- and build the list of effects
local results = d_option.o_results
-- create a new dialog type option if needed
if(not(results) or not(next(results))) then
target_dialog = yl_speak_up.prepare_new_dialog_for_option(
dialog, pname, n_id, d_id, o_id,
yl_speak_up.text_new_dialog_id,
results)
-- make sure we are up to date (a new option was inserted)
results = d_option.o_results
end
-- constructs the list_of_effects; may also update target_dialog and target_effect
local res = yl_speak_up.get_list_of_effects_and_target_dialog_and_effect(dialog, results, pname,
target_dialog, target_effect)
local list_of_effects = res.list
target_dialog = res.target_dialog
target_effect = res.target_effect
-- if no target dialog has been selected: default is to go to the dialog with d_sort 0
if(not(target_dialog) or target_dialog == "" or
(not(dialog.n_dialogs[target_dialog])
and target_dialog ~= "d_end"
and target_dialog ~= "d_got_item")) then
for d, v in pairs(dialog.n_dialogs) do
if(v.d_sort and tonumber(v.d_sort) == 0) then
target_dialog = d
end
end
end
-- build the list of available dialogs for the dropdown list(s)
local dialog_list = yl_speak_up.text_new_dialog_id
local dialog_selected = "1"
-- if there are dialogs defined
if(dialog and dialog.n_dialogs) then
-- the first entry will be "New dialog"
local n = 1
for k, v in pairs(dialog.n_dialogs) do
local d_name = (v.d_name or v.d_id or "?")
-- build the list of available dialogs for the dropdown list(s)
dialog_list = dialog_list..","..minetest.formspec_escape(d_name)
-- which one is the current dialog?
n = n + 1
if(v.d_id == target_dialog) then
dialog_selected = tostring(n)
end
end
if(target_dialog == "d_end") then
dialog_selected = tostring(n + 1)
end
end
dialog_list = dialog_list..",d_end"
if(not(target_dialog)) then
target_dialog = "- none -"
end
-- can the button "prev(ious)" be shown?
local button_prev = ""
-- can the button "next" be shown?
local button_next = ""
-- sort all options by o_sort
local sorted_list = yl_speak_up.get_sorted_options(n_dialog.d_options, "o_sort")
local o_found = o_id
local anz_options = 0
for i, o in ipairs(sorted_list) do
-- the buttons are inside a container; thus, Y is 0.0
if(o == o_id and sorted_list[ i-1 ]) then
button_prev = ""..
"button[7.9,0.0;2.0,0.9;edit_option_prev;Prev]"..
"tooltip[edit_option_prev;Go to previous option/answer "..
"(according to o_sort).]"
end
if(o == o_id and sorted_list[ i+1 ]) then
button_next = ""..
"button[12.5,0.0;2.0,0.9;edit_option_next;Next]"..
"tooltip[edit_option_next;Go to next option/answer "..
"(according to o_sort).]"
end
anz_options = anz_options + 1
end
-- less than yl_speak_up.max_number_of_options_per_dialog options?
local button_add = ""..
-- the buttons are inside a container; thus, Y is 0.0
"button[2.4,0.0;2.0,0.9;add_option;Add]"..
"tooltip[add_option;Add a new option/answer to this dialog.]"
if(anz_options >= yl_speak_up.max_number_of_options_per_dialog
or target_dialog == "d_end") then
button_add = ""
end
-- make all following coordinates relative
local action_text = "container[0.2,14.0]"..
"box[0.25,0.0;21.0,6.7;#555555]"
local tab_list = "tabheader[0.2,0.0;switch_tab;"..
"If the action was successful:,"..
"If the action failed:,"..
"Limit guessing:,"..
"Limit repeating:"
-- show what happens if the action fails
if(caller == "show_if_action_failed") then
-- allow to switch between successful and failed actions
action_text = action_text..tab_list..";2;true;true]"..
"label[0.4,0.6;"..
"If the player *failed* to complete the above action correctly,]"
if(action_data and action_data.a_on_failure
and dialog.n_dialogs and dialog.n_dialogs[ action_data.a_on_failure]) then
action_text = action_text..
-- ..and what the NPC will reply to that answer
"tooltip[1.2,3.9;19.6,2.5;This is what the NPC will say next when "..
"the player has failed to complete the action.]"..
"container[0.0,3.2]"..
"label[0.4,0.4;..the NPC will react to this failed action with the "..
"following dialog \""..tostring(action_data.a_on_failure)..
"\""..
yl_speak_up.show_colored_dialog_text(
dialog,
action_data,
action_data.a_on_failure,
"1.2,0.7;19.6,2.5;d_text_next",
"with the *modified* text",
":]",
"button_edit_action_failed_dialog")..
"container_end[]"
else
action_text = action_text..
"label[0.4,3.6;..go back to the initial dialog.]"
end
-- show time-based restrictions (max guesses per time);
-- the values will be saved in function yl_speak_up.edit_mode_apply_changes
elseif( caller == "show_tab_limit_guessing") then
local timer_name = "timer_on_failure_"..tostring(d_id).."_"..tostring(o_id)
local timer_data = yl_speak_up.get_variable_metadata(timer_name, "parameter", true)
if(not(timer_data)) then
timer_data = {}
end
action_text = table.concat({action_text,
tab_list,
";3;true;true]",
-- allow to switch between successful and failed actions
"label[0.4,0.6;",
"Apply the following time-based restrictions to limit wild guessing:]",
-- timer for failed actions
"label[0.4,1.6;The player can make]",
"field[4.9,1.0;1.5,0.9;timer_max_attempts_on_failure;;",
tostring(timer_data[ "max_attempts" ] or 0),
"]",
"label[6.7,1.6;attempts to complete this action successfully each]",
"field[17.5,1.0;1.5,0.9;timer_max_seconds_on_failure;;",
tostring(timer_data[ "duration" ] or 0),
"]",
"label[19.2,1.6;seconds.]",
"label[0.4,2.2;Hint: 3 attempts per 1200 seconds (=20 minutes or one MineTest day)"..
" may be good values to\navoid wild guessing while not making the player "..
"having to wait too long to try again.]"..
"tooltip[timer_max_attempts_on_failure;How many tries shall the player have?"..
"\nA value of 0 disables this restriction.]"..
"tooltip[timer_max_seconds_on_failure;After which time can the player try again?"..
"\nA value of 0 disables this restriction.]"..
-- ..and what the NPC will explain in such a case
"tooltip[1.2,3.9;19.6,2.5;This is what the NPC will say next when "..
"\nthe player has failed to complete the action too"..
"\nmany times for the NPC's patience and the player"..
"\nhas to wait some time before guessing again.]"..
"container[0.0,3.2]"..
"label[0.4,0.4;The NPC will explain his unwillingness to accept more "..
"guesses ",
yl_speak_up.show_colored_dialog_text(
dialog,
{alternate_text = (timer_data[ "alternate_text" ]
or yl_speak_up.standard_text_if_action_failed_too_often)},
d_id, -- show the same dialog again
"1.2,0.7;19.6,2.5;d_text_next",
"with the following text",
":]",
"button_edit_limit_action_failed_repeat"),
"container_end[]"
}, "")
-- show time-based restrictions (time between repeating this action successfully)
elseif( caller == "show_tab_limit_repeating") then
local timer_name = "timer_on_success_"..tostring(d_id).."_"..tostring(o_id)
local timer_data = yl_speak_up.get_variable_metadata(timer_name, "parameter", true)
if(not(timer_data)) then
timer_data = {}
end
action_text = table.concat({action_text,
tab_list,
";4;true;true]",
"label[0.4,0.6;",
"Apply the following time-based restrictions to limit too quick repeating:]",
-- timer for successful actions
"label[0.4,1.6;If the player completed the action successfully, he shall have to"..
" wait]",
"field[15.0,1.0;1.5,0.9;timer_max_seconds_on_success;;",
tostring(timer_data[ "duration" ] or 0),
"]",
"label[16.7,1.6;seconds until he]",
"label[0.4,2.1;can repeat the action. Hint: 1200 seconds (=20 minutes or one ",
"MineTest day) may be a good value.]",
"tooltip[timer_max_seconds_on_success;",
minetest.formspec_escape(
"If you hand out a quest item, you may not want the player"..
"\nto immediately repeat the action countless times, thus"..
"\nemptying the NPC's storage and using the quest item for"..
"\nother purposes. On the other hand, quest items may get "..
"\nlost, so the player needs a way to repeat each step."..
"\n1200 seconds may be a good value here as well."),
"]",
-- ..and what the NPC will explain in such a case
"tooltip[1.2,3.9;19.6,2.5;",
minetest.formspec_escape(
"This is what the NPC will say next when the player"..
"\nwants to repeat the action too soon for the NPC's"..
"\ntaste - after all the NPC does not have infinite "..
"\ninventory ressources, and the player may abuse the "..
"\nquest item for entirely diffrent purposes.."),
"]",
"container[0.0,3.2]",
-- this will lead back to the same dialog
"label[0.4,0.4;The NPC will explain his unwillingness to repeat the "..
"action so soon ",
yl_speak_up.show_colored_dialog_text(
dialog,
{alternate_text = (timer_data[ "alternate_text" ]
or yl_speak_up.standard_text_if_action_repeated_too_soon)},
d_id, -- show the same dialog again
"1.2,0.7;19.6,2.5;d_text_next",
"with the following text",
":]",
"button_edit_limit_action_success_repeat"),
"container_end[]"
}, "")
-- show what happens if the action was successful
else
-- no action defined
if(count_actions == 0) then
-- do not show tabheader
action_text = action_text..
"label[0.4,0.6;"..
"There is no (A)ction defined. Directly apply the following (Ef)fects:]"
else
-- allow to switch between successful and failed actions
action_text = table.concat({action_text,
tab_list,
";1;true;true]",
"label[0.4,0.6;",
"If the player completed the above action successfully, "..
"apply the following (Ef)fects:]"
}, "")
end
action_text = table.concat({action_text,
-- list of effects
"tablecolumns[text;color,span=1;text;text]",
"table[1.2,0.9;19.6,2.0;table_of_effects;",
list_of_effects,
";0]",
"tooltip[1.2,0.9;19.6,2.0;"..
"*All* (Ef)fects are executed after the action (if there is\n"..
"one defined in this option) has been completed successfully\n"..
"by the player. If there is no action defined, then the\n"..
"(Ef)fects will always be executed when this option here is\n"..
"selected.\n"..
"Please click on an (Ef)fect in order to edit or delete it!]",
"container[0.0,3.2]",
"label[0.4,0.4;The NPC will react to this answer with dialog:]"
}, "")
if(d_id == "d_trade") then
action_text = action_text..
"label[13.5,0.4;..by showing his trade list.]"..
"container_end[]"
else
action_text = table.concat({action_text,
-- allow to change the target dialog via a dropdown menu
"dropdown[11,0.0;9.8,0.7;d_id_",
minetest.formspec_escape(o_id),
";",
dialog_list,
";",
dialog_selected,
",]",
"tooltip[10.2,0.0;3.0,0.7;Select the target dialog with which the NPC shall react "..
"to this answer.\nCurrently, dialog \"",
minetest.formspec_escape(target_dialog),
"\" is beeing displayed.;#FFFFFF;#000000]",
-- ..and what the NPC will reply to that answer
"tooltip[1.2,0.7;19.6,2.5;This is what the NPC will say next when the player has "..
"selected this answer here.]",
yl_speak_up.show_colored_dialog_text(
dialog,
-- this is either the "dialog" effect or an empty fallback
target_effect,
-- this is the text the NPC will say in reaction to this answer
target_dialog,
"1.2,0.7;19.6,2.5;d_text",
"label[13.5,0.4;with the following *modified* text:]",
"",
"button_edit_action_success_dialog"),
"container_end[]"
}, "")
end
end
action_text = action_text.."container_end[]"
-- build up the formspec
local formspec = table.concat({
"size[22,22]",
"bgcolor[#00000000;false]",
-- button back to the current dialog (of which this is an option)
"button[16.4,0.2;5.0,0.9;show_current_dialog;Back to dialog ",
minetest.formspec_escape(d_id),
"]",
"tooltip[show_current_dialog;Go back to dialog ",
minetest.formspec_escape(d_id),
" and continue editing that dialog.]",
-- tell the player what this formspec is about
"label[6.5,0.4;You are editing dialog option \"",
tostring(o_id),
"\":]",
-- the text the NPC says
"container[0.0,0.9]",
"label[0.2,0.0;NPC says ",
minetest.formspec_escape("[dialog \""..tostring(d_id).."\"]:"),
"]",
yl_speak_up.show_colored_dialog_text(
dialog,
{r_id = "", r_type = "dialog"},
d_id,
"1.2,0.3;20.2,2.5;d_text",
"", -- no modifications possible at this step
"",
""), -- no edit button here as this text cannot be changed here
"tooltip[1.2,0.3;20.2,3.0;This is what the NPC says to the player.]",
"container_end[]",
"container[0.0,3.9]",
quest_step_text,
"container_end[]",
-- list the preconditions
"container[0.0,5.4]",
"label[0.2,0.0;If all of the following pre(C)onditions are fulfilled:]",
"tablecolumns[text;color,span=1;text;text]",
"table[1.2,0.3;20.2,2.0;table_of_preconditions;",
list_of_preconditions,
";0]",
"tooltip[1.2,0.3;20.2,2.0;",
"*All* pre(C)onditions need to be true in order\n"..
"for the option to be offered to the player.\n"..
"Please click on a pre(C)ondition in order\n"..
"to edit or delete it!]",
"container_end[]",
-- answer of the player (the actual option)
answer_text,
-- list of (A)ctions (there can only be one per option; i.e. a trade)
action_list_text,
-- list effects and target dialog for successful - and target dialog for unsuccessful
-- actions (including a toggle button)
action_text,
-- container for the buttons/footer
"container[0.0,20.9]",
-- button: delete
"button[0.2,0.0;2.0,0.9;del_option;Delete]",
"tooltip[del_option;Delete this option/answer.]",
-- button: add new
button_add,
-- button: save
"button[4.6,0.0;2.0,0.9;save_option;Save]",
"tooltip[save_option;Save what you canged (or discard it).]",
-- button: prev/next
button_prev,
button_next,
-- button: go back to dialog (repeated from top of the page)
"button[15.8,0.0;5.0,0.9;show_current_dialog;Back to dialog ",
minetest.formspec_escape(d_id),
"]",
"tooltip[show_current_dialog;Go back to dialog ",
minetest.formspec_escape(d_id),
" and continue editing that dialog.]",
-- allow to enter o_sort
"label[10.1,0.5;Sort:]",
"field[11.1,0.0;1.0,0.9;edit_option_o_sort;;",
minetest.formspec_escape(d_option.o_sort),
"]",
"tooltip[edit_option_o_sort;o_sort: The lower the number, the higher up in the "..
"list this option goes\nNegative values are ignored;#FFFFFF;#000000]",
"container_end[]"
}, "")
return formspec
end
yl_speak_up.get_fs_edit_option_dialog_wrapper = function(player, param)
if(not(param)) then
param = {}
end
local pname = player:get_player_name()
yl_speak_up.speak_to[pname].o_id = param.o_id
return yl_speak_up.get_fs_edit_option_dialog(player, param.n_id, param.d_id, param.o_id, param.caller)
end
yl_speak_up.register_fs("edit_option_dialog",
yl_speak_up.input_edit_option_dialog,
yl_speak_up.get_fs_edit_option_dialog_wrapper,
-- no special formspec required:
nil
)

View File

@ -1,377 +0,0 @@
-- This file contains what is necessary to add/edit a precondition.
--
-- Which diffrent types of preconditions are available?
-- -> The following fields are part of a precondition:
-- p_id the ID/key of the precondition/prerequirement
-- p_type selected from values_what
-- p_value used to store the subtype of p_type
--
-- a state/variable ("state"):
-- p_variable name of a variable the player has read access to;
-- dropdown list with allowed options
-- p_operator selected from values_operator
-- p_var_cmp_value can be set freely by the player
--
-- the value of a property of the NPC (for generic NPC) ("property"):
-- p_value name of the property that shall be checked
-- p_operator operator for cheking the property against p_expected_val
-- p_var_cmp_value the expected value of the property
--
-- something that has to be calculated or evaluated (=call a function) ("evaluate"):
-- p_value the name of the function that is to be called
-- p_param1 the first paramter (optional; depends on function)
-- ..
-- p_param9 the 9th parameter (optional; depends on function)
-- p_operator operator for checking the result
-- p_var_cmp_value compare the result of the function with this value
--
-- a block in the world ("block"):
-- p_pos a position in the world; determined by asking the player
-- to punch the block
-- p_node (follows from p_pos)
-- p_param2 (follows from p_pos)
--
-- a trade defined as an action ("trade"): no variables needed (buy and pay stack
-- follow from the trade set as action)
--
-- an inventory: ("player_inv", "npc_inv" or "block_inv")
-- p_itemstack an itemstack; needs to be a minetest.registered_item[..];
-- size/count is also checked
--
-- the inventory of a block on the map: ("block_inv", in addition to the ones above)
-- p_pos a position in the world; determined by asking the player
-- to punch the block
-- p_inv_list_name name of the inventory list of the block
--
-- the player offered/gave the NPC an item: ("player_offered_item"):
-- p_value an itemstack; needs to be a minetest.registered_item[..];
-- size/count is checked for some subtypes
-- p_match_stack_size does the NPC expect exactly one stack size - or is
-- more or less etc. also ok?
-- p_item_group are items of this group instead of the exact item name
-- also acceptable?
-- p_item_desc the description of the itemstack (set by another quest NPC
-- so that the player can distinguish it from other itemstacks
-- with the same item name; see action "npc_gives")
-- p_item_quest_id Special ID to make sure that it is really the *right*
-- item and not just something the player faked with an
-- engraving table or something similar
--
-- a function ("function"): requires npc_master to create and edit
-- p_value the lua code to execute and evaulate
--
-- depends on another option:
-- p_value name of the other option of this dialog that is considered
-- p_fulfilled shall option p_value be true or false?
--
-- the type of the entity of the NPC:
-- p_value name of the entity (i.e. npc_talk:talking_npc)
-- some helper lists for creating the formspecs and evaulating
-- the player's answers:
-- general direction of what a prerequirement may be about
local check_what = {
"- please select -",
"an internal state (i.e. of a quest)", -- 2
"the value of a property of the NPC (for generic NPC)",
"something that has to be calculated or evaluated (=call a function)",
"a block somewhere", -- 3
"a trade", -- 4
"the inventory of the player", -- 5
"the inventory of the NPC", -- 6
"the inventory of a block somewhere", -- 7
"an item the player offered/gave to the NPC", -- 8
"execute Lua code (requires npc_master priv)", -- 7 -> 9
"The preconditions of another dialog option are fulfilled/not fulfilled.", -- 9 -> 11
"nothing - always true (useful for generic dialogs)",
"nothing - always false (useful for temporally deactivating an option)",
"the type of the entity of the NPC",
}
-- how to store these as p_type in the precondition:
local values_what = {"", "state", "property", "evaluate", "block", "trade",
"player_inv", "npc_inv", "block_inv",
"player_offered_item",
-- "function" requires npc_master priv:
"function",
-- depends on the preconditions of another option
"other",
"true", "false",
"entity_type"}
-- options for "a trade"
local check_trade = {
"- please select -",
"The NPC has the item(s) he wants to sell in his inventory.", -- 2
"The player has the item(s) needed to pay the price.", -- 3
"The NPC ran out of stock.", -- 4
"The player cannot afford the price.", -- 5
}
-- how to store these as p_value:
local values_trade = {"", "npc_can_sell", "player_can_buy", "npc_is_out_of_stock", "player_has_not_enough"}
-- options for "the inventory of " (either player or NPC; perhaps blocks later on)
local check_inv = {
"- please select -",
"The inventory contains the following item:",
"The inventory *does not* contain the following item:",
"There is room for the following item in the inventory:",
"The inventory is empty.",
}
-- how to store these as p_value (the actual itemstack gets stored as p_itemstack):
local values_inv = {"", "inv_contains", "inv_does_not_contain", "has_room_for", "inv_is_empty"}
local check_block = {
"- please select -",
"The block is as it is now.",
"There shall be air instead of this block.",
"The block is diffrent from how it is now.",
"I can't punch it. The block is as the block *above* the one I punched.",
}
-- how to store these as p_value (the actual node data gets stored as p_node, p_param2 and p_pos):
-- Note: "node_is_like" occours twice because it is used to cover blocks that
-- cannot be punched as well as normal blocks.
local values_block = {"", "node_is_like", "node_is_air", "node_is_diffrent_from", "node_is_like"}
-- comparison operators for variables
local check_operator = {
"- please select -", -- 1
"== (is equal)", -- 2
"~= (is not equal)", -- 3
">= (is greater or equal)", -- 4
"> (is greater)", -- 5
"<= (is smaller or equal)", -- 6
"< (is smaller)", -- 7
"not (logically invert)", -- 8
"is_set (has a value)", -- 9
"is_unset (has no value)", -- 10
"more than x seconds ago", -- 11
"less than x seconds ago", -- 12
"has completed quest step", -- 13
"quest step *not* completed", -- 14
}
-- how to store these as p_value (the actual variable is stored in p_variable, and the value in p_cmp_value):
local values_operator = {"", "==", "~=", ">=", ">", "<=", "<", "not", "is_set", "is_unset",
"more_than_x_seconds_ago","less_than_x_seconds_ago",
"quest_step_done", "quest_step_not_done"}
-- get the list of variables the player has read access to
yl_speak_up.get_sorted_player_var_list_read_access = function(pname)
local var_list = {}
-- copy the values that are server-specific
for i, v in ipairs(yl_speak_up.custom_server_functions.precondition_descriptions) do
table.insert(var_list, v)
end
-- get the list of variables the player can read
local tmp = yl_speak_up.get_quest_variables_with_read_access(pname)
-- sort that list (the dropdown formspec element returns just an index)
table.sort(tmp)
for i, v in ipairs(tmp) do
table.insert(var_list, v)
end
return var_list
end
-- returns a human-readable text as description of the precondition
-- (as shown in the edit options dialog and in the edit precondition formspec)
yl_speak_up.show_precondition = function(p, pname)
if(not(p.p_type) or p.p_type == "") then
return "(nothing): Always true."
elseif(p.p_type == "item") then
return "item: The player has \""..tostring(p.p_value).."\" in his inventory."
elseif(p.p_type == "quest") then
return "quest: Always false."
elseif(p.p_type == "auto") then
return "auto: Always true."
elseif(p.p_type == "true") then
return "true: Always true."
elseif(p.p_type == "false") then
return "false: Always false."
elseif(p.p_type == "function") then
return "function: evaluate "..tostring(p.p_value)
elseif(p.p_type == "state") then
local var_name = "VALUE_OF[ - ? - ]"
if(p.p_variable) then
var_name = "VALUE_OF[ "..tostring(
yl_speak_up.strip_pname_from_var(p.p_variable, pname)).." ]"
end
if(not(p.p_operator)) then
return "Error: Operator not defined."
elseif(p.p_operator == "not") then
return "not( "..var_name.." )"
elseif(p.p_operator == "is_set") then
return var_name.." ~= nil (is_set)"
elseif(p.p_operator == "is_unset") then
return var_name.." == nil (is_unset)"
elseif(p.p_operator == "more_than_x_seconds_ago") then
return var_name.." was set to current time "..
"*more* than "..tostring(p.p_var_cmp_value).." seconds ago"
elseif(p.p_operator == "less_than_x_seconds_ago") then
return var_name.." was set to current time "..
"*less* than "..tostring(p.p_var_cmp_value).." seconds ago"
elseif(p.p_operator == "quest_step_done") then
return var_name.." shows: player completed quest step \""..
tostring(p.p_var_cmp_value).."\" successfully"
elseif(p.p_operator == "quest_step_not_done") then
return var_name.." shows: player has not yet completed quest step \""..
tostring(p.p_var_cmp_value).."\""
end
if(p.p_var_cmp_value == "") then
return var_name.." "..tostring(p.p_operator).." \"\""
end
return var_name.." "..tostring(p.p_operator).." "..
tostring(p.p_var_cmp_value)
elseif(p.p_type == "property") then
local i = math.max(1,table.indexof(values_operator, p.p_operator))
return tostring(p.p_value)..
" "..tostring(check_operator[i])..
" "..tostring(p.p_var_cmp_value)
elseif(p.p_type == "evaluate") then
local str = ""
for i = 1, 9 do
str = str..tostring(p["p_param"..tostring(i)])
if(i < 9) then
str = str..","
end
end
local i_op = math.max(1,table.indexof(values_operator, p.p_operator))
return "FUNCTION["..tostring(p.p_value).."]"..
"("..str..") "..tostring(check_operator[i_op])..
" "..tostring(p.p_var_cmp_value)
elseif(p.p_type == "block") then
if(not(p.p_pos) or type(p.p_pos) ~= "table"
or not(p.p_pos.x) or not(p.p_pos.y) or not(p.p_pos.z)) then
return "ERROR: p.p_pos is "..minetest.serialize(p.p_pos)
elseif(p.p_value == "node_is_like") then
return "The block at "..minetest.pos_to_string(p.p_pos).." is \""..
tostring(p.p_node).."\" with param2: "..tostring(p.p_param2).."."
elseif(p.p_value == "node_is_air") then
return "There is no block at "..minetest.pos_to_string(p.p_pos).."."
elseif(p.p_value == "node_is_diffrent_from") then
return "There is another block than \""..tostring(p.p_node).."\" at "..
minetest.pos_to_string(p.p_pos)..", or it is at least "..
"rotated diffrently (param2 is not "..tostring(p.p_param2)..")."
end
elseif(p.p_type == "trade") then
local nr = table.indexof(values_trade, p.p_value)
if(nr and check_trade[ nr ]) then
return check_trade[ nr ]
end
elseif(p.p_type == "player_inv" or p.p_type == "npc_inv" or p.p_type == "block_inv") then
local who = "The player"
local what = "\""..tostring(p.p_itemstack).."\" in his inventory."
if(p.p_type == "npc_inv") then
who = "The NPC"
elseif(p.p_type == "block_inv") then
if(not(p.p_pos) or type(p.p_pos) ~= "table"
or not(p.p_pos.x) or not(p.p_pos.y) or not(p.p_pos.z)) then
return "ERROR: p.p_pos is "..minetest.serialize(p.p_pos)
end
who = "The block at "..minetest.pos_to_string(p.p_pos)
what = "\""..tostring(p.p_itemstack).."\" in inventory list \""..
tostring(p.p_inv_list_name).."\"."
end
if(p.p_value == "inv_contains") then
return who.." has "..what
elseif(p.p_value == "inv_does_not_contain") then
return who.." does not have "..what
elseif(p.p_value == "has_room_for") then
return who.." has room for "..what
elseif(p.p_value == "inv_is_empty") then
if(p.p_type == "block_inv") then
return who.." has an empty inventory list \""..
tostring(p.p_inv_list_name).."\"."
end
return who.." has an empty inventory."
end
elseif(p.p_type == "player_offered_item") then
local item = tostring(p.p_value:split(" ")[1])
local amount = tostring(p.p_value:split(" ")[2])
local match = "any amount"
if(p.p_match_stack_size == "any") then
match = "any amount"
elseif(p.p_match_stack_size == "exactly") then
match = "exactly "..tostring(amount)
elseif(p.p_match_stack_size == "less"
or p.p_match_stack_size == "more") then
match = p.p_match_stack_size.." than "..tostring(amount)
elseif(p.p_match_stack_size == "another") then
match = "another amount than " ..tostring(amount)
end
if(p.p_item_group and p.p_item_group ~= "") then
return "The player offered "..tostring(match).." item(s) of the group \""..
tostring(item).."\"."
elseif((p.p_item_quest_id and p.p_item_quest_id ~= "")
or (p.p_item_desc and p.p_item_desc ~= "")) then
return "The player offered "..tostring(match).." of \""..
tostring(p.p_item_desc or "- default description -")..
"\" (\""..tostring(item or "- ? -").."\") "..
"with ID \""..tostring(p.p_item_quest_id or "- no special ID -").."\"."
else
return "The player offered "..tostring(match).." of \""..tostring(item).."\"."
end
elseif(p.p_type == "other") then
local fulfilled = "fulfilled"
if(not(p.p_fulfilled) or p.p_fulfilled ~= "true") then
fulfilled = "*not* fulfilled"
end
return "The preconditions for dialog option \""..tostring(p.p_value).."\" are "..
fulfilled.."."
elseif(p.p_type == "entity_tpye") then
return "the type of the entity of the NPC is: \""..tostring(p.p_value).."\"."
end
-- fallback
return tostring(p.p_value)
end
-- these are only wrapper functions for those in fs_edit_general.lua
yl_speak_up.input_edit_preconditions = function(player, formname, fields)
return yl_speak_up.handle_input_fs_edit_option_related(player, formname, fields,
"p_", "o_prerequisites", yl_speak_up.max_prerequirements,
"pre(C)ondition", "tmp_prereq",
"Please punch the block you want to check in your precondition!",
values_what, values_operator, values_block, values_trade, values_inv,
check_what, check_operator, check_block, check_trade, check_inv,
-- player variables with read access
yl_speak_up.get_sorted_player_var_list_read_access,
"edit_preconditions"
)
end
yl_speak_up.get_fs_edit_preconditions = function(player, table_click_result)
return yl_speak_up.build_fs_edit_option_related(player, table_click_result,
"p_", "o_prerequisites", yl_speak_up.max_prerequirements,
"pre(C)ondition", "tmp_prereq",
"What do you want to check in this precondition?",
values_what, values_operator, values_block, values_trade, values_inv,
check_what, check_operator, check_block, check_trade, check_inv,
-- player variables with read access
yl_speak_up.get_sorted_player_var_list_read_access,
-- show one precondition element
yl_speak_up.show_precondition,
"table_of_preconditions",
"The following expression shall be true:", "Operator:",
"Value to compare with (in some cases parameter):",
"The following shall be true about the block:"
)
end
yl_speak_up.register_fs("edit_preconditions",
yl_speak_up.input_edit_preconditions,
yl_speak_up.get_fs_edit_preconditions,
-- no special formspec required:
nil
)

View File

@ -1,266 +0,0 @@
-- ###
-- Fashion
-- ###
-- normal skins for NPC - without wielded items or capes etc.
yl_speak_up.input_fashion = function(player, formname, fields)
if formname ~= "yl_speak_up:fashion" then
return
end
local pname = player:get_player_name()
local n_id = yl_speak_up.speak_to[pname].n_id
-- is the player editing this npc? if not: abort
if(not(yl_speak_up.edit_mode[pname])
or (yl_speak_up.edit_mode[pname] ~= n_id)) then
return ""
end
-- catch ESC as well
if(not(fields) or (fields.quit or fields.button_cancel or fields.button_exit)) then
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
end
-- which texture from the textures list are we talking about?
-- this depends on the model!
local mesh = yl_speak_up.get_mesh(pname)
local texture_index = yl_speak_up.mesh_data[mesh].texture_index
if(not(texture_index)) then
texture_index = 1
end
-- show extra formspec with wielded item configuration and cape setup
if(fields.button_config_wielded_items
and yl_speak_up.mesh_data[mesh].can_show_wielded_items) then
yl_speak_up.show_fs(player, "fashion_extended")
return
end
-- which skins are available? this depends on mob_type
local mob_type = yl_speak_up.get_mob_type(pname)
local skins = yl_speak_up.mob_skins[mob_type]
local textures = yl_speak_up.speak_to[pname].textures
-- fallback if something went wrong (i.e. unkown NPC)
local skin = (textures[texture_index] or "")
if(not(skins)) then
skins = {skin}
end
local skin_index = table.indexof(skins, skin)
if(skin_index == -1) then
skin_index = 1
end
local new_skin = skin
-- switch back to the stored old skin
if(fields.button_old_skin) then
local old_texture = yl_speak_up.speak_to[pname].old_texture
if(old_texture) then
new_skin = old_texture
end
-- store the new skin
elseif(fields.button_store_new_skin) then
yl_speak_up.speak_to[pname].old_texture = skin
-- show previous skin
elseif(fields.button_prev_skin) then
if(skin_index > 1) then
new_skin = skins[skin_index - 1]
else
new_skin = skins[#skins]
end
-- show next skin
elseif(fields.button_next_skin) then
if(skin_index < #skins) then
new_skin = skins[skin_index + 1]
else
new_skin = skins[1]
end
-- set directly via list
elseif(fields.set_skin_normal) then
local new_index = table.indexof(skins, fields.set_skin_normal)
if(new_index ~= -1) then
new_skin = skins[new_index]
end
end
-- if there is a new skin to consider
if(textures[texture_index] ~= new_skin) then
textures[texture_index] = new_skin
yl_speak_up.mesh_update_textures(pname, textures)
end
if(fields.set_animation
and yl_speak_up.mesh_data[mesh]
and yl_speak_up.mesh_data[mesh].animation
and yl_speak_up.mesh_data[mesh].animation[fields.set_animation]
and yl_speak_up.speak_to[pname]
and yl_speak_up.speak_to[pname].obj) then
local obj = yl_speak_up.speak_to[pname].obj
obj:set_animation(yl_speak_up.mesh_data[mesh].animation[fields.set_animation])
-- store the animation so that it can be restored on reload
local entity = obj:get_luaentity()
if(entity) then
entity.yl_speak_up.animation = yl_speak_up.mesh_data[mesh].animation[fields.set_animation]
end
end
if(fields.button_old_skin or fields.button_store_new_skin) then
yl_speak_up.speak_to[pname].old_texture = nil
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
end
yl_speak_up.show_fs(player, "fashion")
end
-- this only sets the *skin*, depending on the mesh of the NPC;
-- capes and wielded items are supported by an extended formspec for those
-- NPC that can handle them
yl_speak_up.get_fs_fashion = function(pname)
-- which texture from the textures list are we talking about?
-- this depends on the model!
local mesh = yl_speak_up.get_mesh(pname)
if(not(mesh) or not(yl_speak_up.mesh_data[mesh])) then
return "size[9,2]label[0,0;Error: Mesh data missing.]"
end
local texture_index = yl_speak_up.mesh_data[mesh].texture_index
if(not(texture_index)) then
texture_index = 1
end
-- which skins are available? this depends on mob_type
local mob_type = yl_speak_up.get_mob_type(pname)
local skins = yl_speak_up.mob_skins[mob_type]
local textures = yl_speak_up.speak_to[pname].textures
local skin = ""
if(textures and textures[texture_index]) then
skin = (textures[texture_index] or "")
end
-- store the old texture so that we can go back to it
local old_texture = yl_speak_up.speak_to[pname].old_texture
if(not(old_texture)) then
yl_speak_up.speak_to[pname].old_texture = skin
old_texture = skin
end
-- fallback if something went wrong
if(not(skins)) then
skins = {old_texture}
end
local button_cancel = "Cancel"
-- is this player editing this particular NPC? then rename the button
if( yl_speak_up.edit_mode[pname]
and yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id) then
button_cancel = "Back"
end
local skin_list = table.concat(skins, ",")
local skin_index = table.indexof(skins, skin)
if(skin_index == -1) then
skin_index = ""
end
local tmp_textures = textures
if(texture_index ~= 1) then
tmp_textures = yl_speak_up.textures2skin(textures)
end
local preview = yl_speak_up.skin_preview_3d(mesh, tmp_textures, "2,1;6,12", "8,1;6,12")
-- local preview = yl_speak_up.mesh_data[mesh].skin_preview(skin)
local formspec = {
"container[0.5,4.0]",
"dropdown[0.75,14.1;16.25,1.5;set_skin_normal;",
skin_list or "",
";",
tostring(skin_index) or "",
"]",
"label[0.75,13.6;The name of this skin is:]",
"button[0.75,0.75;1.2,12;button_prev_skin;<]",
"button[15.75,0.75;1.2,12;button_next_skin;>]",
"tooltip[button_prev_skin;Select previous skin in list.]",
"tooltip[button_next_skin;Select next skin in list.]",
"tooltip[set_skin_normal;Select a skin from the list.]",
preview,
-- we add a special button for setting the skin in the player answer/reply window
}
if(yl_speak_up.mesh_data[mesh].animation
and yl_speak_up.speak_to[pname]
and yl_speak_up.speak_to[pname].obj) then
local anim_list = {}
for k, v in pairs(yl_speak_up.mesh_data[mesh].animation) do
table.insert(anim_list, k)
end
table.sort(anim_list)
-- which animation is the NPC currently running?
local obj = yl_speak_up.speak_to[pname].obj
local curr_anim = obj:get_animation(pname)
local anim = ""
-- does the current animation match any stored one?
for k, v in pairs(yl_speak_up.mesh_data[mesh].animation) do
if(v.x and v.y and curr_anim and curr_anim.x and curr_anim.y
and v.x == curr_anim.x and v.y == curr_anim.y) then
anim = k
end
end
local anim_index = table.indexof(anim_list, anim)
if(anim_index == -1) then
anim_index = "1"
end
table.insert(formspec, "label[0.75,16.4;Do the following animation:]")
table.insert(formspec, "dropdown[0.75,16.9;16.25,1.5;set_animation;")
table.insert(formspec, table.concat(anim_list, ','))
table.insert(formspec, ";")
table.insert(formspec, tostring(anim_index) or "")
table.insert(formspec, "]")
end
table.insert(formspec, "container_end[]")
local left_window = table.concat(formspec, "")
formspec = {}
local h = -0.8
local button_text = "This shall be your new skin. Wear it proudly!"
if(skin == old_texture) then
button_text = "This is your old skin. It is fine. Keep it!"
end
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
"button_store_new_skin",
"The NPC will wear the currently selected skin.",
button_text,
true, nil, nil, nil)
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
"button_old_skin",
"The NPC will wear the skin he wore before you started changing it.",
"On a second throught - Keep your old skin. It was fine.",
(skin ~= old_texture), nil, nil, nil)
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
"button_config_wielded_items",
"What shall the NPC wield, and which cape shall he wear?",
"I'll tell you what you shall wield.",
(yl_speak_up.mesh_data[mesh].can_show_wielded_items),
"You don't know how to show wielded items. Thus, we can't configure them.",
nil, nil)
return yl_speak_up.show_fs_decorated(pname, true, h,
"",
left_window,
table.concat(formspec, ""),
nil,
h)
end
yl_speak_up.get_fs_fashion_wrapper = function(player, param)
local pname = player:get_player_name()
return yl_speak_up.get_fs_fashion(pname)
end
yl_speak_up.register_fs("fashion",
yl_speak_up.input_fashion,
yl_speak_up.get_fs_fashion_wrapper,
-- no special formspec required:
nil
)

View File

@ -1,205 +0,0 @@
-- inspired/derived from the wieldview mod in 3darmor
yl_speak_up.get_wield_texture = function(item)
if(not(item) or not(minetest.registered_items[ item ])) then
return "3d_armor_trans.png"
end
local def = minetest.registered_items[ item ]
if(def.inventory_image ~= "") then
return def.inventory_image
elseif(def.tiles and type(def.tiles[1]) == "string" and def.tiles[1] ~= "") then
return minetest.inventorycube(def.tiles[1])
end
return "3d_armor_trans.png"
end
yl_speak_up.fashion_wield_give_items_back = function(player, pname)
-- move the item back to the player's inventory (if possible)
local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname})
local player_inv = player:get_inventory()
local left_stack = trade_inv:get_stack("wield", 1)
local right_stack = trade_inv:get_stack("wield", 2)
if(left_stack and not(left_stack:is_empty())
and player_inv:add_item("main", left_stack)) then
trade_inv:set_stack("wield", 1, "")
end
if(right_stack and not(right_stack:is_empty())
and player_inv:add_item("main", right_stack)) then
trade_inv:set_stack("wield", 2, "")
end
end
-- set what the NPC shall wield and which cape to wear
yl_speak_up.input_fashion_extended = function(player, formname, fields)
if formname ~= "yl_speak_up:fashion_extended" then
return
end
local pname = player:get_player_name()
local textures = yl_speak_up.speak_to[pname].textures
local n_id = yl_speak_up.speak_to[pname].n_id
-- is the player editing this npc? if not: abort
if(not(yl_speak_up.edit_mode[pname])
or (yl_speak_up.edit_mode[pname] ~= n_id)) then
return ""
end
-- catch ESC as well
if(not(fields)
or (fields.quit or fields.button_cancel or fields.button_exit or fields.button_save)) then
yl_speak_up.fashion_wield_give_items_back(player, pname)
yl_speak_up.show_fs(player, "fashion")
return
elseif(fields.button_wield_left
or fields.button_wield_right) then
local trade_inv = minetest.get_inventory({type="detached", name="yl_speak_up_player_"..pname})
local player_inv = player:get_inventory()
local left_stack = trade_inv:get_stack("wield", 1)
local right_stack = trade_inv:get_stack("wield", 2)
if(left_stack and left_stack:get_name() and fields.button_wield_left) then
textures[4] = yl_speak_up.get_wield_texture(left_stack:get_name())
yl_speak_up.log_change(pname, n_id,
"(fashion) sword changed to "..tostring(fields.set_sword)..".")
end
if(right_stack and right_stack:get_name() and fields.button_wield_right) then
textures[3] = yl_speak_up.get_wield_texture(right_stack:get_name())
yl_speak_up.log_change(pname, n_id,
"(fashion) shield changed to "..tostring(fields.set_shield)..".")
end
yl_speak_up.fashion_wield_give_items_back(player, pname)
-- only change cape if there really is a diffrent one selected
elseif(fields.set_cape and fields.set_cape ~= textures[1]) then
local mob_type = yl_speak_up.get_mob_type(pname)
local capes = yl_speak_up.mob_capes[mob_type] or {}
-- only set the cape if it is part of the list of allowed capes
if(table.indexof(capes, fields.set_cape) ~= -1) then
textures[1] = fields.set_cape
yl_speak_up.log_change(pname, n_id,
"(fashion) cape changed to "..tostring(fields.set_cape)..".")
end
end
if(fields.button_wield_left or fields.button_wield_right or fields.set_cape or fields.button_sve) then
yl_speak_up.fashion_wield_give_items_back(player, pname)
yl_speak_up.mesh_update_textures(pname, textures)
yl_speak_up.show_fs(player, "fashion_extended")
return
end
yl_speak_up.show_fs(player, "fashion")
end
yl_speak_up.get_fs_fashion_extended = function(pname)
-- which texture from the textures list are we talking about?
-- this depends on the model!
local mesh = yl_speak_up.get_mesh(pname)
local texture_index = yl_speak_up.mesh_data[mesh].texture_index
if(not(texture_index)) then
texture_index = 1
end
local textures = yl_speak_up.speak_to[pname].textures
local skin = (textures[texture_index] or "")
-- which skins are available? this depends on mob_type
local mob_type = yl_speak_up.get_mob_type(pname)
local skins = yl_speak_up.mob_skins[mob_type] or {skin}
local capes = yl_speak_up.mob_capes[mob_type] or {}
local cape = "" -- TODO
-- is this player editing this particular NPC? then rename the button
if(not(yl_speak_up.edit_mode[pname])
or yl_speak_up.edit_mode[pname] ~= yl_speak_up.speak_to[pname].n_id) then
return "label[Error. Not in Edit mode!]"
end
-- make sure the cape can be unset again
if(#capes < 1 or capes[1] ~= "") then
table.insert(capes, 1, "")
end
local cape_list = table.concat(capes, ",")
local cape_index = table.indexof(capes, cape)
if(cape_index == -1) then
cape_index = ""
end
local tmp_textures = textures
if(texture_index ~= 1) then
tmp_textures = yl_speak_up.textures2skin(textures)
end
local preview = yl_speak_up.skin_preview_3d(mesh, tmp_textures, "4.7,0.5;5,10", nil)
local button_cancel = "Cancel"
-- is this player editing this particular NPC? then rename the button
if( yl_speak_up.edit_mode[pname]
and yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id) then
button_cancel = "Back"
end
local formspec = {
"size[13.4,15]",
"label[0.3,0.2;Skin: ",
minetest.formspec_escape(skin),
"]",
"label[4.6,0.65;",
yl_speak_up.speak_to[pname].n_id,
"]",
"label[6,0.65;",
(yl_speak_up.speak_to[pname].n_npc or "- nameless -"),
"]",
"dropdown[9.1,0.2;4,0.75;set_cape;",
cape_list, ";", cape_index, "]",
"label[0.3,4.2;Left:]",
"label[9.1,4.2;Right:]",
"field_close_on_enter[set_sword;false]",
"field_close_on_enter[set_shield;false]",
"image[9.1,1;4,2;",
textures[1] or "",
"]", -- Cape
"image[0.3,4.2;4,4;",
textures[4] or "",
"]", -- Sword
"image[9.1,4.2;4,4;",
textures[3] or "",
"]", --textures[3],"]", -- Shield
"tooltip[0.3,4.2;4,4;This is: ",
minetest.formspec_escape(textures[4]),
"]",
"tooltip[9.1,4.2;4,4;This is: ",
minetest.formspec_escape(textures[3]),
"]",
preview or "",
"button[0.3,8.4;3,0.75;button_cancel;"..button_cancel.."]",
"button[10.1,8.4;3,0.75;button_save;Save]",
"list[current_player;main;1.8,10;8,4;]",
-- set wielded items
"label[0.3,9.7;Wield\nleft:]",
"label[12.0,9.7;Wield\nright:]",
"list[detached:yl_speak_up_player_"..tostring(pname)..";wield;0.3,10.5;1,1;]",
"list[detached:yl_speak_up_player_"..tostring(pname)..";wield;12.0,10.5;1,1;1]",
"button[0.3,11.7;1,0.6;button_wield_left;Set]",
"button[12.0,11.7;1,0.6;button_wield_right;Set]",
"tooltip[button_wield_left;Set and store what your NPC shall wield in its left hand.]",
"tooltip[button_wield_right;Set and store what your NPC shall wield in its right hand.]",
}
return table.concat(formspec, "")
end
yl_speak_up.get_fs_fashion_extended_wrapper = function(player, param)
local pname = player:get_player_name()
return yl_speak_up.get_fs_fashion_extended(pname)
end
yl_speak_up.register_fs("fashion_extended",
yl_speak_up.input_fashion_extended,
yl_speak_up.get_fs_fashion_extended_wrapper,
-- no special formspec required:
nil
)

View File

@ -1,93 +0,0 @@
-- this function is called in fs_edit_general.lua when creating preconditions/effects
-- and from fs_manage_variables.lua when the player clicks on a button;
-- input to this formspec is sent to the respective calling functions
-- find out where this variable is used in NPCs
yl_speak_up.fs_get_list_of_usage_of_variable = function(var_name, pname, check_preconditions,
back_button_name, back_button_text, is_internal_var)
-- TODO: check if the player really has read access to this variable
if(not(is_internal_var)) then
var_name = yl_speak_up.restore_complete_var_name(var_name, pname)
end
-- which NPC (might be several) is using this variable?
-- TODO: ..or if the player at least is owner of these NPC or has extended privs
local npc_list = yl_speak_up.get_variable_metadata(var_name, "used_by_npc")
-- list of all relevant preconditions, actions and effects
local res = {}
local count_read = 0
local count_changed = 0
for i, n_id in ipairs(npc_list) do
-- the NPC may not even be loaded
local dialog = yl_speak_up.load_dialog(n_id, false)
if(dialog and dialog.n_dialogs) then
for d_id, d in pairs(dialog.n_dialogs) do
if(d and d.d_options) then
for o_id, o in pairs(d.d_options) do
local p_text = ""
local r_text = ""
local sort_value = 0
if(o and o.o_prerequisites and check_preconditions) then
for p_id, p in pairs(o.o_prerequisites) do
if(p and p.p_type and p.p_type == "state"
and p.p_variable and p.p_variable == var_name) then
p_text = p_text..yl_speak_up.print_as_table_precon(p,pname)
sort_value = (p.p_var_cmp_value or 0)
count_read = count_read + 1
end
end
end
if(o and o.o_results) then
for r_id, r in pairs(o.o_results) do
if(r and r.r_type and r.r_type == "state"
and r.r_variable and r.r_variable == var_name) then
r_text = r_text..yl_speak_up.print_as_table_effect(r,pname)
-- values set in the results are more important than
-- those set in preconditions
sort_value = (r.r_var_cmp_value or 0)
count_changed = count_changed + 1
end
end
end
-- if preconditions or effects apply: show the action as well
if(o and o.actions and (p_text ~= "" or r_text ~= "")) then
for a_id, a in pairs(o.actions) do
-- no need to introduce an a_text; this will follow
-- directly after p_text, and p_text is finished
p_text = p_text..yl_speak_up.print_as_table_action(a, pname)
end
end
yl_speak_up.print_as_table_dialog(p_text, r_text, dialog,
n_id, d_id, o_id, res, o, sort_value)
end
end
end
end
end
local formspec = yl_speak_up.print_as_table_prepare_formspec(res, "table_of_variable_uses",
back_button_name, back_button_text)
table.insert(formspec,
"label[20.0,1.8;"..
minetest.formspec_escape("Variable \""..
minetest.colorize("#FFFF00", tostring(var_name or "- ? -"))..
"\" is used here:").."]")
if(count_read > 0 or count_changed > 0) then
table.insert(formspec,
"label[16.0,31.0;The variable is accessed in "..
minetest.colorize("#FFFF00", tostring(count_read).." pre(C)onditions")..
" and changed in "..
minetest.colorize("#55FF55", tostring(count_changed).." (Ef)fects")..
".]")
elseif(not(is_internal_var)) then
table.insert(formspec,
"button[0.2,30.6;56.6,1.2;delete_unused_variable;"..
minetest.formspec_escape("Delete this unused variable \""..
tostring(var_name or "- ? -")).."\".]")
else
table.insert(formspec,
"label[16.0,31.0;This is an internal variable and cannot be deleted.]")
end
return table.concat(formspec, "\n")
end

View File

@ -1,202 +0,0 @@
-- in addition: set who can edit this npc;
-- add buttons for fashion (skin editing) and properties;
local old_input_fs_initial_config = yl_speak_up.input_fs_initial_config
yl_speak_up.input_fs_initial_config = function(player, formname, fields)
local pname = player:get_player_name()
local n_id = yl_speak_up.speak_to[pname].n_id
if(fields.back_from_error_msg) then
-- no point in showing the formspec or error message again if we did so already
if(not(yl_speak_up.may_edit_npc(player, n_id))) then
return
end
-- show this formspec again
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
if(fields.button_export_dialog) then
yl_speak_up.show_fs(player, "export")
return
end
if(fields.edit_skin and fields.edit_skin ~= "") then
yl_speak_up.show_fs(player, "fashion")
return
end
if(fields.edit_properties and fields.edit_properties ~= "") then
yl_speak_up.show_fs(player, "properties")
return
end
if((not(fields.save_initial_config)
and not(fields.show_nametag)
and not(fields.list_may_edit)
and not(fields.add_may_edit)
and not(fields.delete_may_edit)
) or (fields and fields.exit)) then
local dialog = yl_speak_up.speak_to[pname].dialog
-- unconfigured NPC
if(fields and fields.exit and not(dialog) or not(dialog.n_dialogs)) then
minetest.chat_send_player(pname, "Aborting initial configuration.")
return
end
-- is the player editing the npc? then leaving this config
-- dialog has to lead back to the talk dialog
if(yl_speak_up.edit_mode[pname] == n_id and n_id) then
yl_speak_up.show_fs(player, "talk",
{n_id = n_id, d_id = yl_speak_up.speak_to[pname].d_id})
end
-- else we can quit here
return
end
local error_msg = nil
local dialog = yl_speak_up.speak_to[pname].dialog
local done = false
if(not(yl_speak_up.may_edit_npc(player, n_id))) then
error_msg = "You are not allowed to edit this NPC."
-- want to change who may edit this npc?
-- delete a player from the list of those allowed to edit the NPC
elseif(fields.delete_may_edit and fields.delete_may_edit ~= ""
and fields.list_may_edit and fields.list_may_edit ~= "") then
if(pname ~= yl_speak_up.npc_owner[ n_id ]) then
error_msg = "Only the owner of the NPC\ncan change this."
elseif(not(dialog)) then
error_msg = "Please set a name for your NPC first!"
else
-- actually delete the player from the list
dialog.n_may_edit[ fields.list_may_edit ] = nil
-- show the next entry
yl_speak_up.speak_to[pname].tmp_index = math.max(1,
yl_speak_up.speak_to[pname].tmp_index-1)
end
done = true
-- add a player who may edit this NPC
elseif(fields.add_may_edit_button and fields.add_may_edit and fields.add_may_edit ~= "") then
if(pname ~= yl_speak_up.npc_owner[ n_id ]) then
error_msg = "Only the owner of the NPC\ncan change this."
-- Note: The owner can now add himself as well. This may be useful before transfering
-- ownership of the NPC to an inexperienced new user who might need help.
-- elseif(fields.add_may_edit == pname) then
-- error_msg = "You are already the owner of this NPC!\nNo need to add you extra here."
elseif(not(minetest.check_player_privs(fields.add_may_edit, {interact=true}))) then
error_msg = "Player \""..minetest.formspec_escape(fields.add_may_edit)..
"\" not found."
elseif(not(dialog)) then
error_msg = "Please set a name for the NPC first!"
else
if(not(dialog.n_may_edit)) then
dialog.n_may_edit = {}
end
dialog.n_may_edit[ fields.add_may_edit ] = true
-- jump to the index with this player so that the player sees that he has been added
local tmp_list = yl_speak_up.sort_keys(dialog.n_may_edit, true)
local index = table.indexof(tmp_list, fields.add_may_edit)
if(index and index > 0) then
-- "Add player:" is added before all other names, so +1
yl_speak_up.speak_to[pname].tmp_index = index + 1
end
end
done = true
-- selected a player name in the woy may edit this NPC dropdown?
elseif(fields.list_may_edit and fields.list_may_edit ~= "") then
local tmp_list = yl_speak_up.sort_keys(dialog.n_may_edit, true)
local index = table.indexof(tmp_list, fields.list_may_edit)
if(fields.list_may_edit == "Add player:") then
index = 0
end
if(index and index > -1) then
yl_speak_up.speak_to[pname].tmp_index = index + 1
end
done = true
end
if(error_msg) then
yl_speak_up.show_fs(player, "msg", { input_to = "yl_speak_up:initial_config",
formspec = "size[6,2]"..
"label[0.2,0.0;"..tostring(error_msg).."]"..
"button[2,1.5;1,0.9;back_from_error_msg;Back]"})
return
end
if( fields.add_may_edit and fields.add_may_edit ~= "") then
yl_speak_up.save_dialog(n_id, dialog)
yl_speak_up.log_change(pname, n_id,
"Added to \"may be edited by\": "..tostring(fields.add_may_edit))
elseif(fields.delete_may_edit and fields.delete_may_edit ~= ""
and fields.list_may_edit and fields.list_may_edit ~= "") then
yl_speak_up.save_dialog(n_id, dialog)
yl_speak_up.log_change(pname, n_id,
"Removed from \"may be edited by\": "..tostring(fields.list_may_edit))
elseif(not(done) or fields.save_initial_config) then
return old_input_fs_initial_config(player, formname, fields)
end
-- update display after editing may_edit_npc:
yl_speak_up.show_fs(player, "initial_config",
{n_id = n_id, d_id = yl_speak_up.speak_to[pname].d_id, false})
end
-- initialize the npc without having to use a staff;
-- add option to show, add and delete other players who may edit this npc;
-- add buttons for skin change and editing properties
local old_get_fs_initial_config = yl_speak_up.get_fs_initial_config
yl_speak_up.get_fs_initial_config = function(player, n_id, d_id, is_initial_config, add_formspec)
-- nothing to add if this is the initial configuration
if(is_initial_config) then
return old_get_fs_initial_config(player, n_id, d_id, is_initial_config, nil)
end
local pname = player:get_player_name()
local dialog = yl_speak_up.speak_to[pname].dialog
-- dialog.n_may_edit was a string for a short time in development
if(not(dialog.n_may_edit) or type(dialog.n_may_edit) ~= "table") then
dialog.n_may_edit = {}
end
local table_of_names = dialog.n_may_edit
local may_be_edited_by = {
-- these buttons and formspecs are provided by the editor:
"button[1.0,5.5;4,0.9;edit_skin;Edit Skin]",
"button[6.0,5.5;4,0.9;edit_properties;Edit Properties]",
-- who can edit this NPC?
"label[0.2,4.45;May be\nedited by:]",
-- offer a dropdown list and a text input field for player names for adding
yl_speak_up.create_dropdown_playerlist(player, pname,
table_of_names, yl_speak_up.speak_to[pname].tmp_index,
2.2, 4.3, 0.0, 1.0, "list_may_edit", "player",
"Remove selected\nplayer from list",
"add_may_edit",
"Enter the name of the player whom you\n"..
"want to grant the right to edit your NPC.\n"..
"The player needs at least the npc_talk_owner priv\n"..
"in order to actually edit the NPC.\n"..
"Click on \"Add\" to add the new player.",
"delete_may_edit",
"If you click here, the player will no\n"..
"longer be able to edit your NPC."
)}
if(not(yl_speak_up.speak_to[pname].tmp_index) or yl_speak_up.speak_to[pname].tmp_index < 2) then
table.insert(may_be_edited_by, "button[9.8,4.3;1.0,1.0;add_may_edit_button;Add]")
table.insert(may_be_edited_by, "tooltip[add_may_edit_button;Click here to add the player "..
"listed to the left\nto those who can edit this NPC.]")
end
-- show the formspec to the player
return old_get_fs_initial_config(player, n_id, d_id, is_initial_config, may_be_edited_by)
end
yl_speak_up.register_fs("initial_config",
-- this function has been changed here:
yl_speak_up.input_fs_initial_config,
-- still handled by the wrapper:
yl_speak_up.get_fs_initial_config_wrapper,
-- no special formspec required:
nil
)

View File

@ -1,364 +0,0 @@
-- Imposing an order on the quest steps is...tricky as best as what will
-- be more important to the players will be the order in which the
-- quest steps have to be solved/done - and not an alphabetical order.
-- But we need an order here for a dropdown menu to select each
-- quest step even if it hasn't been assigned any place in the chain
-- of quest steps yet. So - alphabetical order.
yl_speak_up.get_sorted_quest_step_list = function(pname, q_id)
local quest_step_list = {}
if(not(pname) or not(yl_speak_up.speak_to[pname])) then
return {}
end
local q_id = yl_speak_up.speak_to[pname].q_id
if(q_id and yl_speak_up.quests[q_id] and yl_speak_up.quests[q_id].step_data) then
for step_id, v in pairs(yl_speak_up.quests[q_id].step_data) do
table.insert(quest_step_list, step_id)
end
end
table.sort(quest_step_list)
return quest_step_list
end
-- helper functions for yl_speak_up.input_fs_manage_quest_steps(..)
-- returns the index of the new quest step
yl_speak_up.input_fs_manage_quest_steps_add_new_entry = function(pname, entry_name)
local q_id = yl_speak_up.speak_to[pname].q_id
local res = yl_speak_up.quest_step_add_quest_step(pname, q_id, entry_name)
-- might make sense to show the error message somewhere
if(res ~= "OK") then
return res
end
-- the new entry will be somewhere in it
local quest_step_list = yl_speak_up.get_sorted_quest_step_list(pname)
return table.indexof(quest_step_list, entry_name)
end
-- helper functions for yl_speak_up.input_fs_manage_quest_steps(..)
-- returns a text describing if deleting the quest worked
yl_speak_up.input_fs_manage_quest_steps_del_old_entry = function(pname, entry_name)
local q_id = yl_speak_up.speak_to[pname].q_id
return yl_speak_up.quest_step_del_quest_step(pname, q_id, entry_name)
end
-- helper functions for yl_speak_up.input_fs_manage_quest_steps(..)
-- implements all the functions that are specific to managing quest steps and not part of
-- general item management
yl_speak_up.input_fs_manage_quest_steps_check_fields = function(player, formname, fields, quest_step_name, list_of_entries)
local pname = player:get_player_name()
if(not(quest_step_name)) then
quest_step_name = ""
end
--[[ TODO: implement some back button functionality?
if(fields and fields.show_variable) then
yl_speak_up.show_fs(player, "manage_variables", {var_name = quest_name})
return
end
--]]
-- this function didn't have anything to do
return "NOTHING FOUND"
end
-- makes use of yl_speak_up.handle_input_fs_manage_general and is thus pretty short
yl_speak_up.input_fs_manage_quest_steps = function(player, formname, fields)
local pname = player:get_player_name()
-- route diffrently when the task was adding a quest step
if(fields and fields.back
and pname
and yl_speak_up.speak_to[pname].d_id
and yl_speak_up.speak_to[pname].o_id) then
return yl_speak_up.show_fs(player, "edit_option_dialog", {
d_id = yl_speak_up.speak_to[pname].d_id,
o_id = yl_speak_up.speak_to[pname].o_id,
})
end
if(not(fields) or fields.manage_quests or fields.back) then
return yl_speak_up.show_fs(player, "manage_quests")
end
local res = yl_speak_up.player_is_working_on_quest(player)
-- show a particular quest step?
if(yl_speak_up.handle_input_routing_show_a_quest_step(player, formname, fields, "back_from_error_msg", res)) then
return
end
if(res.current_step) then
-- forward input from that formspec...
if((yl_speak_up.speak_to[res.pname].quest_step_mode == "embedded_select")
and (fields.add_from_available
or (fields.add_step and fields.add_quest_step))) then
return yl_speak_up.input_fs_add_quest_steps(player, "yl_speak_up:add_quest_steps", fields)
end
end
local modes = {"add_to_one_needed", "add_to_all_needed",
"insert_after_prev_step", "insert_before_next_step"}
for i, mode in ipairs(modes) do
if(fields[mode] and fields[mode] ~= "") then
-- let that function sort out what to do;
-- yl_speak_up.speak_to[pname].q_id and yl_speak_up.speak_to[pname].quest_step
-- ought to be set to the current quest and step by now
yl_speak_up.speak_to[pname].quest_step_mode = mode
yl_speak_up.show_fs(player, "add_quest_steps")
return
end
end
local quest_step_list = yl_speak_up.get_sorted_quest_step_list(pname)
local res = yl_speak_up.handle_input_fs_manage_general(player, formname, fields,
-- what_is_the_list_about, min_length, max_length, function_add_new_entry,
"quest step", 2, 70,
yl_speak_up.input_fs_manage_quest_steps_add_new_entry,
quest_step_list,
yl_speak_up.input_fs_manage_quest_steps_del_old_entry,
yl_speak_up.input_fs_manage_quest_steps_check_fields)
return true
end
yl_speak_up.get_fs_manage_quest_steps = function(player, param)
-- small helper function
local em = function(text)
return minetest.colorize("#9999FF", text)
end
local res = yl_speak_up.player_is_working_on_quest(player)
if(res.error_msg) then
return yl_speak_up.build_fs_quest_edit_error(res.error_msg, "back")
end
local step_data = res.step_data
local quest_step_list = yl_speak_up.get_sorted_quest_step_list(res.pname)
if(param and param ~= "") then
local index = table.indexof(quest_step_list, param)
yl_speak_up.speak_to[res.pname].tmp_index_general = index + 1
end
local idx = yl_speak_up.speak_to[res.pname].tmp_index_general
if(idx and idx > 1) then
yl_speak_up.speak_to[res.pname].quest_step = quest_step_list[idx - 1]
end
local formspec = {}
table.insert(formspec, "size[30,12]"..
"container[6,0;18.5,12]"..
"label[0.2,1.2;A quest step is a single thing a player may do in a quest - "..
"like talking to an NPC.\n"..
"Usually not all quest steps can be done/solved at all times.]")
local selected = yl_speak_up.build_fs_manage_general(player, param,
formspec, quest_step_list,
"Create quest step",
"Create a new quest step for this quest.",
"quest step",
"Enter the name of the new quest step you want to create.\n"..
"This is an internal text shown only to yourself.\n"..
"Players cannot see the names of quest steps.",
"If you click here, the selected quest step will be deleted.\n"..
"This will only be possible if it's not used anywhere.",
"1.0")
table.insert(formspec, "container_end[]")
if(not(selected) or selected == "" or not(step_data) or not(step_data[selected])) then
formspec = {} -- we start a new one
-- insert a nicely formated list of quest steps
yl_speak_up.speak_to[res.pname].quest_step_mode = "embedded_select"
table.insert(formspec, yl_speak_up.get_fs_add_quest_steps(player, nil))
table.insert(formspec, "container_end[]")
local lists = yl_speak_up.quest_step_get_start_end_unconnected_lists(step_data)
yl_speak_up.get_sub_fs_show_list_in_box(formspec,
"Start steps:", "select_from_start_steps", lists.start_steps,
"0.1", "2.7", "5.6", "4.3", 0, nil, "#AAFFAA",
"The quest begins with this (or one of these) steps.\n"..
"You need at least one start step.",
nil)
yl_speak_up.get_sub_fs_show_list_in_box(formspec,
"Unconnected steps:", "select_from_unconnected_steps", lists.unconnected_steps,
"0.1", "7.0", "5.6", "4.3", 0, nil, "#FFAAAA",
"These steps are not used yet. They are not required\n"..
"by any other step and do not require steps either.\n"..
"Please decide what to do with them!",
nil)
yl_speak_up.get_sub_fs_show_list_in_box(formspec,
"Quest ends with steps:", "select_from_end_steps", lists.end_steps,
"24", "2.7", "5.6", "8.5", "0.1", nil, "#AAFFAA",
"This quest ends with these steps. They are not required\n"..
"by any other steps and have no successor.\n"..
"Your quest needs at least one end step.",
nil)
return table.concat(formspec, "")
end
-- find out the next quest step
local required_for = yl_speak_up.quest_step_required_for(step_data, selected)
-- middle (this quest step)
table.insert(formspec, "container[6,2.7;12,10.8]"..
"label[0.2,0.5;This quest step is named:]"..
"box[0.7,0.7;17,0.7;#000000]"..
"label[0.8,1.1;")
table.insert(formspec, minetest.colorize("#AAFFAA", minetest.formspec_escape(selected)))
table.insert(formspec, "]")
table.insert(formspec, "container_end[]")
-- show the locations where this quest step is set
local c = 0
for where_id, d in pairs(step_data[selected].where or {}) do
c = c + 1
end
if(c > 1) then
table.insert(formspec, "label[6.2,4.5;This quest step can be set in diffrent ways (")
table.insert(formspec, tostring(c))
table.insert(formspec, " ways) by:]")
table.insert(formspec, "scrollbaroptions[max=")
table.insert(formspec, tostring((c-1.5) * 30)) -- 10 units for default 0.1 scroll factor
table.insert(formspec, ";thumbsize=15")
table.insert(formspec, "]")
table.insert(formspec, "scrollbar[23.2,4.7;0.3,5;vertical;scrollbar_where;0.1]")
elseif(c == 1) then
table.insert(formspec, "label[6.2,4.5;This quest step can be set by:]")
else
table.insert(formspec, "label[6.2,4.5;This quest step cannot be set yet.]")
end
table.insert(formspec, "scroll_container[6.2,4.7;17,5;scrollbar_where;vertical;]")
c = 0
for where_id, d in pairs(step_data[selected].where or {}) do
table.insert(formspec, "container[0,")
table.insert(formspec, tostring(0.2 + c * 3.0))
table.insert(formspec, ";17,4]")
table.insert(formspec, "box[0,0;17,2.7;")
if(c%2 == 0) then
table.insert(formspec, "#000000]")
else
table.insert(formspec, "#222222]")
end
-- describe where in the dialog of the NPC or location this quest step shall be set
local s = "This quest step can be set by "
if(c > 0) then
s = "..alternatively, this quest step can be set by "
end
yl_speak_up.quest_step_show_where_set(res.pname, formspec, s, d.n_id, d.d_id, d.o_id,
"#444488", c + 1) --"#513F23", c + 1) --"#a37e45", c + 1)
table.insert(formspec, "container_end[]")
c = c + 1
end
table.insert(formspec, "scroll_container_end[]")
-- left side (previous quest step)
table.insert(formspec, "container[0,0;5.8,13.5]"..
"label[0.2,2.0;"..em("Required previous").."]"..
"label[0.2,2.4;quest step(s):]"..
"style[insert_before_next_step,insert_after_prev_step,"..
"add_to_one_needed,add_to_all_needed;bgcolor=blue;textcolor=yellow]"..
"button[0.1,0.1;5.6,0.8;show_step_list;Show all quest steps]")
yl_speak_up.get_sub_fs_show_list_in_box(formspec,
em("One of").." these quest steps:", "one_step_required",
step_data[selected].one_step_required,
"0.1", "2.7", "5.6", "4.3", 0, nil, "#AAFFAA",
"At least "..em("one of").." these previous quest steps listed here has to be\n"..
"achieved by the player who is trying to solve this quest.\n"..
"Only then can the player try to achieve this current quest\n"..
"step that you are editing here.\n"..
"If this is empty, then it's usually the/a first step of the quest.\n"..
"If there is one entry, then that entry is the previous quest step.\n"..
"If there are multiple entries, then players have alternate ways\n"..
"to achieve this current quest step here.",
"button[4.6,0.0;0.94,0.7;add_to_one_needed;Edit]"..
"tooltip[add_to_one_needed;Add or remove a quest step to this list.]")
yl_speak_up.get_sub_fs_show_list_in_box(formspec,
em("All of").." these quest steps:", "all_steps_required",
step_data[selected].all_steps_required,
"0.1", "7.0", "5.6", "4.3", 0, nil, "#AAFFAA",
"Sometimes there may not be a particular order in which\n"..
"quest steps ought to be solved. Imagine for example getting\n"..
"indigrents for a cake and delivering them to an NPC.\n"..
"The quest steps listed here can be done in "..em("any order")..".\n"..
"They have "..em("all").." to be achieved first in order for this current\n"..
"quest step to become available.\n"..
"Usually this list is empty.",
"button[4.6,0.0;0.94,0.7;add_to_all_needed;Edit]"..
"tooltip[add_to_all_needed;Add or remove a quest step to this list.]")
if( #step_data[selected].one_step_required > 0
or #step_data[selected].all_steps_required > 0) then
if( #step_data[selected].one_step_required
+ #step_data[selected].all_steps_required > 1) then
table.insert(formspec, "style[show_prev_step;bgcolor=red]")
end
table.insert(formspec, "button[5.6,1.7;0.6,7.0;show_prev_step;<]"..
"tooltip[show_prev_step;Show the previous step according to "..
em("your quest logic")..".\n"..
"The button turns "..minetest.colorize("#FF0000", "red")..
" if there is more than one option. In such\na case "..
"the alphabeticly first previous quest step is choosen.\n"..
"The other "..em("< Prev").." button shows the previous step "..
"in\n"..em("alphabetical order")..".]")
end
-- add buttons for inserting steps between this and the prev/next one
if(#step_data[selected].one_step_required <= 1) then
table.insert(formspec,
"button[5.6,8.7;0.6,2.6;insert_after_prev_step;+]"..
"tooltip[insert_after_prev_step;"..
"Insert a new quest step between the "..em("previous step")..
" (shown\n"..
"to the left) and the "..em("current step")..
" (shown in the middle).\n"..
"Note: This only makes sense if there's just one or no\n"..
" previous step.]")
end
table.insert(formspec, "container_end[]")
-- right side (next quest step)
table.insert(formspec, "container[23.8,0;5.8,13.5]"..
"label[0.6,2.0;Achieving this quest step]"..
"label[0.6,2.4;"..em("helps").." the quester to:]"..
"button[0.4,0.1;5.6,0.8;manage_quests;Manage quests]")
yl_speak_up.get_sub_fs_show_list_in_box(formspec,
"get these quest step(s) "..em("next")..":", "next_steps_show",
required_for,
-- the label needs to be moved slightly to the right to make room for the > button
"0.4", "2.7", "5.6", "8.5", "0.1", nil, "#AAFFAA",
"Once the current quest step has been achieved by the\n"..
"player, the player can strive to achieve these next\n"..
"quest step(s).\n"..
"If this is empty, then it's either the/a last step (quest\n"..
"solved!) - or the quest is not properly set up.",
nil)
if(required_for and #required_for > 0) then
if(#required_for > 1) then
table.insert(formspec, "style[show_next_step;bgcolor=red]")
end
table.insert(formspec, "button[0,1.7;0.6,7.0;show_next_step;>]"..
"tooltip[show_next_step;Show the next step according to "..
em("your quest logic")..".\n"..
"The button turns "..minetest.colorize("#FF0000", "red")..
" if there is more than one option. In such\na case "..
"the alphabeticly first next quest step is choosen.\n"..
"The other "..em("Next >").." button shows the next step "..
"in\n"..em("alphabetical order")..".]")
end
if(#required_for <= 1) then
table.insert(formspec,
"button[0,8.7;0.6,2.6;insert_before_next_step;+]"..
"tooltip[insert_before_next_step;"..
"Insert a new quest step between "..em("current step")..
" (shown\nin the middle) "..
"and the "..em("next step").." (shown to the right).\n"..
"Note: This only makes sense if there's just one or no\n"..
" next step.]")
end
table.insert(formspec, "container_end[]")
return table.concat(formspec, "")
end
yl_speak_up.register_fs("manage_quest_steps",
yl_speak_up.input_fs_manage_quest_steps,
yl_speak_up.get_fs_manage_quest_steps,
-- no special formspec required:
nil
)

View File

@ -1,248 +0,0 @@
-- helper functions for yl_speak_up.input_fs_manage_quests(..)
-- returns the index of the new quest
yl_speak_up.fun_input_fs_manage_quests_add_new_entry = function(pname, entry_name)
local res = yl_speak_up.add_quest(pname, entry_name,
"Name of your quest",
"Enter a longer description here for describing the quest "..
"to players who search for one.",
"Enter a short description here describing what the quest is about.",
"Room for comments/notes")
-- might make sense to show the error message somewhere
if(res ~= "OK") then
return res
end
-- the new entry will be somewhere in it
local quest_list = yl_speak_up.get_sorted_quest_list(pname)
return table.indexof(quest_list, entry_name)
end
-- helper functions for yl_speak_up.input_fs_manage_quests(..)
-- returns a text describing if deleting the quest worked
yl_speak_up.fun_input_fs_manage_quests_del_old_entry = function(pname, entry_name)
-- get q_id from entry_name
local q_id = yl_speak_up.get_quest_id_by_var_name(entry_name, pname)
return yl_speak_up.del_quest(q_id, pname)
end
-- helper functions for yl_speak_up.input_fs_manage_quests(..)
-- implements all the functions that are specific to managing quests and not part of
-- general item management
yl_speak_up.fun_input_fs_manage_quests_check_fields = function(player, formname, fields, quest_name, list_of_entries)
local pname = player:get_player_name()
if(not(quest_name)) then
quest_name = ""
end
if(fields and fields.show_variable) then
yl_speak_up.show_fs(player, "manage_variables", {var_name = quest_name})
return
end
-- this function didn't have anything to do
return "NOTHING FOUND"
end
-- makes use of yl_speak_up.handle_input_fs_manage_general and is thus pretty short
yl_speak_up.input_fs_manage_quests = function(player, formname, fields)
local pname = player:get_player_name()
if(fields and fields.manage_quest_steps and fields.manage_quest_steps ~= "") then
-- the quest we're working at is stored in yl_speak_up.speak_to[pname].q_id
yl_speak_up.show_fs(player, "manage_quest_steps")
return
end
-- show and edit NPCs that may contribute
if( fields and fields.edit_npcs) then
return yl_speak_up.show_fs(player, "add_quest_steps", "manage_quest_npcs")
elseif(fields and fields.edit_locations) then
return yl_speak_up.show_fs(player, "add_quest_steps", "manage_quest_locations")
end
-- show a particular quest step from the start/unconnected/end list?
local res = yl_speak_up.player_is_working_on_quest(player)
if(yl_speak_up.speak_to[pname]
and yl_speak_up.speak_to[pname].q_id
and yl_speak_up.handle_input_routing_show_a_quest_step(player, formname, fields, "back", res)) then
return
end
local quest_list = yl_speak_up.get_sorted_quest_list(pname)
local res = yl_speak_up.handle_input_fs_manage_general(player, formname, fields,
-- what_is_the_list_about, min_length, max_length, function_add_new_entry,
"quest", 2, 80,
yl_speak_up.fun_input_fs_manage_quests_add_new_entry,
quest_list,
yl_speak_up.fun_input_fs_manage_quests_del_old_entry,
yl_speak_up.fun_input_fs_manage_quests_check_fields)
return true
end
yl_speak_up.get_fs_manage_quests = function(player, param)
local pname = player:get_player_name()
local quest_list = yl_speak_up.get_sorted_quest_list(pname)
local formspec = {}
if(param and param ~= "") then
local index = table.indexof(quest_list, param)
yl_speak_up.speak_to[pname].tmp_index_general = index + 1
end
table.insert(formspec, "size[30,12]"..
"container[6,0;18.5,12]"..
"label[0.2,1.2;A quest is a linear sequence of quest steps. Quests can "..
"depend on and influence other quests.\n"..
"Progress for each player is stored in a variable. The name of "..
"that variable cannot be changed after creation.]")
-- if(true) then return formspec[1] end -- TODO: temporally disabled for YL
local selected = yl_speak_up.build_fs_manage_general(player, param,
formspec, quest_list,
"Create quest",
"Create a new varialbe with the name\n"..
"you entered in the field to the left.",
"quest",
"Enter the name of the new quest you want to create.\n"..
"You can't change this name afterwards. But you *can*\n"..
"add and change a human readable description later on.",
"If you click here, the selected quest will be deleted.\n"..
"This will only be possible if it's not used anywhere.")
if(not(selected) or selected == "") then
table.insert(formspec, "container_end[]")
return table.concat(formspec, "")
end
local var_name = yl_speak_up.restore_complete_var_name(selected, pname)
local quest = {}
for q_id, data in pairs(yl_speak_up.quests) do
if(data and data.var_name and data.var_name == var_name) then
quest = data
end
end
-- which quest is the player working on? that's important for showing quest steps
if(not(yl_speak_up.speak_to[pname])) then
yl_speak_up.speak_to[pname] = {}
end
yl_speak_up.speak_to[pname].q_id = quest.id
local quest_state_selected = table.indexof({"created","testing","open","official"}, quest.state)
if(quest_state_selected == -1) then
quest_state_selected = 1
end
-- index 1 is "Add variable:"
table.insert(formspec, "button[12,2.15;4.5,0.6;show_variable;Show and edit this variable]")
table.insert(formspec, "scroll_container[0,3;18,8;scr0;vertical;1]")
table.insert(formspec, "button[12,0.15;4.5,0.6;manage_quest_steps;Manage quest steps]")
table.insert(formspec, "label[0.5,0.5;Quest ID:]")
table.insert(formspec, "label[3.5,0.5;"..minetest.formspec_escape(quest.id or "- ? -").."]")
table.insert(formspec, "label[0.5,1.1;State:]")
table.insert(formspec, "dropdown[3.5,0.8;4.0,0.5;quest_state;created,testing,open,official;"..tostring(quest_state_selected).."]")
table.insert(formspec, "label[0.5,1.7;Creator/Owner:]")
table.insert(formspec, "field[3.5,1.4;4.0,0.5;quest_owner;;"..minetest.formspec_escape(quest.owner or "- ? -").."]")
table.insert(formspec, "label[0.5,2.3;Name:]")
table.insert(formspec, "field[3.5,2.0;13.0,0.5;quest_name;;"..minetest.formspec_escape(quest.name or "- ? -").."]")
table.insert(formspec, "label[0.5,2.9;Short Description:]")
table.insert(formspec, "field[3.5,2.6;13.0,0.5;quest_short_desc;;"..minetest.formspec_escape(quest.short_desc or "- ? -").."]")
table.insert(formspec, "label[0.5,3.5;Full Description:]")
table.insert(formspec, "textarea[3.5,3.2;13.0,1.5;quest_desc;;"..minetest.formspec_escape(quest.description or "- ? -").."]")
table.insert(formspec, "label[0.5,5.1;Internal comment:]")
table.insert(formspec, "textarea[3.5,4.8;13.0,1.5;quest_comment;;"..minetest.formspec_escape(quest.comment or "- ? -").."]")
table.insert(formspec, "button[3.5,6.5;4.0,0.8;save_changes;TODO Save changes]")
table.insert(formspec, "scroll_container_end[]")
table.insert(formspec, "container_end[]")
-- TODO: make the content of the fields and textareas more readable (more contrast)
-- TODO: actually process and store changed entries
--[[
-- TODO: entries that are not yet shown:
quest.var_name = var_name -- name of the variable where progress is stored for each player
quest.subquests = {} -- list of other quest_ids that contribute to this quest
-- -> determined from quests.npcs and quests.locations
quest.is_subquest_of = {} -- list of quest_ids this quest contributes to
-- -> determined from quests.npcs and quests.locations
quest.rewards = {} -- list of rewards (item stacks) for this ques
quest.testers = {} -- list of player names that can test the quest
-- -> during the created/testing phase: any player for which
-- quest.var_name is set to a value
quest.solved_by = {} -- list of names of players that solved the quest at least once
--]]
-- left side: quest steps
-- quest.step_data = {}
-- table containing information about a quest step (=key)
-- this may also be information about WHERE a quest step shall take place
table.insert(formspec, "button[0.1,0.1;5.6,0.8;show_step_list;Show all quest steps]")
local lists = yl_speak_up.quest_step_get_start_end_unconnected_lists(quest.step_data or {})
yl_speak_up.get_sub_fs_show_list_in_box(formspec,
"Start steps:", "select_from_start_steps", lists.start_steps,
"0.1", "1.0", "5.6", "3.5", 0, nil, "#AAFFAA",
"The quest begins with this (or one of these) steps.\n"..
"You need at least one start step.",
nil)
yl_speak_up.get_sub_fs_show_list_in_box(formspec,
"Unconnected steps:", "select_from_unconnected_steps", lists.unconnected_steps,
"0.1", "4.5", "5.6", "3.5", 0, nil, "#FFAAAA",
"These steps are not used yet. They are not required\n"..
"by any other step and do not require steps either.\n"..
"Please decide what to do with them!",
nil)
yl_speak_up.get_sub_fs_show_list_in_box(formspec,
"Quest ends with steps:", "select_from_end_steps", lists.end_steps,
"0.1", "8.0", "5.6", "3.5", 0, nil, "#AAFFAA",
"This quest ends with these steps. They are not required\n"..
"by any other steps and have no successor.\n"..
"Your quest needs at least one end step.",
nil)
-- right side:
-- All these values could in theory either be derived from quest.var_name
-- and/or from quest.step_data.where.
-- These lists here are needed regardless of that because they may be used
-- to add NPC/locations/quest items that are *not yet* used but are planned
-- to be used for this quest eventually.
table.insert(formspec, "style[edit_npcs,edit_locations,edit_items;bgcolor=blue;textcolor=yellow]")
-- quest.npcs = {}
-- list of NPC that *may* contribute to this quest (only IDs without leading n_)
-- turn that list into a more readable list of names
local npc_names = {}
for i, id in ipairs(quest.npcs or {}) do
local d = yl_speak_up.npc_list[id] or {}
table.insert(npc_names, "n_"..tostring(id).." "..(d.name or "- unknown -"))
end
yl_speak_up.get_sub_fs_show_list_in_box(formspec,
"NPC that (may) participate:", "select_from_npcs", npc_names,
"24", "1.0", "5.6", "3.5", 0, nil, "#AAAAFF",
"This is a list of NPC that may be relevant for this quest.\n"..
"Add an NPC to this list and then edit the NPC.\n"..
"Now you can set quest steps from this quest in the NPC's options.",
"button[4.6,0.0;0.94,0.7;edit_npcs;Edit]")
-- quest.locations = {}
-- list of locations that *may* contribute to this quest
yl_speak_up.get_sub_fs_show_list_in_box(formspec,
"Locations:", "select_from_locations", quest.locations or {},
"24", "4.5", "5.6", "3.5", 0, nil, "#AAAAFF",
"This is a list of locations that may be relevant for this quest.\n"..
"It works the same way as for the NPC above.",
"button[4.6,0.0;0.94,0.7;edit_locations;Edit]")
-- quest.items = {}
-- data of quest items that *may* be created and/or accepted in this quest
yl_speak_up.get_sub_fs_show_list_in_box(formspec,
"Quest items:", "select_from_quest_items", quest.items or {},
"24", "8.0", "5.6", "3.5", 0, nil, "#FFFFFF",
"This is a list of quest items.\n"..
"Add quest items here in order to use them more easily in your NPC.",
"button[4.6,0.0;0.94,0.7;edit_items;Edit]")
-- store the quest ID so that we know what we're working at
yl_speak_up.speak_to[pname].q_id = quest.id
return table.concat(formspec, "")
end
yl_speak_up.register_fs("manage_quests",
yl_speak_up.input_fs_manage_quests,
yl_speak_up.get_fs_manage_quests,
-- no special formspec required:
nil
)

View File

@ -1,483 +0,0 @@
-- helper functions for yl_speak_up.input_fs_manage_variables(..)
-- returns the index of the new variable
yl_speak_up.fun_input_fs_manage_variables_add_new_entry = function(pname, entry_name)
local res = yl_speak_up.add_quest_variable(pname, entry_name)
if(not(res)) then
return -1
end
local var_list = yl_speak_up.get_quest_variables(pname, true)
-- make names of own variables shorter
yl_speak_up.strip_pname_from_varlist(var_list, pname)
table.sort(var_list)
return table.indexof(var_list, entry_name)
end
-- helper functions for yl_speak_up.input_fs_manage_variables(..)
-- returns a text describing if deleting the variable worked
yl_speak_up.fun_input_fs_manage_variables_del_old_entry = function(pname, entry_name)
-- delete (empty) variable
return yl_speak_up.del_quest_variable(pname, entry_name, nil)
end
-- helper functions for yl_speak_up.input_fs_manage_variables(..)
-- implements all the functions that are specific to managing variables and not part of
-- general item management
yl_speak_up.fun_input_fs_manage_variables_check_fields = function(player, formname, fields, var_name, list_of_entries)
local pname = player:get_player_name()
if(not(var_name)) then
var_name = ""
end
local var_name_with_prefix = yl_speak_up.restore_complete_var_name(var_name, pname)
-- show all stored values for a variable in a table
if(fields and fields.show_stored_values_for_var and var_name) then
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:manage_variables",
formspec = yl_speak_up.fs_show_all_var_values(player, pname, var_name)
})
return
-- show details about a quest (if it is a quest variable)
elseif(fields and fields.show_quest) then
yl_speak_up.show_fs(player, "manage_quests", var_name)
return
-- show where this variable is used
elseif(fields and fields.show_var_usage and fields.show_var_usage ~= "") then
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:manage_variables",
formspec = yl_speak_up.fs_get_list_of_usage_of_variable(
fields.list_of_entries, pname, true,
"back_from_msg",
"Back to manage variables",
-- not an internal variable
false)
})
return
-- enable, disable and list variables in debug mode
elseif(fields and fields.enable_debug_mode and var_name) then
yl_speak_up.set_variable_metadata(var_name, pname, "debug", pname, true)
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:manage_variables",
formspec = "size[10,2]"..
"label[0.2,0.0;Activating debug mode for variable \""..
minetest.colorize("#FFFF00",
minetest.formspec_escape(tostring(var_name)))..
"\".\nYou will now receive a chat message whenever the "..
"variable changes.]"..
"button[1.5,1.5;2,0.9;back_from_msg;Back]"})
return
elseif(fields and fields.disable_debug_mode and var_name) then
yl_speak_up.set_variable_metadata(var_name, pname, "debug", pname, nil)
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:manage_variables",
formspec = "size[10,2]"..
"label[0.2,0.0;Deactivating debug mode for variable \""..
minetest.colorize("#FFFF00",
minetest.formspec_escape(tostring(var_name)))..
"\".\nYou will no longer receive a chat message whenever the "..
"variable changes.]"..
"button[1.5,1.5;2,0.9;back_from_msg;Back]"})
return
elseif(fields and fields.list_debug_mode) then
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:manage_variables",
formspec = "size[10,6]"..
"label[0.2,0.0;You are currently receiving debug information for the "..
"following variables:]"..
"tablecolumns[text]"..
"table[0.8,0.8;8.8,4.0;list_of_variables_in_debug_mode;"..
-- the table entries will already be formspec_escaped
table.concat(yl_speak_up.get_list_of_debugged_variables(pname), ",").."]"..
"button[1.5,5.5;2,0.9;back_from_msg;Back]"})
return
-- a player name was given; the value for that player shall be shown
elseif(fields and fields.show_stored_value_for_player and var_name
and fields.stored_value_for_player and fields.stored_value_for_player ~= "") then
yl_speak_up.show_fs(player, "manage_variables", {var_name = var_name,
for_player = fields.stored_value_for_player})
return
-- change the value for a player (possibly to nil)
elseif(fields and fields.store_new_value_for_player and var_name
and fields.stored_value_for_player and fields.stored_value_for_player ~= "") then
local old_value = yl_speak_up.get_quest_variable_value(
fields.stored_value_for_player, var_name_with_prefix)
yl_speak_up.set_quest_variable_value(fields.stored_value_for_player, var_name_with_prefix,
fields.current_value_for_player)
local new_value = yl_speak_up.get_quest_variable_value(
fields.stored_value_for_player, var_name_with_prefix)
local success_msg = minetest.colorize("#00FF00", "Successfully set variable")
if(new_value ~= fields.current_value_for_player) then
success_msg = minetest.colorize("#FF0000", "FAILED TO set variable")
end
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:manage_variables",
formspec = "size[10,2.5]"..
"label[0.2,0.0;"..success_msg.." \""..
minetest.colorize("#FFFF00",
minetest.formspec_escape(tostring(var_name)))..
"\"\nfor player "..
minetest.formspec_escape(fields.stored_value_for_player)..
"\n(old value: "..
minetest.colorize("#AAAAAA", old_value)..
")\nto new value "..
minetest.colorize("#FFFF00", fields.current_value_for_player)..".]"..
"button[1.5,2.0;2,0.9;back_from_msg;Back]"})
return
-- remove the value for a player (set to nil)
elseif(fields and fields.unset_value_for_player and var_name
and fields.stored_value_for_player and fields.stored_value_for_player ~= "") then
local old_value = yl_speak_up.get_quest_variable_value(
fields.stored_value_for_player, var_name_with_prefix)
yl_speak_up.set_quest_variable_value(fields.stored_value_for_player, var_name_with_prefix, nil)
local new_value = yl_speak_up.get_quest_variable_value(
fields.stored_value_for_player, var_name_with_prefix)
local success_msg = minetest.colorize("#00FF00", "Unset variable")
if(new_value) then
success_msg = minetest.colorize("#FF0000", "FAILED TO unset variable")
end
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:manage_variables",
formspec = "size[10,2]"..
"label[0.2,0.0;"..success_msg.." \""..
minetest.colorize("#FFFF00",
minetest.formspec_escape(tostring(var_name)))..
"\"\nfor player "..
minetest.formspec_escape(fields.stored_value_for_player)..
"\n(old value: "..
minetest.colorize("#AAAAAA", old_value)..
").]"..
"button[1.5,1.5;2,0.9;back_from_msg;Back]"})
return
-- revoke read or write access to a variable
elseif(fields
and ((fields.revoke_player_var_read_access and fields.revoke_player_var_read_access ~= "")
or (fields.revoke_player_var_write_access and fields.revoke_player_var_write_access ~= ""))
and var_name) then
local right = "read"
if(fields.revoke_player_var_write_access and fields.revoke_player_var_write_access ~= "") then
right = "write"
end
-- which player are we talking about?
local selected = yl_speak_up.speak_to[pname]["tmp_index_var_"..right.."_access"]
local pl_with_access = yl_speak_up.get_access_list_for_var(var_name, pname, right.."_access")
local tmp_list = {}
for k, v in pairs(pl_with_access) do
table.insert(tmp_list, k)
end
table.sort(tmp_list)
local grant_to = ""
if(selected > 1) then
grant_to = tmp_list[ selected-1 ]
end
local error_msg = ""
local pl_with_access = yl_speak_up.get_access_list_for_var(var_name, pname, right.."_access")
if(not(grant_to) or grant_to == "") then
error_msg = "For which player do you want to revoke "..right.." access?"
elseif(pname ~= yl_speak_up.npc_owner[ n_id ]
and not(minetest.check_player_privs(pname, {npc_talk_master=true}))) then
error_msg = "Only the owner of the NPC or players with\n"..
"the npc_talk_master priv can change this."
elseif(not(pl_with_access[ grant_to ])) then
error_msg = minetest.formspec_escape(grant_to).." doesn't have "..right..
" access\nto this variable. Nothing changed."
-- try to revoke access
elseif(not(yl_speak_up.manage_access_to_quest_variable(var_name, pname, grant_to,
right.."_access", nil))) then
error_msg = "An internal error occoured."
else
-- not really an error message here...rather a success message
error_msg = "Revoked "..right.." access to variable\n\""..
minetest.formspec_escape(var_name)..
"\"\nfor player "..minetest.formspec_escape(grant_to)..".\n"..
"Note: This will *not* affect existing preconditions/effects!"
end
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:manage_variables",
formspec = "size[6,2]"..
"label[0.2,0.0;"..error_msg.."]"..
"button[1.5,1.5;2,0.9;back_from_msg;Back]"})
return
-- grant read or write access to a variable
elseif(fields
and ((fields.grant_player_var_read_access and fields.grant_player_var_read_access ~= "")
or (fields.grant_player_var_write_access and fields.grant_player_var_write_access ~= ""))
and var_name) then
local right = "read"
if(fields.grant_player_var_write_access and fields.grant_player_var_write_access ~= "") then
right = "write"
end
local grant_to = fields[ "grant_player_var_"..right.."_access"]
local error_msg = ""
local pl_with_access = yl_speak_up.get_access_list_for_var(var_name, pname, right.."_access")
if(pname ~= yl_speak_up.npc_owner[ n_id ]
and not(minetest.check_player_privs(pname, {npc_talk_master=true}))) then
error_msg = "Only the owner of the NPC or players with\n"..
"the npc_talk_master priv can change this."
elseif(grant_to == pname) then
error_msg = "You already have "..right.." access to this variable."
elseif(pl_with_access[ grant_to ]) then
error_msg = minetest.formspec_escape(grant_to).." already has "..right..
" access\nto this variable."
elseif(not(minetest.check_player_privs(grant_to, {interact=true}))) then
error_msg = "Player \""..minetest.formspec_escape(grant_to).."\" not found."
-- try to grant access
elseif(not(yl_speak_up.manage_access_to_quest_variable(var_name, pname, grant_to,
right.."_access", true))) then
error_msg = "An internal error occoured."
else
-- not really an error message here...rather a success message
error_msg = "Granted "..right.." access to variable\n\""..
minetest.formspec_escape(var_name)..
"\"\nto player "..minetest.formspec_escape(grant_to).."."
end
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:manage_variables",
formspec = "size[6,2]"..
"label[0.2,0.0;"..error_msg.."]"..
"button[1.5,1.5;2,0.9;back_from_msg;Back]"})
return
-- the player clicked on a name in the list of players with read or write access
elseif(fields
and ((fields.list_var_read_access and fields.list_var_read_access ~= "")
or (fields.list_var_write_access and fields.list_var_write_access ~= ""))
and var_name) then
local right = "read"
if(fields.list_var_write_access and fields.list_var_write_access ~= "") then
right = "write"
end
local selected = fields[ "list_var_"..right.."_access"]
local pl_with_access = yl_speak_up.get_access_list_for_var(var_name, pname, right.."_access")
local tmp_list = {}
for k, v in pairs(pl_with_access) do
table.insert(tmp_list, k)
end
table.sort(tmp_list)
local index = table.indexof(tmp_list, selected)
if(selected == "Add player:") then
index = 0
end
if(index and index > -1) then
yl_speak_up.speak_to[pname]["tmp_index_var_"..right.."_access"] = index + 1
end
yl_speak_up.show_fs(player, "manage_variables")
return
end
-- this function didn't have anything to do
return "NOTHING FOUND"
end
-- makes use of yl_speak_up.handle_input_fs_manage_general and is thus pretty short
yl_speak_up.input_fs_manage_variables = function(player, formname, fields)
local pname = player:get_player_name()
local list_of_entries = yl_speak_up.get_quest_variables(pname, true)
-- make names of own variables shorter
yl_speak_up.strip_pname_from_varlist(list_of_entries, pname)
table.sort(list_of_entries)
local res = yl_speak_up.handle_input_fs_manage_general(player, formname, fields,
-- what_is_the_list_about, min_length, max_length, function_add_new_entry,
"variable", 2, 30,
yl_speak_up.fun_input_fs_manage_variables_add_new_entry,
list_of_entries,
yl_speak_up.fun_input_fs_manage_variables_del_old_entry,
yl_speak_up.fun_input_fs_manage_variables_check_fields)
end
yl_speak_up.get_fs_manage_variables = function(player, param)
local pname = player:get_player_name()
-- variables owned by the player - including those with write access
local var_list = yl_speak_up.get_quest_variables(pname, true)
-- make names of own variables shorter
yl_speak_up.strip_pname_from_varlist(var_list, pname)
local formspec = {}
if(not(param) or type(param) ~= "table") then
param = {}
end
if(param and type(param) == "table" and param.var_name and param.var_name ~= "") then
local index = table.indexof(var_list, param.var_name)
yl_speak_up.speak_to[pname].tmp_index_general = index + 1
end
table.insert(formspec, "size[18,12]"..
"label[0.2,1.2;Note: Each variable will store a diffrent value for each "..
"player who interacts with the NPC.\n"..
"You can grant read and write access to other players for your "..
"variables so that they can also use them as well.]")
local selected = yl_speak_up.build_fs_manage_general(player, param.var_name,
formspec, var_list,
"Create variable",
"Create a new varialbe with the name\n"..
"you entered in the field to the left.",
"variable",
"Enter the name of the new variable you\n"..
"want to create.",
"If you click here, the selected variable\n"..
"will be deleted.")
if(selected and selected ~= "") then
local k = selected
-- index 1 is "Add variable:"
local pl_with_read_access = yl_speak_up.get_access_list_for_var(k, pname, "read_access")
local pl_with_write_access = yl_speak_up.get_access_list_for_var(k, pname, "write_access")
if(not(yl_speak_up.speak_to[pname].tmp_index_var_read_access)
or yl_speak_up.speak_to[pname].tmp_index_var_read_access == 1) then
yl_speak_up.speak_to[pname].tmp_index_var_read_access = 1
table.insert(formspec, "button[14.6,2.95;1.0,0.6;add_read_access;Add]"..
"tooltip[add_read_access;Grant the player whose name you entered\n"..
"you entered in the field to the left read access\n"..
"to your variable.]")
end
if(not(yl_speak_up.speak_to[pname].tmp_index_var_write_access)
or yl_speak_up.speak_to[pname].tmp_index_var_write_access == 1) then
yl_speak_up.speak_to[pname].tmp_index_var_write_access = 1
table.insert(formspec, "button[14.6,3.95;1.0,0.6;add_write_access;Add]"..
"tooltip[add_write_access;Grant the player whose name you entered\n"..
"you entered in the field to the left *write* access\n"..
"to your variable.]")
end
local list_of_npc_users = "- none -"
local list_of_node_pos_users = "- none -"
-- expand name of variable k again
local k_long = yl_speak_up.add_pname_to_var(k, pname)
-- which npc and which node_pos use this variable? create a list for display
local c1 = 0
local c2 = 0
if(k_long
and yl_speak_up.player_vars[ k_long ]
and yl_speak_up.player_vars[ k_long ][ "$META$"]) then
local npc_users = yl_speak_up.get_variable_metadata(k_long, "used_by_npc")
c1 = #npc_users
if(npc_users and c1 > 0) then
list_of_npc_users = minetest.formspec_escape(table.concat(npc_users, ", "))
end
local node_pos_users = yl_speak_up.get_variable_metadata(k_long, "used_by_node_pos")
c2 = #node_pos_users
if(node_pos_users and c2 > 0) then
list_of_node_pos_users = minetest.formspec_escape(table.concat(
node_pos_users, ", "))
end
end
table.insert(formspec, "button[10.0,10.05;4.0,0.6;list_debug_mode;What am I debugging?]"..
"tooltip[list_debug_mode;"..
"Show for which variables you currently have "..
"\nactivated the debug mode.]")
local debuggers = yl_speak_up.get_variable_metadata(k_long, "debug")
local i = table.indexof(debuggers, pname)
if(i and i > 0) then
table.insert(formspec,
"button[5.0,10.05;4.0,0.6;disable_debug_mode;Deactivate debug mode]"..
"tooltip[disable_debug_mode;"..
"You will no longer receive a chat message "..
"\nwhen this value changes for a player.]")
else
table.insert(formspec,
"button[5.0,10.05;4.0,0.6;enable_debug_mode;Activate debug mode]"..
"tooltip[enable_debug_mode;"..
"You will receive a chat message whenever the value "..
"\nof this variable changes for one player. The debug\n"..
"messages will be sent even after relogin.]")
end
-- checking/changing debug value for one specific player
if(not(param.for_player)) then
param.for_player = ""
end
table.insert(formspec,
"label[0.2,8.05;Show stored value for player:]"..
"field[4.9,7.75;4.0,0.6;stored_value_for_player;;")
table.insert(formspec, minetest.formspec_escape(param.for_player))
table.insert(formspec, "]")
table.insert(formspec,
"button[9.0,7.75;4.5,0.6;show_stored_value_for_player;Show value for this player]"..
"tooltip[stored_value_for_player;Enter the name of the player for which you\n"..
"want to check (or change) the stored value.]"..
"tooltip[show_stored_value_for_player;Click here to read and the current value"..
"\nstored for this player.]")
if(param.for_player and param.for_player ~= "") then
local v = yl_speak_up.get_quest_variable_value(param.for_player, k_long) or ""
table.insert(formspec,
"label[0.2,9.05;Found stored value:]"..
"field[4.9,8.75;4.0,0.6;current_value_for_player;;")
table.insert(formspec, minetest.formspec_escape(v))
table.insert(formspec, "]"..
"tooltip[current_value_for_player;You can see and change the current "..
"value here.]"..
"button[9.0,8.75;4.5,0.6;store_new_value_for_player;"..
"Store this as new value]"..
"tooltip[store_new_value_for_player;"..
"Click here to update the stored value for this player."..
"\nWARNING: Be very careful here and never do this without"..
"\n informing the player about this change!]"..
"button[13.9,8.75;3.0,0.6;unset_value_for_player;"..
"Remove this entry]"..
"tooltip[unset_value_for_player;Click here to delete the entry for this "..
"player.\nSetting the entry to an empty string would not be "..
"the same!]")
end
table.insert(formspec, "button[12.2,2.15;3.0,0.6;show_var_usage;Where is it used?]"..
"tooltip[show_var_usage;Show which NPC use this variable in which context.]"..
-- offer a dropdown list and a text input field for new varialbe names for adding
"label[0.2,3.25;Players with read access to this variable:]")
table.insert(formspec, yl_speak_up.create_dropdown_playerlist(player, pname,
pl_with_read_access,
yl_speak_up.speak_to[pname].tmp_index_var_read_access,
6.9, 2.95, 0.0, 0.6, "list_var_read_access", "player",
"Remove player from list",
"grant_player_var_read_access",
"Enter the name of the player that shall\n"..
"have read access to this variable.",
"revoke_player_var_read_access",
"If you click here, the selected player\n"..
"will no longer be able to add new\n"..
"pre(C)onditions which read your variable."))
table.insert(formspec, "label[0.2,4.25;Players with *write* access to this variable:]")
table.insert(formspec, yl_speak_up.create_dropdown_playerlist(player, pname,
pl_with_write_access,
yl_speak_up.speak_to[pname].tmp_index_var_write_access,
6.9, 3.95, 0.0, 0.6,
"list_var_write_access", "player", "Remove player from list",
"grant_player_var_write_access",
"Enter the name of the player that shall\n"..
"have *write* access to this variable.",
"revoke_player_var_write_access",
"If you click here, the selected player\n"..
"will no longer be able to *write* new\n"..
"values into this variable."))
local var_type = (yl_speak_up.get_variable_metadata(k_long, "var_type")
or "String/text or numerical value, depending on how you use it")
table.insert(formspec, "label[0.2,5.05;Type of variable: ")
table.insert(formspec, minetest.colorize("#FFFF00", var_type)) -- show variable type
table.insert(formspec, ".]")
if(var_type == "quest") then
table.insert(formspec, "button[4.2,4.75;4.5,0.6;show_quest;Show and edit this quest]")
end
table.insert(formspec, "label[0.2,6.05;This variable is used by the following ")
table.insert(formspec,
minetest.colorize("#FFFF00", tostring(c1)).." NPC:\n\t"..
-- those are only n_id - no need to formspec_escape that
minetest.colorize("#FFFF00", list_of_npc_users))
table.insert(formspec, ".]")
table.insert(formspec, "label[0.2,7.05;This variable is used by the following ")
table.insert(formspec,
minetest.colorize("#FFFF00", tostring(c2)).." node positions:\n\t"..
-- those are only pos_to_string(..) - no need to formspec_escape that
minetest.colorize("#FFFF00", list_of_node_pos_users))
table.insert(formspec, ".]")
table.insert(formspec,
"button[0.2,10.05;4.0,0.6;show_stored_values_for_var;Show all stored values]"..
"tooltip[show_stored_values_for_var;A diffrent value can be stored for each "..
"player.\nShow these values in a table.]")
end
return table.concat(formspec, "")
end
yl_speak_up.register_fs("manage_variables",
yl_speak_up.input_fs_manage_variables,
yl_speak_up.get_fs_manage_variables,
-- no special formspec required:
nil
)

View File

@ -1,66 +0,0 @@
yl_speak_up.input_notes = function(player, formname, fields)
if(fields and fields.back) then
return yl_speak_up.show_fs(player, "talk")
elseif(fields and fields.store_notes and fields.notes_text) then
local pname = player:get_player_name()
local dialog = yl_speak_up.speak_to[pname].dialog
local n_id = yl_speak_up.speak_to[pname].n_id
-- update the dialog data the player sees
dialog.d_notes = fields.notes_text
-- actually store/update it on disc as well
local stored_dialog = yl_speak_up.load_dialog(n_id, false)
stored_dialog.d_notes = dialog.d_notes
yl_speak_up.save_dialog(n_id, stored_dialog)
-- log the change
yl_speak_up.log_change(pname, n_id, "Updated notes to: "..tostring(dialog.d_notes))
return yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:notes",
formspec = "size[10,3]"..
"label[0.5,1.0;Notes successfully updated.]"..
"button[3.5,2.0;2,0.9;back_from_error_msg;Back]"
})
else
return yl_speak_up.show_fs(player, "notes")
end
end
yl_speak_up.get_fs_notes = function(player, param)
local pname = player:get_player_name()
local n_id = yl_speak_up.speak_to[pname].n_id
-- generic dialogs are not part of the NPC
local dialog = yl_speak_up.speak_to[pname].dialog
return table.concat({"size[20,20]",
"label[2,0.5;Internal notes on NPC ",
minetest.formspec_escape(n_id or "- ? -"),
", named ",
minetest.formspec_escape(dialog.n_npc) or "- ? -",
"]",
"button[17.8,0.2;2.0,0.9;back;Back]",
"button[15.0,0.2;2.0,0.9;store_notes;Save]",
"textarea[0.2,2;19.6,13;notes_text;Notes (shown only to those who can edit this NPC):;",
minetest.formspec_escape(dialog.d_notes or "Enter text here."),
"]",
"textarea[0.2,15.2;19.6,4.8;;;",
"This can be used to make your NPC more intresting by storing information about..\n"..
"* its character\n"..
"* special characteristics of the NPC\n"..
"* linguistic peculiarities and habits\n"..
"* origin, relationships, lots of lore\n"..
"* friendships / enmities\n"..
"* personal goals / motivations / background\n"..
"* planned quests\n"..
"* trades\n"..
"and whatever else you want to keep notes on for this NPC.]"
})
end
yl_speak_up.register_fs("notes",
yl_speak_up.input_notes,
yl_speak_up.get_fs_notes,
-- no special formspec required:
nil
)

View File

@ -1,139 +0,0 @@
-- allow owner to see and edit properties of the NPC
yl_speak_up.input_properties = function(player, formname, fields)
local pname = player:get_player_name()
if(not(pname) or not(yl_speak_up.speak_to[pname])) then
return
end
local n_id = yl_speak_up.speak_to[pname].n_id
if(fields and fields.back and fields.back ~= "") then
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
local selected_row = nil
if(fields and fields.store_new_val and fields.store_new_val ~= "" and fields.prop_val) then
yl_speak_up.set_npc_property(pname, fields.prop_name, fields.prop_val, "manually")
elseif(fields and fields.delete_prop and fields.delete_prop ~= "") then
-- delete the old property
yl_speak_up.set_npc_property(pname, fields.prop_name, nil, "manually")
elseif(fields and fields.table_of_properties) then
local selected = minetest.explode_table_event(fields.table_of_properties)
if(selected.type == "CHG" or selected.type == "DLC") then
selected_row = selected.row
end
end
yl_speak_up.show_fs(player, "properties", {selected = selected_row})
end
yl_speak_up.get_fs_properties = function(pname, selected)
if(not(pname)) then
return ""
end
local n_id = yl_speak_up.speak_to[pname].n_id
-- is the player editing this npc? if not: abort
if(not(yl_speak_up.edit_mode[pname])
or (yl_speak_up.edit_mode[pname] ~= n_id)) then
return ""
end
-- we want the long version with additional information
local property_data = yl_speak_up.get_npc_properties_long_version(pname, true)
if(not(property_data)) then
-- something went wrong - there really is nothing useful we can do
-- if the NPC we want to interact with doesn't exist or is broken
return
end
local s = ""
if(not(property_data.prop_names)) then
property_data.prop_names = {}
end
local anz_prop = #property_data.prop_names
for i, k in ipairs(property_data.prop_names) do
local v = property_data.properties[k]
s = s.."#BBBBFF,"..minetest.formspec_escape(k)..","..minetest.formspec_escape(v)..","
end
s = s.."#00FF00,add,Add a new property"
if(not(selected) or selected == "") then
selected = -1
end
local add_selected = "label[3.5,6.5;No property selected.]"
selected = tonumber(selected)
if(selected > anz_prop + 1 or selected < 1) then
selected = -1
elseif(selected > anz_prop) then
add_selected = "label[0.2,6.5;Add new property:]"..
"field[3.0,6.5;3.5,1.0;prop_name;;]"..
"label[6.5,6.5;with value:]"..
"field[8.2,6.5;4.5,1.0;prop_val;;]"..
"button[8.2,7.8;2.0,1.0;store_new_val;Store]"
-- external properties can usually only be read but not be changed
elseif(string.sub(property_data.prop_names[selected], 1, 5) == "self."
and not(yl_speak_up.custom_property_handler[property_data.prop_names[selected]])) then
add_selected = "label[3.5,6.5;Properties of the type \"self.\" usually cannot be modified.]"
elseif(string.sub(property_data.prop_names[selected], 1, 6) == "server"
and not(minetest.check_player_privs(pname, {npc_talk_admin=true}))) then
add_selected = "label[3.5,6.5;Properties starting with \"server\" can only be "..
"changed by players\nwho have the \"npc_talk_admin\" priv."
else
local name = property_data.prop_names[selected]
local val = minetest.formspec_escape(property_data.properties[name])
local name_esc = minetest.formspec_escape(name)
add_selected = "label[0.2,6.5;Change property:]"..
"field[3.0,6.5;3.5,1.0;prop_name;;"..name_esc.."]"..
"label[6.5,6.5;to value:]"..
"field[8.2,6.5;4.5,1.0;prop_val;;"..val.."]"..
"button[8.2,7.8;2.0,1.0;store_new_val;Store]"..
"button[10.4,7.8;2.0,1.0;delete_prop;Delete]"
end
if(selected < 1) then
selected = ""
end
local dialog = yl_speak_up.speak_to[pname].dialog
local npc_name = minetest.formspec_escape(dialog.n_npc or "- nameless -")
return table.concat({"size[12.5,8.5]",
"label[2,0;Properties of ",
npc_name,
" (ID: ",
tostring(n_id),
"):]",
"tablecolumns[color,span=1;text;text]",
"table[0.2,0.5;12,4.0;table_of_properties;",
s,
";",
tostring(selected),
"]",
"tooltip[0.2,0.5;12,4.0;",
"Click on \"add\" to add a new property.\n",
"Click on the name of a property to change or delete it.]",
"label[2.0,4.5;"..
"Properties are important for NPC that want to make use of generic dialogs.\n"..
"Properties can be used to determine which generic dialog(s) shall apply to\n"..
"this particular NPC and how they shall be configured. You need the\n"..
"\"npc_talk_admin\" priv to edit properties starting with the text \"server\".]",
"button[5.0,7.8;2.0,0.9;back;Back]",
add_selected
}, "")
end
yl_speak_up.get_fs_properties_wrapper = function(player, param)
if(not(param)) then
param = {}
end
local pname = player:get_player_name()
return yl_speak_up.get_fs_properties(pname, param.selected)
end
yl_speak_up.register_fs("properties",
yl_speak_up.input_properties,
yl_speak_up.get_fs_properties_wrapper,
-- force formspec version 1:
1
)

View File

@ -1,41 +0,0 @@
yl_speak_up.input_quest_gui = function(player, formname, fields)
-- this return value is necessary for custom actions
local ret = {quit = true}
local pname = player:get_player_name()
if(fields and fields.back_from_msg) then
yl_speak_up.show_fs(player, "quest_gui")
return ret
end
-- new variables have to be added (and deleted) somewhere after all
if(fields.manage_variables) then
-- remember which formspec we are comming from
yl_speak_up.speak_to[pname][ "working_at" ] = "quest_gui"
yl_speak_up.show_fs(player, "manage_variables")
return ret
elseif(fields.manage_quests) then
yl_speak_up.speak_to[pname][ "working_at" ] = "quest_gui"
yl_speak_up.show_fs(player, "manage_quests")
return ret
end
-- the calling NPC shall no longer do anything
return ret
end
yl_speak_up.get_fs_quest_gui = function(player, param)
local pname = player:get_player_name()
return "size[24,20]"..
"label[0,0.5;Hi. This is a quest admin gui.]"..
"button[0.2,1.0;4.0,0.6;manage_variables;Manage variables]"..
"button[6.2,1.0;4.0,0.6;manage_quests;Manage quests]"
end
yl_speak_up.register_fs("quest_gui",
yl_speak_up.input_quest_gui,
yl_speak_up.get_fs_quest_gui,
-- no special formspec required:
nil
)

View File

@ -1,147 +0,0 @@
-- when the player is editing the NPC and has changed it without having
-- saved the changes yet: ask what shall be done (save? discard? back?)
yl_speak_up.input_save_or_discard_changes = function(player, formname, fields)
local pname = player:get_player_name()
-- if the player is not even talking to this particular npc
if(not(yl_speak_up.speak_to[pname])) then
return
end
local target_dialog = yl_speak_up.speak_to[pname].target_dialog
if(not(target_dialog)) then
target_dialog = ""
end
local edit_mode = (yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id)
local d_id = yl_speak_up.speak_to[pname].d_id
local n_id = yl_speak_up.speak_to[pname].n_id
local o_id = yl_speak_up.speak_to[pname].o_id
-- the player decided to go back and continue editing the current dialog
if(edit_mode and fields.back_to_dialog_changes) then
-- do NOT clear the list of changes; just show the old dialog again
yl_speak_up.show_fs(player, "show_last_fs", {})
return
-- save changes and continue on to the next dialog
elseif(edit_mode and fields.save_dialog_changes) then
-- actually save the dialog (the one the NPC currently has)
yl_speak_up.save_dialog(n_id, yl_speak_up.speak_to[pname].dialog)
-- log all the changes
for i, c in ipairs(yl_speak_up.npc_was_changed[ n_id ]) do
yl_speak_up.log_change(pname, n_id, c)
end
-- clear list of changes
yl_speak_up.npc_was_changed[ n_id ] = {}
-- save_dialog removed d_dynamic (because that is never to be saved!); we have
-- to add d_dynamic back so that we can use it as a target dialog in further editing:
yl_speak_up.speak_to[pname].dialog.n_dialogs["d_dynamic"] = {}
yl_speak_up.speak_to[pname].dialog.n_dialogs["d_dynamic"].d_options = {}
-- discard changes and continue on to the next dialog
elseif(edit_mode and fields.discard_dialog_changes) then
-- the current dialog and the one we want to show next may both be new dialogs;
-- if we just reload the old state, they would both get lost
local target_dialog_data = yl_speak_up.speak_to[pname].dialog.n_dialogs[ target_dialog ]
-- actually restore the old state and discard the changes by loading the dialog anew
yl_speak_up.speak_to[pname].dialog = yl_speak_up.load_dialog(n_id, false)
-- clear list of changes
yl_speak_up.npc_was_changed[ n_id ] = {}
local dialog = yl_speak_up.speak_to[pname].dialog
-- do we have to save again after restoring current and target dialog?
local need_to_save = false
-- if the current dialog was a new one, it will be gone now - restore it
if(d_id and d_id ~= "" and not(dialog.n_dialogs[ d_id ])) then
-- we can't just restore the current dialog - after all the player wanted
-- to discard the changes; but we can recreate the current dialog so that it
-- is in the "new dialog" state again
local next_id = tonumber(string.sub( d_id, 3))
yl_speak_up.add_new_dialog(dialog, pname, next_id)
yl_speak_up.log_change(pname, n_id, "Saved new dialog "..tostring( d_id )..".")
need_to_save = true
end
if(target_dialog and target_dialog ~= "" and not(dialog.n_dialogs[ target_dialog ])) then
-- restore the new target dialog
dialog.n_dialogs[ target_dialog ] = target_dialog_data
yl_speak_up.log_change(pname, n_id, "Saved new dialog "..tostring( target_dialog )..".")
need_to_save = true
end
if(need_to_save) then
yl_speak_up.save_dialog(n_id, dialog)
end
end
-- are there any changes which might be saved or discarded?
if(edit_mode
and yl_speak_up.npc_was_changed[ n_id ]
and #yl_speak_up.npc_was_changed[ n_id ] > 0) then
yl_speak_up.show_fs(player, "save_or_discard_changes", {})
return
end
yl_speak_up.show_fs(player, "proceed_after_save", {})
end
yl_speak_up.get_fs_save_or_discard_changes = function(player, param)
local pname = player:get_player_name()
local n_id = yl_speak_up.speak_to[pname].n_id
-- TODO
local target_name = "quit"
local target_dialog = nil -- TODO
if(target_dialog and target_dialog ~= "") then
target_name = "go on to dialog "..minetest.formspec_escape(target_dialog)
if(target_dialog == "edit_option_dialog") then
target_name = "edit option \""..
minetest.formspec_escape(tostring(o_id)).."\" of dialog \""..
minetest.formspec_escape(tostring(d_id)).."\""
end
end
yl_speak_up.speak_to[pname].target_dialog = target_dialog
local d_id = yl_speak_up.speak_to[pname].d_id
-- reverse the order of the changes in the log so that newest are topmost
local text = ""
for i,t in ipairs(yl_speak_up.npc_was_changed[ n_id ]) do
text = minetest.formspec_escape(t).."\n"..text
end
-- build a formspec showing the changes to this dialog and ask for save
return table.concat({"size[14,6.2]",
"bgcolor[#00000000;false]",
-- TODO: make this more flexible
"label[0.2,0.2;You are about to leave dialog ",
minetest.formspec_escape(d_id),
" and ",
target_name,
".]",
"label[0.2,0.65;These changes have been applied to dialog ",
minetest.formspec_escape(d_id),
":]",
"hypertext[0.2,1;13.5,4;list_of_changes;<normal>",
minetest.formspec_escape(text),
"\n</normal>",
"]",
"button_exit[1.2,5.2;3,0.9;discard_dialog_changes;Discard changes]",
"button[5.7,5.2;3,0.9;back_to_dialog_changes;Back]",
"button_exit[10.2,5.2;3,0.9;save_dialog_changes;Save changes]",
"tooltip[save_dialog_changes;Save all changes to this dialog and ",
target_name,
".]",
"tooltip[discard_dialog_changes;Undo all changes and ",
target_name,
".]",
"tooltip[back_to_dialog_changes;Go back to dialog ",
minetest.formspec_escape(d_id),
" and continue editing it.]"
}, "")
end
yl_speak_up.register_fs("save_or_discard_changes",
yl_speak_up.input_save_or_discard_changes,
yl_speak_up.get_fs_save_or_discard_changes,
-- no special formspec required:
nil
)

View File

@ -1,64 +0,0 @@
-- called only by mange_variables formspec
yl_speak_up.fs_show_all_var_values = function(player, pname, var_name)
-- wrong parameters? no need to show an error message here
if(not(var_name) or not(pname) or not(player)) then
return ""
end
-- TODO: check if the player really has read access to this variable
var_name = yl_speak_up.restore_complete_var_name(var_name, pname)
-- player names with values as key; normally the player name is the key and
-- the value the value - but that would be a too long list to display, and
-- so we rearrange the array for display here
local players_with_value = {}
-- the diffrent values that exist
local values = {}
local var_data = yl_speak_up.player_vars[ var_name ]
local count_players = 0
for player_name, v in pairs(var_data) do
-- metadata is diffrent and not of relevance here
if(player_name and player_name ~= "$META$" and v) then
if(not(players_with_value[ v ])) then
players_with_value[ v ] = {}
table.insert(values, v)
end
table.insert(players_with_value[ v ], player_name)
count_players = count_players + 1
end
end
-- the values ought to be shown in a sorted way
table.sort(values)
-- construct the lines that shall form the table
local lines = {"#FFFFFF,Value:,#FFFFFF,Players for which this value is stored:"}
for i, v in ipairs(values) do
table.insert(lines,
"#FFFF00,"..minetest.formspec_escape(v)..",#CCCCCC,"..
-- text, prefix, line_length, max_lines
yl_speak_up.wrap_long_lines_for_table(
table.concat(players_with_value[ v ], ", "),
",,,#CCCCCC,", 80, 8))
end
-- true here means: lines are already sorted;
-- ",": don't insert blank lines between entries
local formspec = yl_speak_up.print_as_table_prepare_formspec(lines, "table_of_variable_values",
"back_from_msg", "Back", true, ",",
"color,span=1;text;color,span=1;text") -- the table columns
table.insert(formspec,
"label[18.0,1.8;"..
minetest.formspec_escape("For variable \""..
minetest.colorize("#FFFF00", tostring(var_name or "- ? -"))..
"\", these values are stored:").."]")
if(values and #values > 0) then
table.insert(formspec,
"label[18.0,31.0;The variable holds "..
minetest.colorize("#FFFF00", tostring(#values)).." diffrent values for "..
minetest.colorize("#FFFF00", tostring(count_players)).." diffrent players.]")
else
table.insert(formspec,
"label[18.0,31.0;The variable does not currently hold any stored values.]")
end
return table.concat(formspec, "\n")
end

View File

@ -1,256 +0,0 @@
-- helpful for debugging the content/texts of the created dialog structure
yl_speak_up.input_show_what_points_to_this_dialog = function(player, formname, fields)
local pname = player:get_player_name()
if(fields.back_from_show_what_points_here
or not(fields.turn_dialog_into_alternate_text)) then
-- back to the dialog
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
end
-- fields.turn_dialog_into_alternate_text is set
local n_id = yl_speak_up.speak_to[pname].n_id
local this_dialog = yl_speak_up.speak_to[pname].d_id
local dialog = yl_speak_up.speak_to[pname].dialog
if(not(dialog)
or not(dialog.n_dialogs)
or not(this_dialog)
or not(dialog.n_dialogs[ this_dialog ])) then
return
end
-- only show this information when editing this npc
if(yl_speak_up.edit_mode[pname] ~= yl_speak_up.speak_to[pname].n_id) then
return
end
-- find out what needs to be turned into an alternate dialog
local found = {}
-- how many options does the current dialog have?
local count_options = 0
-- iterate over all dialogs
for d_id, d in pairs(dialog.n_dialogs) do
-- the dialog itself may have options that point back to the dialog itself
if(d.d_options) then
-- iterate over all options
for o_id, o in pairs(d.d_options) do
-- this is only possible if there are no options for this dialog
if(d_id == this_dialog) then
count_options = count_options + 1
end
-- preconditions are not relevant;
-- effects are (dialog and on_failure)
if(o.o_results) then
for r_id, r in pairs(o.o_results) do
if(r and r.r_type and r.r_type == "dialog"
and r.r_value == this_dialog) then
table.insert(found, {d_id=d_id, o_id=o_id, id=r_id,
element=r, text="option was selected"})
elseif(r and r.r_type and r.r_type == "on_failure"
and r.r_value == this_dialog) then
table.insert(found, {d_id=d_id, o_id=o_id, id=r_id,
element=r, text="the previous effect failed"})
end
end
end
-- actions may be relevant
if(o.actions) then
for a_id, a in pairs(o.actions) do
if(a and a.a_on_failure
and a.a_on_failure == this_dialog) then
table.insert(found, {d_id=d_id, o_id=o_id, id=a_id,
element=a, text="action failed"})
end
end
end
end
end
end
local error_msg = ""
if(count_options > 0) then
error_msg = "This dialog still has dialog options.\nConversion not possible."
elseif(#found < 1) then
error_msg = "Found no option, action or effect\nthat points to this dialog."
elseif(#found > 1) then
error_msg = "Found more than one option, action\nor effect that points to this dialog."
end
if(error_msg ~= "") then
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:show_what_points_to_this_dialog",
formspec = "size[8,2]"..
"label[0.2,0.5;Error: "..error_msg.."]"..
"button[1.5,1.5;2,0.9;back_from_error_msg;Back]"})
return
end
-- all fine so far; this is the text we want to set as alternate text
local d_text = dialog.n_dialogs[ this_dialog ].d_text
local f = found[1]
-- are we dealing with a result/effect or an action?
t = "o_results"
if(f.element.a_id) then
t = "actions"
end
-- there may already be an alternate text stored there
local alternate_text = dialog.n_dialogs[ f.d_id ].d_options[ f.o_id ][ t ][ f.id ].alternate_text
if(not(alternate_text)) then
-- no old text found
alternate_text = d_text
else
-- the old text may reference this d_text
alternate_text = string.gsub(alternate_text, "%$TEXT%$", d_text)
end
-- log the change
table.insert(yl_speak_up.npc_was_changed[ n_id ],
"Dialog "..tostring(this_dialog)..": Deleted this dialog and turned it into an "..
"alternate text for dialog \""..tostring(f.d_id).."\" option \""..tostring(f.o_id)..
"\" element \""..tostring(f.id).."\".")
-- actually set the new alternate_text
dialog.n_dialogs[ f.d_id ].d_options[ f.o_id ][ t ][ f.id ].alternate_text = alternate_text
-- delete this dialog
dialog.n_dialogs[ this_dialog ] = nil
-- we need to show a new/diffrent dialog to the player now - because the old one was deleted
yl_speak_up.speak_to[pname].d_id = f.d_id
yl_speak_up.speak_to[pname].o_id = f.o_id
if(t == "o_results") then
-- we can't really know where this ought to point to - the old dialog is gone, so
-- let's point to the current dialog to avoid errors
dialog.n_dialogs[ f.d_id ].d_options[ f.o_id ][ t ][ f.id ].r_value = f.d_id
-- dialog - normal switching to the next dialog or on_failure - the previous effect failed:
yl_speak_up.show_fs(player, "edit_effects", f.id)
else
-- the player may have to change this manually; we really can't know what would fit
-- (but the old dialog is gone)
dialog.n_dialogs[ f.d_id ].d_options[ f.o_id ][ t ][ f.id ].a_on_failure = f.d_id
-- an action failed:
yl_speak_up.show_fs(player, "edit_actions", f.id)
end
end
-- show which dialogs point/lead to this_dialog
yl_speak_up.get_fs_show_what_points_to_this_dialog = function(player, this_dialog)
local pname = player:get_player_name()
local dialog = yl_speak_up.speak_to[pname].dialog
if(not(dialog)
or not(dialog.n_dialogs)
or not(this_dialog)
or not(dialog.n_dialogs[ this_dialog ])) then
return ""
end
-- only show this information when editing this npc
if(yl_speak_up.edit_mode[pname] ~= yl_speak_up.speak_to[pname].n_id) then
return ""
end
local found = {}
-- colored lines for the table showing the results
local res = {}
-- iterate over all dialogs
for d_id, d in pairs(dialog.n_dialogs) do
-- the dialog itself may have options that point back to the dialog itself
if(d.d_options) then
-- iterate over all options
for o_id, o in pairs(d.d_options) do
local r_text = ""
local p_text = ""
local alternate_dialog = nil
local alternate_text = nil
-- preconditions are not relevant;
-- effects are (dialog and on_failure)
if(o.o_results) then
for r_id, r in pairs(o.o_results) do
if(r and r.r_type and r.r_type == "dialog"
and r.r_value == this_dialog) then
r_text = r_text..yl_speak_up.print_as_table_effect(
r, pname)
table.insert(found, {d_id, o_id, r_id,
"option was selected"})
alternate_dialog = r.r_value
if(r.alternate_text) then
alternate_text =
"Show alternate text: "..
tostring(r.alternate_text)
end
elseif(r and r.r_type and r.r_type == "on_failure"
and r.r_value == this_dialog) then
r_text = r_text..yl_speak_up.print_as_table_effect(
r, pname)
table.insert(found, {d_id, o_id, r_id,
"the previous effect failed"})
alternate_dialog = r.r_value
alternate_text = "The previous effect failed. "
if(r.alternate_text) then
alternate_text = alternate_text..
"Show alternate text: "..
tostring(r.alternate_text)
else
alternate_text = alternate_text..
"Show this dialog here."
end
end
end
end
-- actions may be relevant
if(o.actions) then
for a_id, a in pairs(o.actions) do
if(a and a.a_on_failure
and a.a_on_failure == this_dialog) then
p_text = p_text..yl_speak_up.print_as_table_action(
a, pname)
table.insert(found, {d_id, o_id, a_id,
"action failed"})
alternate_dialog = a.a_on_failure
alternate_text = "The action failed. "
if(a.alternate_text) then
alternate_text = alternate_text..
"Show this alternate text: "..
tostring(a.alternate_text)
else
alternate_text = alternate_text..
"Show this dialog here."
end
end
end
end
yl_speak_up.print_as_table_dialog(p_text, r_text, dialog,
dialog.n_id, d_id, o_id,
-- sort value: formed by dialog and option id (not perfect but
-- good enough)
res, o, tostring(d_id).." "..tostring(o_id),
alternate_dialog, alternate_text)
end
end
end
local d_id = this_dialog
local formspec = yl_speak_up.print_as_table_prepare_formspec(res, "table_of_dialog_uses",
"back_from_show_what_points_here", "Back to dialog \""..tostring(d_id).."\"")
table.insert(formspec,
"label[20.0,1.8;Dialog \""..minetest.formspec_escape(this_dialog)..
"\" is referenced here:]")
if(#found ~= 1) then
table.insert(formspec,
"label[16.0,31.0;"..
minetest.formspec_escape("This dialog \""..tostring(d_id)..
"\" can be reached from "..
minetest.colorize("#FFFF00", tostring(#found))..
" options/actions/results.").."]")
else
table.insert(formspec,
"button[0.2,30.6;56.6,1.2;turn_dialog_into_alternate_text;"..
minetest.formspec_escape("Turn this dialog \""..
tostring(d_id)).."\" into an alternate text.]")
end
return table.concat(formspec, "\n")
end
yl_speak_up.register_fs("show_what_points_to_this_dialog",
yl_speak_up.input_show_what_points_to_this_dialog,
yl_speak_up.get_fs_show_what_points_to_this_dialog,
-- no special formspec required:
nil
)

View File

@ -1,753 +0,0 @@
-- we have a better version of this in the editor - so not offer this particular entry:
yl_speak_up.get_fs_talkdialog_add_basic_edit = function(
pname, formspec, h, pname_for_old_fs, is_a_start_dialog,
active_dialog, luaentity, may_edit_npc, anz_options)
return {h = h, formspec = formspec}
end
-- This is the main talkdialog the NPC shows when right-clicked. (now in edit_mode)
local old_input_talk = yl_speak_up.input_talk
yl_speak_up.input_talk = function(player, formname, fields)
local pname = player:get_player_name()
-- Is the player working on this particular npc?
local edit_mode = yl_speak_up.in_edit_mode(pname)
-- if not: do the normal things outside edit mode
if(not(edit_mode) and not(fields.button_start_edit_mode)) then
return old_input_talk(player, formname, fields)
end
-- selected option/answer
local o = ""
-- do all the processing and executing old_input_talk (in non-edit_mode)
-- can do - but do not execute any actions; just return o
fields.just_return_selected_option = true
o = old_input_talk(player, formname, fields)
-- old_input_talk handled it (including some error detection like
-- wrong formname, not talking to npc, npc not configured)
-- if in edit mode: detect if something was changed;
local result = yl_speak_up.edit_mode_apply_changes(pname, fields)
-- o is only nil if the old function returned nil; it does that
-- when it found a fitting reaction to a button press
if(not(o) and not(fields.button_start_edit_mode)) then
return
end
local n_id = yl_speak_up.speak_to[pname].n_id
-- 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_talk_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
-- make sure the inventory of the NPC is loaded
yl_speak_up.load_npc_inventory(n_id, true, nil)
-- 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
-- load the NPC dialog anew - but only what the NPC itself has to say, no generic dialogs
yl_speak_up.speak_to[pname].dialog = yl_speak_up.load_dialog(n_id, false)
-- 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
-- show which dialogs point to this one
if(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(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
-- change skin, cape and wielded items
if(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(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
if(fields.button_export_dialog) then
yl_speak_up.show_fs(player, "export")
return
end
if(fields.button_edit_notes) then
yl_speak_up.show_fs(player, "notes")
return
end
-- the player wants to give something to the NPC
-- (more complex in edit mode)
if(fields.player_offers_item) then
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})
return
end
-- button was clicked, now let's execute the results
local d_id = yl_speak_up.speak_to[pname].d_id
local dialog = yl_speak_up.speak_to[pname].dialog
-- all three buttons (pre(C)onditions, (Ef)fects, edit option) lead to the same new formspec
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["quests_"..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
-- we may soon need actions and o_results from the selected_option
local selected_option = {}
if(yl_speak_up.check_if_dialog_has_option(dialog, d_id, o)) then
selected_option = dialog.n_dialogs[d_id].d_options[o]
end
-- translate a dialog name into a d_id
fields.d_id = yl_speak_up.d_name_to_d_id(dialog, fields.d_id)
-- 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
-- in edit mode, *all* options are displayed
local old_calculate_displayable_options = yl_speak_up.calculate_displayable_options
yl_speak_up.calculate_displayable_options = function(pname, d_options, allow_recursion)
if(yl_speak_up.in_edit_mode(pname)) then
-- no options - nothing to do
if(not(d_options)) then
return {}
end
-- in edit mode: show all options (but sort them first)
local retval = {}
local sorted_list = yl_speak_up.get_sorted_options(d_options, "o_sort")
for i, o_k in ipairs(sorted_list) do
retval[o_k] = true
end
return retval
end
-- outside edit mode: really calculate what can be displayed
return old_calculate_displayable_options(pname, d_options, allow_recursion)
end
-- in edit mode, autoanswer, random dialogs and d_got_item play no role and are *not* applied
-- (we want to see and edit all options regardless of preconditions)
local old_apply_autoanswer_etc = yl_speak_up.apply_autoanswer_and_random_and_d_got_item
yl_speak_up.apply_autoanswer_and_random_and_d_got_item = function(player, pname, d_id, dialog, allowed, active_dialog, recursion_depth)
-- no automatic switching in edit_mode
if(yl_speak_up.in_edit_mode(pname)) then
return
end
return old_apply_autoanswer_etc(player, pname, d_id, dialog, allowed, active_dialog, recursion_depth)
end
-- helper function for yl_speak_up.get_fs_talkdialog:
-- shows the text the NPC "speaks" and adds edit and navigation buttons
-- (all only in *edit_mode*)
local old_talkdialog_main_text = yl_speak_up.get_fs_talkdialog_main_text
yl_speak_up.get_fs_talkdialog_main_text = function(pname, formspec, h, dialog, dialog_list, c_d_id, active_dialog, alternate_text)
if(not(yl_speak_up.in_edit_mode(pname))) then
return old_talkdialog_main_text(pname, formspec, h, dialog, dialog_list, c_d_id, active_dialog, alternate_text)
end
local d_id_to_dropdown_index = {}
-- allow to change skin, wielded items etc.
table.insert(formspec, "button[15.75,3.5;3.5,0.9;edit_skin;Edit Skin]")
if(not(dialog) or not(dialog.n_dialogs)) then
return {h = h, formspec = formspec, d_id_to_dropdown_index = {}, dialog_list = dialog_list}
end
-- display the window with the text the NPC is saying
-- 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
local d_name = dialog.n_dialogs[d].d_name or d
-- build the list of available dialogs for the dropdown list(s)
dialog_list = dialog_list..","..minetest.formspec_escape(d_name)
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", "15.4,5.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", "17.6,5.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
dialog_list = dialog_list..",d_end"
d_id_to_dropdown_index["d_end"] = #sorted_list + 2
if(not(yl_speak_up.is_special_dialog(c_d_id))) then
table.insert(formspec, "label[0.2,4.2;Dialog ")
table.insert(formspec, minetest.formspec_escape(c_d_id))
table.insert(formspec, ":]")
table.insert(formspec, "field[5.0,3.6;9.8,1.2;d_name;;")
table.insert(formspec, minetest.formspec_escape(dialog.n_dialogs[c_d_id].d_name or c_d_id))
table.insert(formspec, "]")
table.insert(formspec, "tooltip[d_name;Dialogs can have a *name* that is diffrent from\n"..
"their ID (which is i.e. d_4). The name will be shown\n"..
"in the dropdown list. Save a new name by clicking on\n"..
"the dialog \"Save\" button.]")
end
table.insert(formspec, "label[0.2,5.5;Dialog:]") -- "..minetest.formspec_escape(c_d_id)..":]")
table.insert(formspec, "dropdown[5.0,5.0;9.8,1;d_id;"..dialog_list..";"..
(d_id_to_dropdown_index[c_d_id] or "1")..",]")
table.insert(formspec, "tooltip[5.0,5.0;9.8,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", "3.5,5.0;1,0.9", "show_new_dialog",
"+",
"Create a new dialog.",
true)
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
"button", "11.0,0.3;2,1.0", "button_edit_notes",
"Notes",
"Keep notes of what this NPC is for, how his character is etc.",
true)
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
"button", "13.2,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.4,0.3;2,0.9", "button_save_dialog",
"Save",
"Save this dialog.",
true)
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
"button", "17.5,0.3;2.4,0.9", "button_export_dialog",
"Export",
"Export: Show the dialog in .json format which you can copy and store on your computer.",
true)
local tmp = "[0.2,6;19.6,16.8;d_text;"
-- static help text instead of text input field for d_got_item
if(c_d_id == "d_got_item") then
table.insert(formspec, "hypertext"..tmp..
"<normal>Note:\nThis is a special dialog. "..
"It will be called when the player clicks on "..
"<b>I want to give you something</b>."..
"\nMost of the things listed below will be added automaticly when you add a "..
"new option to this dialog. In most cases you may just have to edit the "..
"<b>precondition</b> so that the <i>right item</i> is accepted, and then "..
"set the <b>target dialog</b> <i>according to your needs</i>. Please also "..
"edit the <b>alternate text</b> so that it fits your <i>item</i>!"..
"\nThis is how it works in detail:"..
"\n<b>Each option</b> you add here ought to deal with one item(stack) that "..
"the NPC expects from the player, i.e. <i>farming:bread 2</i>. "..
"Each option needs to be selected <b>automaticly</b> and ought to contain:"..
"\n* a <b>precondition</b> regarding "..
"<b>an item the player offered/gave to the NPC</b> "..
"(shown as <b>player_offered_item</b> in overview) "..
"where you define which item(stack) is relevant for this option"..
"\n* an <b>effect</b> regarding <b>an item the player offered to the NPC</b> "..
"(shown as <b>deal_with_offered_item</b> in overview) "..
"where you define what shall happen to the offered item. Usually "..
"the NPC will accept the item and put it into its inventory."..
"\n* Don't forget to set a suitable target dialog for the <b>effect</b>! "..
"Your NPC ought to comment on what he got, i.e. "..
"<i>Thank you for those two breads! You saved me from starving.</i>"..
"You can also work with an alternate text here (as is done in the "..
"default setup when adding a new option here)."..
"\n</normal>]")
-- static help text instead of text input field for d_trade
elseif(c_d_id == "d_trade") then
table.insert(formspec, "hypertext"..tmp..
"<normal>Note:\nThis is a special dialog. "..
"It will be called when the player clicks on "..
"<b>Let's trade!</b>."..
"\nSome of the things listed below will be added automaticly when you add a "..
"new option to this dialog. In most cases you may just have to edit the "..
"<b>precondition</b> so that the <i>right item(stack)</i> is beeing "..
"searched for, and you need to add suitable effects. The ones added "..
"automaticly are just an example."..
"\nNote that once the NPC found a matching precondition, it will execute the "..
"relevant effects and present the player the trade list. Any further options "..
"that might also fit will not be executed this time. Only <b>one</b> option "..
"(or none) will be selected each time."..
"\nThis is how it works in detail:"..
"\n<b>Each option</b> you add here ought to deal with one item(stack) that "..
"the NPC might or might not have in its inventory, "..
"i.e. <i>default:stick 4</i>. "..
"Each option needs to be selected <b>automaticly</b> and ought to contain:"..
"\n* at least one <b>precondition</b> regarding "..
"<b>the inventory of the NPC</b> "..
"where you define which item(stack) is relevant for this option "..
"(you can add multiple such preconditions for each option)"..
"\n* at least one <b>effect</b> regarding what the NPC shall do if the "..
"precondition matches. In most cases, <b>NPC crafts something</b>, "..
"<b>put item from the NPC's inventory into a chest etc.</b> or "..
"<b>take item from a chest etc. and put it into the NPC's inventory</b> "..
"will be what you are looking for. More than one effect is possible."..
"\n* In this particular case, no target dialog needs to be selected. After "..
"executing the effect(s), the trade list view will be shown to the "..
"player."..
"\n</normal>]")
elseif(c_d_id == "d_dynamic") then
table.insert(formspec, "hypertext"..tmp..
"<normal>Note:\nThis is a special dialog. "..
"Each time right before this special dialog is displayed, a "..
"function is called that can fill the <b>d_dynamic</b> dialog "..
"with text and options."..
"\nThat function has to decide <b>based on NPC, player and context</b> what "..
"it wants to display this time."..
"\nThe d_dynamic dialog is <b>never saved</b> as part of the dialog. "..
"It has to be dynamicly created by your function each time it is needed."..
"\nThe d_dynamic dialog will always be available as a <b>legitimate target "..
"dialog</b> of a dialog option. Its options can do all that which "..
"options of other dialogs can do. Its options can also lead back to "..
"normal static parts of the dialog."..
"\n</normal>]")
elseif(active_dialog and active_dialog.d_text) then
table.insert(formspec, "textarea"..tmp..";"..
minetest.formspec_escape(active_dialog.d_text or "?")..
"]")
else
table.insert(formspec, "textarea"..tmp..";"..
minetest.formspec_escape("[no text]")..
"]")
end
return {h = h, formspec = formspec, d_id_to_dropdown_index = d_id_to_dropdown_index,
dialog_list = dialog_list}
end
-- helper function for yl_speak_up.get_fs_talkdialog:
-- prints one entry (option/answer) in normal and *edit_mode*
local old_talkdialog_line = yl_speak_up.get_fs_talkdialog_line
yl_speak_up.get_fs_talkdialog_line = function(
formspec, h, pname_for_old_fs, oid, sb_v,
dialog, allowed, pname,
-- these additional parameters are needed *here*, in edit_mode:
active_dialog, dialog_list, d_id_to_dropdown_index,
current_index, anz_options)
if(not(yl_speak_up.in_edit_mode(pname))) then
-- in normal mode:
return old_talkdialog_line(formspec, h, pname_for_old_fs, oid, sb_v,
dialog, allowed, pname,
active_dialog, dialog_list, d_id_to_dropdown_index,
current_index, anz_options)
end
-- in edit mode:
local offset = 0.0
local field_length = 43.8
if(pname_for_old_fs) then
offset = 0.7
field_length = 41.8
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.9+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
or yl_speak_up.is_special_dialog(v.r_value)) 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!
local arrow = "->"
local only_once = ""
if(sb_v.o_visit_only_once and sb_v.o_visit_only_once == 1) then
arrow = "*"
only_once = "\nNote: This option can be selected only *once* per talk!"
end
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
"button", tostring(12.6+offset)..","..h..";1,0.9", "button_" .. oid,
arrow,
"Go to target dialog "..minetest.formspec_escape(target_dialog or "")..
" that will be shown when this option ("..oid..") is selected."..
only_once,
(target_dialog))
-- allow to set a new target dialog
table.insert(formspec, "dropdown["..tostring(5.0+offset)..","..h..";7.7,1;d_id_"..
oid..";"..
dialog_list..";"..
(d_id_to_dropdown_index[(target_dialog or "?")] or "0")..",]")
-- add a tooltip "Change target dialog"
table.insert(formspec, "tooltip[5.0,"..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))
local d_option = active_dialog.d_options[sb_v.o_id]
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
"button", tostring(2.25+offset)..","..h..";0.6,0.9", "quests_"..oid,
"Q",
"This option sets a (Q)est step if possible.\n"..
"A special precondition is evaluated automaticly\n"..
"to check if the quest step can be set.",
(d_option and d_option.quest_id and d_option.quest_step))
-- 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.05+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)))
if(sb_v.o_autoanswer) then
table.insert(formspec,
"label["..tostring(13.5+offset+0.2)..","..tostring(h+0.5)..";"..
minetest.formspec_escape("[Automaticly selected if preconditions are met] "..
tostring(sb_v.o_text_when_prerequisites_met))..
"]")
elseif(active_dialog.o_random) then
table.insert(formspec,
"label["..tostring(13.5+offset+0.2)..","..tostring(h+0.5)..";"..
minetest.formspec_escape("[One of these options is randomly selected] "..
tostring(sb_v.o_text_when_prerequisites_met))..
"]")
else
-- show the actual text for the option
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
"field", tostring(13.5+offset)..","..h..";"..
tostring(field_length-5.3)..",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)
end
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
"button", tostring(10.5+offset+field_length-2.2)..","..h..";1.0,0.9", "delete_option_"..oid,
"Del",
"Delete this option/answer.",
true)
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
-- "image_button", tostring(9.9+offset+field_length-0.5)..","..h..";0.5,0.9"..
-- ";gui_furnace_arrow_bg.png^[transformR180",
"button", tostring(10.5+offset+field_length-1.1)..","..h..";0.5,0.9",
"option_move_down_"..oid,
"v",
"Move this option/answer one down.",
(current_index < anz_options))
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
-- "image_button", tostring(9.9+offset+field_length-1.0)..","..h..";0.5,0.9"..
-- ";gui_furnace_arrow_bg.png",
"button", tostring(10.5+offset+field_length-0.5)..","..h..";0.5,0.9",
"option_move_up_"..oid,
"^",
"Move this option/answer one up.",
(current_index > 1))
return {h = h, formspec = formspec}
end
-- add a prefix to "I want to give you something." dialog option in edit_mode:
local old_talkdialog_offers_item = yl_speak_up.get_fs_talkdialog_add_player_offers_item
yl_speak_up.get_fs_talkdialog_add_player_offers_item = function(pname, formspec, h, dialog, add_text, pname_for_old_fs)
local offer_item_add_text = nil
if(yl_speak_up.in_edit_mode(pname)) then
-- force showing the "I want to give you something"-text
offer_item_add_text = minetest.formspec_escape("[dialog d_got_item] -> ")
end
return old_talkdialog_offers_item(pname, formspec, h, dialog, offer_item_add_text, pname_for_old_fs)
end
-- helper function for yl_speak_up.get_fs_talkdialog:
-- if the player can edit the NPC,
-- either add a button for entering edit mode
-- or add the buttons needed to edit the dialog when in *edit mode*
local old_talkdialog_add_edit_and_command_buttons = yl_speak_up.get_fs_talkdialog_add_edit_and_command_buttons
yl_speak_up.get_fs_talkdialog_add_edit_and_command_buttons = function(
pname, formspec, h, pname_for_old_fs, is_a_start_dialog,
active_dialog, luaentity, may_edit_npc, anz_options)
-- add the buttons that are added to all editable NPC *first*:
-- inventory access, commands for mobs_npc, custom commands
local res = old_talkdialog_add_edit_and_command_buttons(
pname, formspec, h, pname_for_old_fs, is_a_start_dialog,
active_dialog, luaentity, may_edit_npc, anz_options)
formspec = res.formspec
h = res.h
-- if the player cannot *enter* edit_mode:
if(not(may_edit_npc)) then
return res
end
local edit_mode = yl_speak_up.in_edit_mode(pname)
-- button "show log" for those who can edit the NPC (entering edit mode is not required)
local text = minetest.formspec_escape(
"[Log] Show me your log.")
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
"show_log",
text, text,
true, nil, nil, pname_for_old_fs)
-- Offer to enter edit mode if the player has the npc_talk_owner priv OR is allowed to edit the NPC.
-- The npc_talk_master priv allows to edit all NPC.
if(not(edit_mode)) 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
return {h = h, formspec = formspec}
end
local offset = 0.0
-- If in edit mode, add new menu entries: "add new options", "end edit mode" and what else is needed.
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)
local b_text = "Turn this into"
if(is_a_start_dialog) then
b_text = "This shall no longer be"
end
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
"turn_into_a_start_dialog",
"With automatic selection of options, it is possible that the real\n"..
"start dialog will never be shown to the player. However, we need\n"..
"to add some buttons to that start dialog for i.e. giving items\n"..
"to the NPC and for trading. Therefore, dialogs can be marked as\n"..
"*a* start dialog so that these buttons will be added to those dialogs.",
b_text.." *a* start dialog where buttons for trade etc. are shown.",
not(active_dialog and active_dialog.d_sort and tonumber(active_dialog.d_sort) == 0),
"The start dialog automaticly counts as *a* start dialog where buttons for "..
"trade etc. are shown.", nil, pname_for_old_fs)
-- chat option: Mute/Unmute NPC
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
return {h = h, formspec = formspec}
end
-- apply force_edit_mode if necessary
local old_get_fs_talkdialog = yl_speak_up.get_fs_talkdialog
yl_speak_up.get_fs_talkdialog = function(player, n_id, d_id, alternate_text, recursion_depth)
if(not(player)) then
return
end
local pname = player:get_player_name()
-- are we in force edit mode, and can the player edit this NPC?
if(yl_speak_up.force_edit_mode[pname]
-- not already in edit mode?
and (not(yl_speak_up.edit_mode[pname]) or yl_speak_up.edit_mode[pname] ~= n_id)
and yl_speak_up.may_edit_npc(player, n_id)) then
yl_speak_up.edit_mode[pname] = n_id
end
return old_get_fs_talkdialog(player, n_id, d_id, alternate_text, recursion_depth)
end
--[[
yl_speak_up.get_fs_talk_wrapper = function(player, param)
if(not(param)) then
param = {}
end
-- recursion depth from autoanswer: 0 (the player selected manually)
return yl_speak_up.get_fs_talkdialog(player, param.n_id, param.d_id, param.alternate_text,0)
end
--]]
yl_speak_up.register_fs("talk",
-- this function is changed here:
yl_speak_up.input_talk,
-- the underlying function is changed as well - but the wrapper calls that already; so ok:
yl_speak_up.get_fs_talk_wrapper,
-- no special formspec required:
nil
)

View File

@ -1,124 +0,0 @@
-- helper function
yl_speak_up.wrap_long_lines_for_table = function(text, prefix, line_length, max_lines)
-- show newlines as <\n> in order to save space
local text = (text or "?")
text = string.gsub(text, "\n", minetest.formspec_escape("<br>"))
-- break the text up into lines of length x
local parts = minetest.wrap_text(text, line_length, true)
if(not(parts) or #parts < 2) then
return minetest.formspec_escape(text)
end
local show_parts = {}
-- only show the first two lines (we don't have infinite room)
for i, p in ipairs(parts) do
if(i <= max_lines) then
table.insert(show_parts, minetest.formspec_escape(p))
end
end
if(#parts > max_lines) then
return table.concat(show_parts, prefix)..minetest.formspec_escape(" [...]")
end
return table.concat(show_parts, prefix)
end
-- helper functions for yl_speak_up.fs_get_list_of_usage_of_variable
-- and yl_speak_up.show_what_points_to_this_dialog
yl_speak_up.print_as_table_precon = function(p, pname)
return ",#FFFF00,"..
minetest.formspec_escape(tostring(p.p_id))..
",#FFFF00,pre(C)ondition,#FFFF00,"..
minetest.formspec_escape(p.p_type)..",#FFFF00,"..
minetest.formspec_escape(yl_speak_up.show_precondition(p, pname))
end
yl_speak_up.print_as_table_effect = function(r, pname)
return ",#55FF55,"..
minetest.formspec_escape(tostring(r.r_id))..
",#55FF55,(Ef)fect,#55FF55,"..
minetest.formspec_escape(r.r_type)..",#55FF55,"..
minetest.formspec_escape(yl_speak_up.show_effect(r, pname))
end
yl_speak_up.print_as_table_action = function(a, pname)
return ",#FF9900,"..
minetest.formspec_escape(tostring(a.a_id))..
",#FF9900,(A)ction,#FF9900,"..
minetest.formspec_escape(a.a_type)..",#FF9900,"..
-- these lines can get pretty long when a description for a quest item is set
yl_speak_up.wrap_long_lines_for_table(
yl_speak_up.show_action(a, pname),
",#FFFFFF,,#FFFFFF,,#FFFFFF,,#FF9900,",
80, 4)
end
yl_speak_up.print_as_table_dialog = function(p_text, r_text, dialog, n_id, d_id, o_id, res, o, sort_value,
alternate_dialog, alternate_text)
if(p_text == "" and r_text == "" ) then
return
end
local d_text = yl_speak_up.wrap_long_lines_for_table(
dialog.n_dialogs[ d_id ].d_text or "?",
",#FFFFFF,,#FFFFFF,,#FFFFFF,,#BBBBFF,",
80, 3)
if(not(alternate_dialog) or not(alternate_text)) then
alternate_text = ""
else
alternate_text = ",#BBBBFF,"..minetest.formspec_escape(tostring(alternate_dialog))..
-- show alternate text in a diffrent color
",#BBBBFF,Dialog,#BBBBFF,says next:,#FFBBBB,"..
yl_speak_up.wrap_long_lines_for_table(
alternate_text,
",#FFFFFF,,#FFFFFF,,#FFFFFF,,#FFBBBB,",
80, 3)
end
res[ tostring(n_id).." "..tostring(d_id).." "..tostring(o_id) ] = {
text = "#6666FF,"..
tostring(n_id)..",#6666FF,NPC,#6666FF,named:,#6666FF,"..
minetest.formspec_escape(dialog.n_npc or "?")..","..
"#BBBBFF,"..
tostring(d_id)..",#BBBBFF,Dialog,#BBBBFF,says:,#BBBBFF,"..
d_text..","..
"#FFFFFF,"..
tostring(o_id)..",#FFFFFF,Option,#FFFFFF,A:,#FFFFFF,"..
minetest.formspec_escape(tostring(o.o_text_when_prerequisites_met or "?"))..
p_text..r_text..
alternate_text,
sort_value = sort_value}
end
yl_speak_up.print_as_table_prepare_formspec = function(res, table_name, back_button_name, back_button_text,
is_already_sorted, concat_with, table_columns)
local sorted_res = {}
-- this is the default for "show where a variable is used"
if(not(is_already_sorted)) then
local sorted_list = yl_speak_up.get_sorted_options(res, "sort_value")
for i, k in pairs(sorted_list) do
table.insert(sorted_res, res[ k ].text)
end
table_columns = "color,span=1;text;color,span=1;text;color,span=1;text;color,span=1;text"
else
sorted_res = res
end
if(not(concat_with)) then
-- insert blank lines between lines belonging together
concat_with = ",#FFFFFF,,#FFFFFF,,#FFFFFF,,#FFFFFF,,"
end
local formspec = {
"size[57,33]",
-- back to the list with that one precondition or effect
"button[0.2,0.2;56.6,1.2;"..back_button_name..";"..
minetest.formspec_escape(back_button_text).."]",
"button[0.2,31.6;56.6,1.2;"..back_button_name..";"..
minetest.formspec_escape(back_button_text).."]",
"tablecolumns["..tostring(table_columns).."]",
}
table.insert(formspec,
"table[1.2,2.4;55.0,28.0;"..tostring(table_name)..";"..
table.concat(sorted_res, concat_with).."]")
return formspec
end

View File

@ -1,98 +0,0 @@
-- when in edit mode: ask for saving dialogs when needed
local old_show_fs = yl_speak_up.show_fs
yl_speak_up.show_fs = function(player, fs_name, param)
if(not(player)) then
return
end
local pname = player:get_player_name()
if(not(yl_speak_up.speak_to[pname])) then
return
end
local last_fs = yl_speak_up.speak_to[pname].last_fs
-- show the save or discard changes dialog
if(fs_name and fs_name == "save_or_discard_changes") then
yl_speak_up.show_fs_ver(pname, "yl_speak_up:save_or_discard_changes",
yl_speak_up.get_fs_save_or_discard_changes(player, param))
return
-- the player either saved or discarded; we may proceed now
elseif(fs_name and fs_name == "proceed_after_save") then
fs_name = yl_speak_up.speak_to[pname].next_fs
param = yl_speak_up.speak_to[pname].next_fs_param
yl_speak_up.speak_to[pname].next_fs = nil
yl_speak_up.speak_to[pname].next_fs_param = nil
yl_speak_up.speak_to[pname].last_fs = fs_name
yl_speak_up.speak_to[pname].last_fs_param = param
if(not(fs_name) or fs_name == "quit") then
yl_speak_up.reset_vars_for_player(pname, false)
return
end
-- the player clicked on "back" in the above dialog
elseif(fs_name and fs_name == "show_last_fs") then
-- call the last formspec again - and with the same parameters
fs_name = yl_speak_up.speak_to[pname].last_fs
param = yl_speak_up.speak_to[pname].last_fs_param
-- do we need to check if there is something that needs saving?
elseif(fs_name
-- msg is just a loop for displaying (mostly error) messages
and fs_name ~= "msg"
and fs_name ~= "player_offers_item"
-- is the player editing the NPC? that is: might there be any changes?
and (yl_speak_up.edit_mode[pname] == yl_speak_up.speak_to[pname].n_id)) then
local last_fs = yl_speak_up.speak_to[pname].last_fs
local d_id = yl_speak_up.speak_to[pname].d_id
local o_id = yl_speak_up.speak_to[pname].o_id
-- only these two formspecs need to ask specificly if the data ought to be saved
if(last_fs == "talk" or last_fs == "edit_option_dialog" or fs_name == "quit") then
local last_param = yl_speak_up.speak_to[pname].last_fs_param
local show_save_fs = false
if(not(param)) then
param = {}
end
-- set the target dialog
yl_speak_up.speak_to[pname].target_dialog = param.d_id
-- if we are switching from one dialog to another: is it the same?
if(last_fs == "talk" and fs_name == last_fs
and param and param.d_id and param.d_id ~= d_id) then
-- diffrent parameters: save (if needed)
show_save_fs = true
elseif(fs_name == "talk" and param and param.do_save) then
-- player clicked on save button
show_save_fs = true
-- leaving a dialog: save!
elseif(last_fs == "talk" and fs_name ~= last_fs) then
show_save_fs = true
-- clicking on "save" in an edit option dialog: save!
elseif(last_fs == "edit_option_dialog" and fs_name == last_fs
and param and param.caller and param.caller == "save_option") then
show_save_fs = true
-- leaving editing an option: save!
elseif(last_fs == "edit_option_dialog" and fs_name ~= last_fs) then
show_save_fs = true
-- quitting: save!
elseif(fs_name == "quit") then
yl_speak_up.speak_to[pname].target_dialog = nil
show_save_fs = true
end
-- show the save or discard dialog
if(show_save_fs) then
yl_speak_up.speak_to[pname].next_fs = fs_name
yl_speak_up.speak_to[pname].next_fs_param = param
-- check first if it's necessary to ask for save or discard
yl_speak_up.input_save_or_discard_changes(player, "", {})
return
end
end
-- store the new formspec
yl_speak_up.speak_to[pname].last_fs = fs_name
-- and its parameter
yl_speak_up.speak_to[pname].last_fs_param = param
end
-- Note: fs_name and param *may* have been changed in edit_mode by the code above
old_show_fs(player, fs_name, param)
end

View File

@ -1,70 +0,0 @@
-- overrides for api/api_trade_inv.lua:
-- the player *can* place something into the npc_gives inventory list in edit_mode:
local old_trade_inv_allow_put = yl_speak_up.trade_inv_allow_put
yl_speak_up.trade_inv_allow_put = function(inv, listname, index, stack, player)
if(not(player)) then
return 0
end
-- allow putting something in in edit mode - but not otherwise
if(listname and listname == "npc_gives") then
local pname = player:get_player_name()
local n_id = yl_speak_up.speak_to[pname].n_id
-- only in edit mode! else the NPC manages this slot
if(n_id and yl_speak_up.in_edit_mode(pname)) then
return stack:get_count()
end
end
return old_trade_inv_allow_put(inv, listname, index, stack, player)
end
-- prevent do_trade_simple from executing trade and reporting successful action:
local old_do_trade_simple = yl_speak_up.do_trade_simple
yl_speak_up.do_trade_simple = function(player, count)
if(not(player)) then
return
end
local pname = player:get_player_name()
-- which trade are we talking about?
local trade = yl_speak_up.trade[pname]
if(trade.n_id and yl_speak_up.edit_mode[pname] == trade.n_id) then
-- instruct old_do_trade_simple to neither execute the trade nor see this
-- as an action that was executed
trade.dry_run_no_exec = true
end
return old_do_trade_simple(player, count)
end
-- overrides for api/api_trade.lua:
-- do not allow deleting trades that are actions of an option if not in edit mode:
local old_delete_trade_simple = yl_speak_up.delete_trade_simple
yl_speak_up.delete_trade_simple = function(player, trade_id)
local pname = player:get_player_name()
local n_id = yl_speak_up.speak_to[pname].n_id
-- get the necessary dialog data
local dialog = yl_speak_up.speak_to[pname].dialog
if(dialog and dialog.trades and trade_id
and dialog.trades[ trade_id ] and n_id) then
if( dialog.trades[ trade_id ].d_id
and yl_speak_up.edit_mode[pname] ~= n_id) then
yl_speak_up.show_fs(player, "msg", {
input_to = "yl_speak_up:do_trade_simple",
formspec = "size[6,2]"..
"label[0.2,-0.2;"..
"Trades that are attached to dialog options\n"..
"can only be deleted in edit mode. Please tell\n"..
"your NPC that you are its owner and have\n"..
"new commands!]"..
"button[2,1.5;1,0.9;back_from_error_msg;Back]"})
return
end
end
return old_delete_trade_simple(player, trade_id)
end

View File

@ -229,7 +229,8 @@ yl_speak_up.reload = function(modpath, log_entry)
dofile(modpath .. "export_to_ink.lua")
dofile(modpath .. "fs/fs_export.lua")
dofile(modpath .. "editor/edit_mode.lua")
-- edit_mode.lua has been moved to the mod npc_talk_edit:
-- dofile(modpath .. "editor/edit_mode.lua")
-- initialize and load all registered generic dialogs
yl_speak_up.load_generic_dialogs()

View File

@ -17,6 +17,12 @@ Reporting bugs: Please report issues <a href="https://gitea.your-land.de/Sokomin
Clone via i.e. `git clone https://gitea.your-land.de/Sokomine/yl_speak_up`
or install via <a href="https://content.minetest.net/">ContentDB</a>.
In order to be able to edit the dialogs of the NPC ingame, please install
<a href="https://gitea.your-land.de/Sokomine/npc_talk_edit">npc_talk_edit</a>.
The only situation where you might not need this extra mod is when you
design your own adventure game and your players are not expected to edit
the NPC.
Optional dependency:
<a href="https://content.minetest.net/packages/TenPlus1/mobs/">mobs_redo</a>
is highly recommended. You may be able to use other mob mods with
@ -30,7 +36,7 @@ You might also wish to use <a href="https://content.minetest.net/packages/TenPlu
from TenPlus1 as that mod provides very useful actual NPC and will be used by
<a href="https://gitea.your-land.de/Sokomine/npc_talk">npc_talk</a> if installed.
To get started, best install `yl_speak_up`, `npc_talk`, `mobs_redo` and `mobs_npc` in your world.
To get started, best install `yl_speak_up`, `npc_talk_edit`, `npc_talk`, `mobs_redo` and `mobs_npc` in your world.
## Table of Content