export to ink: support for variables (to a degree) and precondtions (to a degree)

This commit is contained in:
Sokomine 2024-03-02 19:54:21 +01:00
parent 1c89bd7dcf
commit 13de73ecde

View File

@ -1,7 +1,6 @@
-- helper functions for export_to_ink_language:
-- 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
@ -58,18 +57,35 @@ end
-- 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, only_once, label)
-- don't repeat the text of the choice in the output when running ink
alternate_text, divert_to, only_once, label,
precondition_list, effect_list)
-- usually, options/answers/choices can be selected multiple times;
-- we support the default ink way of "*" as well (but only until the player stops talking,
-- not persistently stored)
if(not(only_once)) then
table.insert(lines, "\n+ ")
else
table.insert(lines, "\n* ")
end
-- helps to regcognize what has been changed how when importing again
if(label and label ~= "") then
table.insert(lines, "(")
table.insert(lines, tostring(label))
table.insert(lines, ") ")
end
-- are there any preconditions which can be handled by ink? most can not as they can
-- only be determined ingame (i.e. state of a block); even the value of variables may
-- have been changed externally
if(precondition_list and #precondition_list > 0) then
for _, p_text in ipairs(precondition_list) do
if(p_text ~= "") then
table.insert(lines, "{ ")
table.insert(lines, p_text)
table.insert(lines, " } ")
end
end
end
-- don't repeat the text of the choice in the output when running ink
table.insert(lines, "[")
table.insert(lines, choice_text)
table.insert(lines, "]")
@ -85,6 +101,16 @@ yl_speak_up.export_to_ink.print_choice = function(lines, choice_text, n_id, star
-- write the divert into a new line as well
table.insert(lines, "\n ")
end
-- setting a variable to a value is something we can model in ink as well
if(effect_list and #effect_list > 0) then
for _, e_text in ipairs(effect_list) do
table.insert(lines, "\n ~ ")
table.insert(lines, e_text)
end
-- the divert needs to be put into a new line
table.insert(lines, "\n")
end
-- actually go to the dialog this option leads to
table.insert(lines, " -> ")
if(not(start_dialog) or start_dialog == "") then
start_dialog = "d_1"
@ -182,6 +208,117 @@ yl_speak_up.export_to_ink.print_effect_knot = function(lines, n_id, d_id, o_id,
end
-- which variables are used by this NPC?
yl_speak_up.export_to_ink.print_variables_used = function(lines, dialog, n_id, pname)
if(not(dialog) or not(dialog.n_dialogs)) then
return
end
local vars_used = {}
for d_id, d_data in pairs(dialog.n_dialogs or {}) do
for o_id, o_data in pairs(d_data.d_options or {}) do
-- variables may be used in preconditions
for p_id, p in pairs(o_data.o_prerequisites or {}) do
-- we are checking the state of a variable
if(p and p.p_type and p.p_type == "state") then
-- store as key in order to avoid duplicates
vars_used[ p.p_variable ] = true
-- properties are comparable to variables
elseif(p and p.p_type and p.p_type == "property") then
vars_used[ "property "..p.p_value ] = true
end
end
for r_id, r in pairs(o_data.o_results or {}) do
if(r and r.r_type and r.r_type == "state") then
vars_used[ r.r_variable ] = true
elseif(r and r.r_type and r.r_type == "property") then
vars_used[ "property "..r.r_value ] = true
end
end
end
end
table.insert(lines, "\n")
-- we stored as key/value in order to avoid duplicates
for var_name, _ in pairs(vars_used) do
-- replace blanks with an underscore in an attempt to turn it into a legal var name
-- (this is not really sufficient as var names in yl_speak_up are just strings,
-- while the ink language expects sane var names like other lanugages)
-- TODO: this is not necessarily a legitimate var name!
local parts = string.split(var_name, " ")
table.remove(parts, 1)
local v_name = table.concat(parts, "_")
-- stor it for later use
vars_used[var_name] = v_name
-- add the variable as a variable to INK
table.insert(lines, "\nVAR ")
table.insert(lines, v_name)
table.insert(lines, " = NONE") -- start with undefined/nil (we don't know the stored value)
end
table.insert(lines, "\n")
return vars_used
end
-- which preconditions and effects can be modelled in ink?
--
-- in singleplayer adventures, properties can be relevant as well;
-- in multiplayer, other players may affect the state of the property
--
-- *some* functions may be relevant here:
-- (but not for variables)
-- * compare a variable with a variable
-- * counted dialog option visits
-- * counted option visits
--
-- types "true" and "false" can be relevant later on
-- small helper function
local var_with_operator = function(liste, var_name, op, var_cmp_value, vars_used)
-- visits are not stored as variables in ink
if(not(vars_used[var_name])) then
vars_used[var_name] = var_name
end
if(op == "~=") then
op = "!="
end
if(op=="==" or op=="!=" or op==">=" or op==">" or op=="<=" or op==">") then
table.insert(liste, tostring(vars_used[var_name]).." ".. op.." "..tostring(var_cmp_value))
elseif(op=="not") then
table.insert(liste, "not "..tostring(vars_used[var_name]))
elseif(op=="is_set") then
table.insert(liste, tostring(vars_used[var_name]))
elseif(op=="is_unset") then
table.insert(liste, tostring(vars_used[var_name]).." == NONE")
end
-- the following values for op cannot really be checked here and are not printed:
-- "more_than_x_seconds_ago","less_than_x_seconds_ago",
-- "quest_step_done", "quest_step_not_done"
end
yl_speak_up.export_to_ink.translate_precondition_list = function(dialog, preconditions, vars_used, n_id)
-- collect preconditions that may work in ink
local liste = {}
-- variables may be used in preconditions
for p_id, p in pairs(preconditions or {}) do
if(p and p.p_type and p.p_type == "state") then
-- state changes of variables may mostly work in ink as well
var_with_operator(liste, p.p_variable, p.p_operator, p.p_var_cmp_value, vars_used)
elseif(p and p.p_type and p.p_type == "property") then
-- same with properties
var_with_operator(liste, p.p_value, p.p_operator, p.p_var_cmp_value, vars_used)
elseif(p and p.p_type and p.p_type == "evaluate" and p.p_value == "counted_visits_to_option") then
-- simulate the visit counter that ink has in yl_speak_up
local tmp_var_name = n_id.."_"..p.p_param1.."_"..p.p_param2
var_with_operator(liste, tmp_var_name, p.p_operator, p.p_var_cmp_value, vars_used)
elseif(p and p.p_type and p.p_type == "true") then
table.insert(liste, p.p_type)
elseif(p and p.p_type and p.p_type == "false") then
table.insert(liste, p.p_type)
end
end
return liste
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
@ -194,6 +331,9 @@ yl_speak_up.export_to_ink_language = function(dialog, n_id)
"\nWhat do you wish to do?",
"\n+ Talk to ", tostring(dialog.n_npc), " -> ", tostring(n_id).."_"..tostring(start_dialog),
"\n+ End -> END"}
local vars_used = ink_export.print_variables_used(tmp, dialog, n_id, pname)
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:
@ -265,13 +405,18 @@ yl_speak_up.export_to_ink_language = function(dialog, n_id)
alternate_text_on_success = ""
end
-- which preconditions can be translated to ink?
local p_list = ink_export.translate_precondition_list(dialog, o_data.o_prerequisites,
vars_used, n_id)
local e_list = {} --TODO
-- 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,
o_data.o_visit_only_once,
o_id)
o_id, p_list, e_list)
end -- dealt with the option
table.insert(tmp, "\n")
-- add way to end talking to the NPC