From 9b76be0fedeb5837d6728cd62972b02a9f34b7dc Mon Sep 17 00:00:00 2001 From: Sebastian Schmittner Date: Sat, 30 Nov 2024 23:57:01 +0100 Subject: [PATCH] Added linearize story feature to editor --- commons/utils.js | 21 +++++++++ editor/code.js | 108 +++++++++++++++++++++++++++++++++++++++++++++- editor/index.html | 1 + viewer/code.js | 23 ++-------- viewer/utils.js | 1 + 5 files changed, 133 insertions(+), 21 deletions(-) create mode 120000 viewer/utils.js diff --git a/commons/utils.js b/commons/utils.js index ec100c0..9131c11 100644 --- a/commons/utils.js +++ b/commons/utils.js @@ -22,3 +22,24 @@ export function create_element_with_classes_and_attributes( } return element; } + +export function replace_variables(text, variables) { + if (!variables || !text) { + return text; + } + var re = text; + for (const key in variables) { + re = re.replaceAll("${" + key + "}", variables[key]); + } + return re; + } + + export function get_text_from_section(section, variables){ + let text = ""; + if (section?.text_lines) { + text = section.text_lines.join("\n"); + } else if (section?.text) { + text = section.text; + } + return replace_variables(text, variables); + } \ No newline at end of file diff --git a/editor/code.js b/editor/code.js index 326d378..6d6c907 100644 --- a/editor/code.js +++ b/editor/code.js @@ -6,7 +6,10 @@ cytoscape.use(cytoscapeKlay); import { toast_alert, toast_ok } from "./toast.js"; import { supported_actions } from "./common.js"; -import { create_element_with_classes_and_attributes } from "./utils.js"; +import { + create_element_with_classes_and_attributes, + get_text_from_section, +} from "./utils.js"; const data_url_regexp = /^data:image\/([a-z]*);base64,(.*)$/; @@ -925,6 +928,10 @@ document .getElementById("redraw_button") .addEventListener("click", redraw_adventure_graph); +document + .getElementById("linearize_button") + .addEventListener("click", create_linear_story); + document.addEventListener("keydown", handle_global_key_down); async function load_example() { @@ -940,6 +947,105 @@ async function load_example() { } } +function create_linear_story() { + const start_at = prompt("Start at section:", "1"); + if (!start_at) { + return; + } + const end_at = prompt("Finish at section:"); + if (!end_at) { + return; + } + const passing_through = []; + var passing = prompt( + "Add section to pass through list (leave empty to finish):" + ); + while (passing) { + passing_through.push(passing); + passing = prompt( + "Add section to pass through list (leave empty to finish):" + ); + } + toast_ok("Generating linear story..."); + console.debug( + "Creating linearized story from", + start_at, + "to", + end_at, + "passing_through", + passing_through + ); + const linearized_history = depth_first_search( + [start_at], + end_at, + passing_through + ); + console.debug("linearized_history", linearized_history); + toast_ok("Found a linear story. Generating Markdown..."); + + const markdown = markdown_from_section_id_list(linearized_history); + + trigger_data_dl( + "data:text/plain;charset=utf-8," + encodeURIComponent(markdown), + get_file_safe_title() + ".md" + ); +} + +function markdown_from_section_id_list(section_ids) { + let md = ""; + for (const id of section_ids) { + console.debug("adding section to markdown", id); + md += get_text_from_section(story.sections?.[id], story?.state?.variables); + md += "\n\n"; + if (story.sections?.[id]?.media?.src) { + md += "![](" + story.sections?.[id]?.media?.src + ")\n\n"; + } + } + return md; +} + +function depth_first_search(linearized_history, end_at, passing_through) { + if (linearized_history[linearized_history.length - 1] == end_at) { + console.debug("dfs reached target", end_at); + if (passing_through) { + for (const passing of passing_through) { + if (!linearized_history.includes(passing)) { + console.debug(linearized_history, "not passing through", passing); + return null; + } + } + } + console.debug( + linearized_history, + "is passing through all of", + passing_through + ); + return linearized_history; + } + const current_section_id = linearized_history[linearized_history.length - 1]; + const current_section = story.sections[current_section_id]; + if (!current_section) { + toast_alert("No section " + current_section_id); + return null; + } + if (!current_section.next) { + console.debug("Dead end", current_section_id); + return null; + } + for (const next of current_section.next) { + const found_path = depth_first_search( + [...linearized_history, next.next], + end_at, + passing_through + ); + if (found_path) { + return found_path; + } + } + console.debug("No continuation possible for", linearized_history); + return null; +} + function on_load() { redraw_adventure_graph(); load_variables_menu(); diff --git a/editor/index.html b/editor/index.html index f94e201..32e4840 100644 --- a/editor/index.html +++ b/editor/index.html @@ -33,6 +33,7 @@
  • Load Adventure
  • Save Adventure in one file
  • Save Adventure text and pictures separately
  • +
  • Create Linear Story