Compare commits
8 Commits
5c7ffe3739
...
f503d436dc
Author | SHA1 | Date |
---|---|---|
Sokomine | f503d436dc | |
Sokomine | a8b92a754f | |
Sokomine | 189165dd22 | |
Sokomine | 6381ea59b9 | |
Sokomine | c2107d0bf3 | |
Sokomine | b076e6a2f5 | |
Sokomine | 2d677127dd | |
Sokomine | 24d3a05ce0 |
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
-----------------------------------------------------------------------------
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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*)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!
|
||||
|
|
35
readme.md
35
readme.md
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue