forked from Sokomine/yl_speak_up
		
	
		
			
				
	
	
		
			276 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			276 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| -- 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
 | |
| 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, 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)
 | |
| 	return knot_name
 | |
| end
 | |
| 
 | |
| -- 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))
 | |
| 
 | |
| 	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
 | |
| 
 | |
| 
 | |
| -- 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))
 | |
| 
 | |
| 	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, " ===",
 | |
| 		"\nWhat do you wish to do?",
 | |
| 		"\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]
 | |
| 		-- 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 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 {})
 | |
| 
 | |
| 			-- 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]
 | |
| 				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
 | |
| 
 | |
| 			-- 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
 | |
| 		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)
 | |
| 		end
 | |
| 	end
 | |
| 	return table.concat(tmp, "")
 | |
| end
 |