Compare commits

...

8 Commits

12 changed files with 437 additions and 101 deletions

View File

@ -191,6 +191,7 @@ yl_speak_up.show_fs_npc_text = function(pname, formspec, dialog, alternate_text,
-- replace $NPC_NAME$ etc.
local t = minetest.formspec_escape(yl_speak_up.replace_vars_in_text(
alternate_text, dialog, pname))
-- t = "Visits to this dialog: "..tostring(active_dialog.visits).."\n"..t
if(fs_version > 2) then
yl_speak_up.add_formspec_element_with_tooltip_if(formspec,
"hypertext", "0.2,5;19.6,17.8", "d_text",

View File

@ -30,3 +30,46 @@ yl_speak_up.get_start_dialog_id = function(dialog)
end
-- count visits to this dialog - but *not* for generic dialogs as those are just linked and not
-- copied for each player; also not in edit_mode as it makes no sense there
yl_speak_up.count_visits_to_dialog = function(pname)
if(not(pname)) then
return
end
local d_id = yl_speak_up.speak_to[pname].d_id
local dialog = yl_speak_up.speak_to[pname].dialog
if(not(d_id) or not(dialog) or not(dialog.n_dialogs) or not(dialog.n_dialogs[d_id])) then
return
end
if(not(dialog.n_dialogs[d_id].is_generic)) then
if(not(dialog.n_dialogs[d_id].visits)) then
dialog.n_dialogs[d_id].visits = 0
end
dialog.n_dialogs[d_id].visits = dialog.n_dialogs[d_id].visits + 1
end
end
-- count visits to options - but *not* for generic dialogs as those are just linked and not
-- copied for each player;
-- called after all effects have been executed successfully
-- not called in edit_mode because effects are not executed there
yl_speak_up.count_visits_to_option = function(pname, o_id)
if(not(pname)) then
return
end
local d_id = yl_speak_up.speak_to[pname].d_id
local dialog = yl_speak_up.speak_to[pname].dialog
if(not(d_id) or not(dialog) or not(dialog.n_dialogs) or not(dialog.n_dialogs[d_id])
or not(o_id)
or not(dialog.n_dialogs[d_id].d_options)
or not(dialog.n_dialogs[d_id].d_options[o_id])) then
return
end
local o_data = dialog.n_dialogs[d_id].d_options[o_id]
if(not(o_data.is_generic)) then
if(not(o_data.visits)) then
o_data.visits = 0
end
o_data.visits = o_data.visits + 1
end
end

View File

@ -254,6 +254,59 @@ yl_speak_up.custom_functions_p_[ "text_contained_these_words" ] = {
}
yl_speak_up.custom_functions_p_[ "counted_visits_to_dialog" ] = {
description = "counted dialog visits: "..
"How many times has the player visited/seen this dialog during this talk?",
param1_text = "Name of dialog (i.e. \"d_1\"):",
param1_desc = "Enter the dialog ID of the dialog for which you want to get the amount of "..
"visits. If the dialog does not exist, -1 is returned.",
code = function(player, n_id, p)
local pname = player:get_player_name()
if(not(pname)) then
return -1
end
local dialog = yl_speak_up.speak_to[pname].dialog
local d_id = p["p_param1"]
if(not(yl_speak_up.check_if_dialog_exists(dialog, d_id))) then
return -1
end
local visits = dialog.n_dialogs[d_id].visits
if(not(visits)) then
return 0
end
return visits
end,
}
yl_speak_up.custom_functions_p_[ "counted_visits_to_option" ] = {
description = "counted dialog option/answers visits: "..
"How many times has the player visited/seen this dialog *option* during this talk?",
param1_text = "Name of dialog (i.e. \"d_1\"):",
param1_desc = "Enter the dialog ID of the dialog the option belongs to.",
param2_text = "Name of option (i.e. \"o_2\"):",
param2_desc = "Enter the option ID of the dialog for which you want to get the amount of "..
"visits. If the option does not exist, -1 is returned.",
code = function(player, n_id, p)
local pname = player:get_player_name()
if(not(pname)) then
return -1
end
local dialog = yl_speak_up.speak_to[pname].dialog
local d_id = p["p_param1"]
local o_id = p["p_param2"]
if(not(yl_speak_up.check_if_dialog_has_option(dialog, d_id, o_id))) then
return -1
end
local visits = dialog.n_dialogs[d_id].d_options[o_id].visits
if(not(visits)) then
return 0
end
return visits
end,
}
-----------------------------------------------------------------------------
-- Custom actions (of type "evaluate")
-----------------------------------------------------------------------------

View File

@ -45,6 +45,16 @@ yl_speak_up.load_dialog = function(n_id, player) -- returns the saved dialog
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"

View File

@ -386,6 +386,27 @@ yl_speak_up.edit_mode_apply_changes = function(pname, fields)
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"
@ -394,7 +415,7 @@ yl_speak_up.edit_mode_apply_changes = function(pname, fields)
elseif(d_option.o_autoanswer and d_option.o_autoanswer == 1) then
old_answer_mode = "automaticly"
end
if(fields.otpion_autoanswer ~= old_answer_mode) then
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

View File

@ -271,11 +271,25 @@ yl_speak_up.get_fs_edit_option_dialog = function(player, n_id, d_id, o_id, calle
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*)

View File

@ -201,11 +201,11 @@ 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)
-- no options - nothing to do
if(not(d_options)) then
return {}
end
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")
@ -451,11 +451,18 @@ yl_speak_up.get_fs_talkdialog_line = function(
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(9.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.",
" that will be shown when this option ("..oid..") is selected."..
only_once,
(target_dialog))
-- allow to set a new target dialog

View File

@ -60,6 +60,8 @@ yl_speak_up.execute_all_relevant_effects = function(player, effects, o_id, actio
n_id, d_id, o_id)
end
yl_speak_up.debug_msg(player, n_id, o_id, "No effects given.")
-- the player has visited this option successfully
yl_speak_up.count_visits_to_option(pname, o_id)
-- no effects? Then...return to the start dialog
return {next_dialog = "", alternate_text = nil}
end
@ -137,6 +139,7 @@ yl_speak_up.execute_all_relevant_effects = function(player, effects, o_id, actio
tostring(r.r_id)..". New target dialog: "..tostring(r.r_value)..".")
-- we also stop execution here
-- any quest step is NOT set (because effects and/or action weren't successful)
-- the visit counter for this option is not incresed - after all the visit failed
return {next_dialog = r.r_value, alternate_text = r.alternate_text}
end
last_result = res
@ -161,6 +164,8 @@ yl_speak_up.execute_all_relevant_effects = function(player, effects, o_id, actio
yl_speak_up.quest_step_reached(player, d_option.quest_step, d_option.quest_id,
n_id, d_id, o_id)
end
-- the player has visited this option successfully
yl_speak_up.count_visits_to_option(pname, o_id)
return {next_dialog = target_dialog, alternate_text = alternate_text}
end

View File

@ -1,141 +1,271 @@
-- helper functions for export_to_ink_language:
-- d: dialog
yl_speak_up.export_to_ink_dialog_knot = function(lines, d, n_d_id)
-- TODO: include effect setting variables to values (problem: diffrent characters allowed)
-- TODO: include preconditions/conditions regarding setting variables
-- this table will hold the functions for exporting to ink so that we don't fill that namespace too much
yl_speak_up.export_to_ink = {}
-- an abbreviation
local ink_export = yl_speak_up.export_to_ink
-- in order to be able to deal with multiple NPC in ink, we use the NPC id n_id
-- plus the dialog id d_id as a name prefix; o_id, a_id and r_id are appended
-- as needed
yl_speak_up.export_to_ink.print_knot_name = function(lines, knot_name)
table.insert(lines, "\n\n=== ")
table.insert(lines, n_d_id)
table.insert(lines, tostring(knot_name or "ERROR"))
table.insert(lines, " ===")
end
-- execution of effects ends if an on_failure effect is reached; for ink to be able to
-- display effects (as tags) correctly, we need to add them at the right place - some
-- tags come after the option/choice, some after the last action (if there is an action),
-- some between on_failure actions (if they exist)
yl_speak_up.export_to_ink.add_effect_tags = function(text, sorted_e_list, effects, start_at_effect)
if(not(text)) then
text = ""
end
if(not(start_at_effect) or start_at_effect > #sorted_e_list) then
return text
end
for i = start_at_effect, #sorted_e_list do
local r_id = sorted_e_list[i]
if(effects and effects[r_id]) then
local r = effects[r_id]
if(r and r.r_type and r.r_type == "on_failure") then
-- end as soon as we reach the next on_failure dialog
return text
end
if(r and r.r_type and r.r_type ~= "dialog") then
if(text ~= "") then
text = text.."\n "
end
-- the dialog effect is something diffrent
text = text.."# effect "..tostring(r_id).." "..tostring(yl_speak_up.show_effect(r))
end
end
end
return text
end
-- choices are a bit complicated as they may contain alternate_text that is to be
-- displayed instead (in yl_speak_up) and before (in ink) shwoing the target dialog text;
-- also, the divert_to target dialog may need to be rewritten
yl_speak_up.export_to_ink.print_choice = function(lines, choice_text, n_id, start_dialog,
alternate_text, divert_to)
-- don't repeat the text of the choice in the output when running ink
table.insert(lines, "\n+ [")
table.insert(lines, choice_text)
table.insert(lines, "]")
-- dialogs, actions and effects can have an alternate_text with which they override the
-- text of the target_dialog/divert_to;
-- this isn't perfect as alternate_text supports $TEXT$ for inserting the text of the
-- target dialog anywhere in the alternate_text - while ink will print out this alternate_text
-- first and then that of the target dialog/divert_to
if(alternate_text and alternate_text ~= "") then
-- a new line and some indentation makes this more readable
table.insert(lines, "\n ")
table.insert(lines, alternate_text)
-- write the divert into a new line as well
table.insert(lines, "\n ")
end
table.insert(lines, " -> ")
if(not(start_dialog) or start_dialog == "") then
start_dialog = "d_1"
end
if(not(divert_to) or divert_to == "") then
-- go back to the start dialog (the start dialog may have been changed)
divert_to = tostring(n_id).."_"..tostring(start_dialog)
elseif(divert_to == "d_end" or divert_to == tostring(n_id).."_d_end") then
-- go back to choosing between talking to NPC and end
divert_to = tostring(n_id).."_main"
elseif(string.sub(divert_to, 1, 2) ~= "n_") then
-- make sure it is prefixed with the n_id
divert_to = tostring(n_id).."_"..tostring(divert_to)
end
table.insert(lines, divert_to)
end
-- this prints the dialog as a knot - but without choices (those are added to the lines table later)
-- d: dialog
yl_speak_up.export_to_ink.print_dialog_knot = function(lines, n_id, d_id, d)
local knot_name = tostring(n_id).."_"..tostring(d_id)
ink_export.print_knot_name(lines, knot_name)
-- many characters at the start of a line have a special meaning;
-- hopefully they will not be obstrusive later on;
-- TODO: in order to be on the safe side: add a ":" in front of each line?
local t = d.d_text or ""
if(t == "") then
-- entirely empty text for knots does not work
t = "No text."
end
-- t = string.gsub(t, "\n([:>=])", "\n %1")
table.insert(lines, "\n")
table.insert(lines, t)
table.insert(lines, "\n")
return knot_name
end
-- a: action
yl_speak_up.export_to_ink_action_knot = function(lines, a, n_d_id, o_id, next_target)
table.insert(lines, "\n\n=== ")
table.insert(lines, n_d_id.."_"..tostring(o_id).."_"..tostring(a.a_id))
table.insert(lines, " ===")
table.insert(lines, "\n:action:")
-- actions can fail *and* be aborted by the player; in order to model that in ink, we add
-- a knot for each action
-- Parameter:
-- a action
yl_speak_up.export_to_ink.print_action_knot = function(lines, n_id, d_id, o_id, start_dialog,
a, alternate_text_on_success, next_target)
local knot_name = tostring(n_id).."_"..tostring(d_id).."_"..tostring(o_id).."_"..tostring(a.a_id)
ink_export.print_knot_name(lines, knot_name)
table.insert(lines, "\n:action: ")
table.insert(lines, a.a_id)
table.insert(lines, " ")
table.insert(lines, yl_speak_up.show_action(a))
table.insert(lines, "\n+ [Action was successful] -> ")
table.insert(lines, next_target)
table.insert(lines, "\n+ [Action failed] -> ")
table.insert(lines, tostring(a.a_on_failure or next_target or "ERROR"))
table.insert(lines, "\n+ [Back] -> ")
table.insert(lines, n_d_id)
ink_export.print_choice(lines, "Action was successful", n_id, start_dialog,
alternate_text_on_success, next_target)
ink_export.print_choice(lines, "Action failed", n_id, start_dialog,
a.alternate_text, a.a_on_failure)
ink_export.print_choice(lines, "Back", n_id, start_dialog,
nil, tostring(n_id).."_"..tostring(d_id))
return knot_name
end
-- r: effect/result
-- r_prev: previous effect
yl_speak_up.export_to_ink_effect_knot = function(lines, r, n_d_id, o_id, next_target, r_prev)
table.insert(lines, "\n\n=== ")
table.insert(lines, n_d_id.."_"..tostring(o_id).."_"..tostring(r.r_id))
table.insert(lines, " ===")
table.insert(lines, "\n:effect:")
-- there is a special on_failure effect that can lead to a diffrent target dialog and print
-- out a diffrent alternate_text if the *previous* effect failed; in order to model that in
-- ink, we add a knot for such on_failure effects
-- Parameter:
-- r effect/result
-- r_prev previous effect
yl_speak_up.export_to_ink.print_effect_knot = function(lines, n_id, d_id, o_id, start_dialog,
r, r_prev, alternate_text_on_success, next_target)
local knot_name = tostring(n_id).."_"..tostring(d_id).."_"..tostring(o_id).."_"..tostring(r.r_id)
ink_export.print_knot_name(lines, knot_name)
table.insert(lines, "\n:effect: ")
table.insert(lines, r.r_id)
table.insert(lines, " ")
-- show text of the *previous effect* - because that is the one which may have failed:
table.insert(lines, yl_speak_up.show_effect(r))
table.insert(lines, "\nThe previous effect was: ")
table.insert(lines, r_prev.r_id)
table.insert(lines, " ")
-- show text of the *previous effect* - because that is the one which may have failed:
table.insert(lines, yl_speak_up.show_effect(r_prev))
table.insert(lines, "\n+ [Effect was successful] -> ")
table.insert(lines, next_target)
table.insert(lines, "\n+ [Effect failed] -> ")
table.insert(lines, tostring(r.r_value or "ERROR"))
ink_export.print_choice(lines, "Effect was successful", n_id, start_dialog,
alternate_text_on_success, next_target)
ink_export.print_choice(lines, "Effect failed", n_id, start_dialog,
r.alternate_text, r.r_value)
return knot_name
end
yl_speak_up.export_to_ink_language = function(dialog, n_id)
local start_dialog = yl_speak_up.get_start_dialog_id(dialog)
if(not(start_dialog)) then
start_dialog = "d_1"
end
local main = tostring(n_id).."_main"
local tmp = {"-> ", main,
"\n=== ", main, " ===\n",
"\n=== ", main, " ===",
"\nWhat do you wish to do?",
"\n+ Talk to ", tostring(dialog.n_npc), " -> ", tostring(n_id).."_d_1",
"\n+ Talk to ", tostring(dialog.n_npc), " -> ", tostring(n_id).."_"..tostring(start_dialog),
"\n+ End -> END"}
local sorted_d_list = yl_speak_up.sort_keys(dialog.n_dialogs or {}, true)
for i, d_id in ipairs(sorted_d_list) do
-- store the knots for actions and effects here:
local tmp2 = {}
local d = dialog.n_dialogs[d_id]
local n_d_id = tostring(n_id).."_"..tostring(d_id)
yl_speak_up.export_to_ink_dialog_knot(tmp, d, n_d_id)
-- print the dialog knot, but without choices (those we add in the following loop)
local this_knot_name = ink_export.print_dialog_knot(tmp, n_id, d_id, d)
-- iterate over all options
local sorted_o_list = yl_speak_up.get_sorted_options(dialog.n_dialogs[d_id].d_options or {}, "o_sort")
for j, o_id in ipairs(sorted_o_list) do
local o_data = d.d_options[o_id]
local target_action = nil
local target_effect = nil
local target_dialog = nil
local sorted_a_list = yl_speak_up.sort_keys(o_data.actions or {})
local sorted_e_list = yl_speak_up.sort_keys(o_data.o_results or {})
-- is there an action? That takes precedence.
if(#sorted_a_list > 0) then
-- this action dialog knot needs to be created:
target_action = n_d_id.."_"..tostring(o_id).."_"..tostring(sorted_a_list[1])
end
-- is there an effect/result of type on_failure?
local effect_list = {}
for k, r_id in ipairs(sorted_e_list) do
local r = o_data.o_results[r_id]
-- checking for previous effect failed?
if(not(target_effect)
and k > 1 and r and r.r_type and r.r_type == "on_failure") then
-- this effect dialog knot needs to be created:
target_effect = n_d_id.."_"..tostring(o_id).."_"..tostring(r_id)
elseif(target_effect
and k > 1 and r and r.r_type and r.r_type == "on_failure") then
-- collect all effects that need their own knots
table.insert(effect_list, target_effect)
-- normal target dialog?
elseif(not(target_dialog)
and r and r.r_type and r.r_type == "dialog") then
target_dialog = tostring(n_id).."_"..tostring(r.r_value)
end
end
table.insert(effect_list, target_dialog)
local target = (target_action or target_effect or target_dialog or "d_1")
table.insert(tmp, "+ [")
table.insert(tmp, o_data.o_text_when_prerequisites_met)
table.insert(tmp, "] -> ")
table.insert(tmp, target)
table.insert(tmp, "\n")
-- add the actions
local next_target = "ERROR"
for k, a_id in ipairs(sorted_a_list) do
local a = o_data.actions[a_id]
if(k < #sorted_a_list) then
next_target = n_d_id.."_"..tostring(o_id).."_"..tostring(sorted_a_list[k+1])
elseif(target_effect) then
next_target = target_effect
elseif(target_dialog) then
next_target = target_dialog
else
next_target = "d_1"
end
yl_speak_up.export_to_ink_action_knot(tmp2, a, n_d_id, o_id, next_target)
end
-- add the effects
-- we will get alternate_text from the dialog result later on
local alternate_text_on_success = ""
local target_dialog = nil
-- what is the normal target dialog/divert (in ink language) of this dialog?
for k, r_id in ipairs(sorted_e_list) do
local r = o_data.o_results[r_id]
-- checking for previous effect failed?
if(not(target_effect)
and k > 1 and r and r.r_type and r.r_type == "on_failure") then
-- this effect dialog knot needs to be created:
if(effect_list > 0) then
next_target = effect_list[1]
table.remove(effect_list, 1)
else
next_target = "ERROR"
end
yl_speak_up.export_to_ink_effect_knot(tmp2, r, n_d_id, o_id, next_target, o_data.o_results[sorted_e_list[k-1]])
if(r and r.r_type and r.r_type == "dialog") then
target_dialog = tostring(n_id).."_"..tostring(r.r_value)
alternate_text_on_success = r.alternate_text or ""
end
end
end
-- iterate backwards through the effects and serach for on_failure;
-- the first effect cannot be an on_failure effect because on_failure effects
-- decide on failure/success of the *previous* effect
for k = #sorted_e_list, 2, -1 do
local r_id = sorted_e_list[k]
local r = o_data.o_results[r_id]
if(r and r.r_type and r.r_type == "on_failure") then
local r_prev = o_data.o_results[sorted_e_list[k-1]]
-- *after* this effect we still need to execute all the other
-- remaining effects (read: add them as tag)
alternate_text_on_success = ink_export.add_effect_tags(
alternate_text_on_success,
sorted_e_list, o_data.o_results, k)
-- whatever dialog comes previously - the dialog, an action, or
-- another on_failure dialog - needs to lead to this dialog
target_dialog = ink_export.print_effect_knot(tmp2,
n_id, d_id, o_id, start_dialog,
r, r_prev,
alternate_text_on_success, target_dialog)
-- we have dealt with the alternate text (it will only be shown
-- in the last on_failure dialog before we go to the target)
alternate_text_on_success = ""
end
end
-- add the remaining effects
alternate_text_on_success = ink_export.add_effect_tags(
alternate_text_on_success,
sorted_e_list, o_data.o_results, 1)
-- iterate backwards through the actions (though usually only one is supported)
for k = #sorted_a_list, 1, -1 do
local a_id = sorted_a_list[k]
local a = o_data.actions[a_id]
target_dialog = ink_export.print_action_knot(tmp2,
n_id, d_id, o_id, start_dialog,
a,
alternate_text_on_success, target_dialog)
-- has been dealt with
alternate_text_on_success = ""
end
-- what remains is to print the option/choice itself
ink_export.print_choice(tmp,
-- TODO: deal with when_prerequisites_not_met
o_data.o_text_when_prerequisites_met, n_id, start_dialog,
alternate_text_on_success, target_dialog)
end -- dealt with the option
table.insert(tmp, "\n")
-- add way to end talking to the NPC
table.insert(tmp, "\n+ Farewell! -> "..tostring(n_id).."_main")
ink_export.print_choice(tmp, "Farewell!", n_id, start_dialog,
nil, tostring(n_id).."_main")
-- add the knots for actions and effects for this dialog and all its options:
for _, line in ipairs(tmp2) do
table.insert(tmp, line)

View File

@ -224,6 +224,13 @@ yl_speak_up.get_fs_talkdialog_line = function(
sb_v.o_text_when_prerequisites_not_met, dialog, pname))
end
if(t or t_alt) then
-- some options can be visited only once; talking to the NPC anew resets that
-- (not stored persistently)
if(t and sb_v.visits and sb_v.visits > 0
and sb_v.o_visit_only_once and sb_v.o_visit_only_once == 1) then
t_alt = minetest.formspec_escape("[Done] ")..t
t = nil
end
-- actually show the button
h = yl_speak_up.add_edit_button_fs_talkdialog(formspec, h,
"button_" .. oid,
@ -477,6 +484,9 @@ yl_speak_up.get_fs_talkdialog = function(player, n_id, d_id, alternate_text, rec
"label[0.2,0.5;Ups! Something went wrong. Please try again.]"
end
-- how often has the player visted this dialog?
yl_speak_up.count_visits_to_dialog(pname)
-- evaluate the preconditions of each option and check if the option can be offered
local allowed = yl_speak_up.calculate_displayable_options(pname, active_dialog.d_options,
-- avoid loops by limiting max recoursion depths for autoanswers

View File

@ -709,6 +709,13 @@ yl_speak_up.check_if_dialog_has_option = function(dialog, d_id, o_id)
and dialog.n_dialogs[d_id].d_options[o_id])
end
-- checks if dialog exists
yl_speak_up.check_if_dialog_exists = function(dialog, d_id)
return (dialog and d_id
and dialog.n_dialogs
and dialog.n_dialogs[d_id])
end
-- has the player the right privs?
-- this is used for the "I am your master" talk based configuration; *NOT* for the staffs!

View File

@ -58,6 +58,7 @@ To get started, best install `yl_speak_up`, `npc_talk`, `mobs_redo` and `mobs_np
1. [1.20 Logging](#logging)
1. [1.21 Export/Import](#export)
1. [1.22 Storing internal notes](#notes)
1. [1.23 Counting visits to dialogs and options](#visit-counter)
2. [For moderators: Generic NPCs](#for-moderators)
1. [2.1 Generic behaviour](#generic_behaviour)
@ -77,6 +78,7 @@ To get started, best install `yl_speak_up`, `npc_talk`, `mobs_redo` and `mobs_np
1. [3.11 Additional custom replacements (in addition to NPC name, player name etc)](#add-simple-variables)
1. [3.12 Custom Preconditions, Actions and Effects](#precon_action_effect)
1. [3.13 Integration into your own NPC/mob mods](#integration)
1. [3.14 Dynamic dialog](#dynamic-dialog)
4. [Future](#future)
@ -325,6 +327,7 @@ special meaning:
| `d_end` | End the conversation (i.e. after teleporting the player). |
| `d_got_item` | The NPC [got something](#npc_wants) and is trying to decide what to do with it. |
| `d_trade` | [Trade](#trading-simple)-specific options. I.e. crafting new items when stock is low.|
| `d_dynamic` | [Dynamic dialog](#dynamic-dialog) that is changed on the fly. Each NPC has exactly one. |
### 1.14 Trading (simple)
@ -532,6 +535,29 @@ this NPC - its character, how it behaves and talks, who his friends are etc.
These internal notes are only shown to players who can edit this NPC.
### 1.23 Counting visits to dialogs and options
<a name="visit-counter"></a>
Whenever a dialog text is displayed to the player and whenever the player
selects an option which is successful (no aborted actions or `on_failure`
effects inbetween), a counter called `visits` is increased by one for that
dialog or option.
This information is *not* persistent! When the player leaves the talk by
choosing `Farewell!` or pressing ESC all visit information is lost.
The feature can be used to help players see which options they've tried
and which path they've followed. If an option is set to `*once*` instead
of the default `often` in the edit options dialog, that option can only
be selected *once* each time the player talks to this NPC. After that the
option gets greyed out and displays "[Done]" followed by the normal option
text.
Visits are not counted in edit mode and not counted for generic dialogs.
There are custom preconditions for checking the number of visits to a dialog
and/or option.
## 2. For moderators: Generic NPCs
<a name="for-moderators"></a>
@ -802,7 +828,16 @@ You also need to make sure that the textures of your mob can be edited. In order
* call `table.insert(yl_speak_up.emulate_orders_on_rightclick, <entity_name>)` if your mob is a `mobs_redo` one and can stand, follow (its owner) and walk around randomly. As you override `on_rightclick`, this setting will make sure to add buttons to emulate previous behaviour shown when clicking on the NPC.
### 3.14 Dynamic dialogs
<a name="dynamic-dialog"></a>
Sometimes you may have to generate a dialog on the fly and/or wish for more dynamic texts and answers.
Each NPC has a `d_dynamic` dialog that will never be saved with the NPC data and that can be changed each time the player selected an option.
This is particulary useful for using external functions for generating dialog texts and options/answers plus their reactions.
Warning: This feature is not finished yet and undergoing heavy changes. Do not rely on its functionality yet!
## 4. Future