text: /**
  🚀
  @file dialog jump
  @summary jump from one dialog entry to another
  @license MIT
  @version 13.3.6
  @requires 7.0
  @author Sean S. LeBlanc
  
  @description
  This can be used to simplify complex dialog
  by moving portions to self-contained dialog entries,
  and then jumping to the appropriate id when necessary.
  
  You can also provide raw dialog text instead of an id;
  Functionally this isn't much different from writing raw dialog text,
  but it has some uses for advanced cases (e.g. when combined with dialog choices)
  
  Usage:
  	(jump "dialogId")
  	(jumpNow "dialogId")
  	(jump "dialog to print")
  	(jumpNow "dialog to print")
  
  Note: be careful of infinite loops, e.g.
  DLG_infinite_loop
  """
  this will print forever(jump "DLG_infinite_loop")
  """
  
  HOW TO USE:
  Copy-paste into a script tag after the bitsy source
  */
  (function (bitsy) {
  'use strict';
  
  bitsy = bitsy && Object.prototype.hasOwnProperty.call(bitsy, 'default') ? bitsy['default'] : bitsy;
  
  /**
  @file utils
  @summary miscellaneous bitsy utilities
  @author Sean S. LeBlanc
  */
  
  /*
  Helper used to replace code in a script tag based on a search regex
  To inject code without erasing original string, using capturing groups; e.g.
  	inject(/(some string)/,'injected before $1 injected after')
  */
  function inject(searchRegex, replaceString) {
  	// find the relevant script tag
  	var scriptTags = document.getElementsByTagName('script');
  	var scriptTag;
  	var code;
  	for (var i = 0; i < scriptTags.length; ++i) {
  		scriptTag = scriptTags[i];
  		var matchesSearch = scriptTag.textContent.search(searchRegex) !== -1;
  		var isCurrentScript = scriptTag === document.currentScript;
  		if (matchesSearch && !isCurrentScript) {
  			code = scriptTag.textContent;
  			break;
  		}
  	}
  
  	// error-handling
  	if (!code) {
  		throw new Error('Couldn\'t find "' + searchRegex + '" in script tags');
  	}
  
  	// modify the content
  	code = code.replace(searchRegex, replaceString);
  
  	// replace the old script tag with a new one using our modified code
  	var newScriptTag = document.createElement('script');
  	newScriptTag.textContent = code;
  	scriptTag.insertAdjacentElement('afterend', newScriptTag);
  	scriptTag.remove();
  }
  
  /**
   * Helper for getting an array with unique elements
   * @param  {Array} array Original array
   * @return {Array}       Copy of array, excluding duplicates
   */
  function unique(array) {
  	return array.filter(function (item, idx) {
  		return array.indexOf(item) === idx;
  	});
  }
  
  /**
  
  @file kitsy-script-toolkit
  @summary makes it easier and cleaner to run code before and after Bitsy functions or to inject new code into Bitsy script tags
  @license WTFPL (do WTF you want)
  @requires Bitsy Version: 4.5, 4.6
  @author @mildmojo
  
  @description
  HOW TO USE:
    import {before, after, inject, addDialogTag, addDeferredDialogTag} from "./helpers/kitsy-script-toolkit";
  
    before(targetFuncName, beforeFn);
    after(targetFuncName, afterFn);
    inject(searchRegex, replaceString);
    addDialogTag(tagName, dialogFn);
    addDeferredDialogTag(tagName, dialogFn);
  
    For more info, see the documentation at:
    https://github.com/seleb/bitsy-hacks/wiki/Coding-with-kitsy
  */
  
  
  // Ex: inject(/(names.sprite.set\( name, id \);)/, '$1console.dir(names)');
  function inject$1(searchRegex, replaceString) {
  	var kitsy = kitsyInit();
  	if (
  		!kitsy.queuedInjectScripts.some(function (script) {
  			return searchRegex.toString() === script.searchRegex.toString() && replaceString === script.replaceString;
  		})
  	) {
  		kitsy.queuedInjectScripts.push({
  			searchRegex: searchRegex,
  			replaceString: replaceString,
  		});
  	} else {
  		console.warn('Ignored duplicate inject');
  	}
  }
  
  // Ex: before('load_game', function run() { alert('Loading!'); });
  //     before('show_text', function run(text) { return text.toUpperCase(); });
  //     before('show_text', function run(text, done) { done(text.toUpperCase()); });
  function before(targetFuncName, beforeFn) {
  	var kitsy = kitsyInit();
  	kitsy.queuedBeforeScripts[targetFuncName] = kitsy.queuedBeforeScripts[targetFuncName] || [];
  	kitsy.queuedBeforeScripts[targetFuncName].push(beforeFn);
  }
  
  // Ex: after('load_game', function run() { alert('Loaded!'); });
  function after(targetFuncName, afterFn) {
  	var kitsy = kitsyInit();
  	kitsy.queuedAfterScripts[targetFuncName] = kitsy.queuedAfterScripts[targetFuncName] || [];
  	kitsy.queuedAfterScripts[targetFuncName].push(afterFn);
  }
  
  function kitsyInit() {
  	// return already-initialized kitsy
  	if (bitsy.kitsy) {
  		return bitsy.kitsy;
  	}
  
  	// Initialize kitsy
  	bitsy.kitsy = {
  		queuedInjectScripts: [],
  		queuedBeforeScripts: {},
  		queuedAfterScripts: {}
  	};
  
  	var oldStartFunc = bitsy.startExportedGame;
  	bitsy.startExportedGame = function doAllInjections() {
  		// Only do this once.
  		bitsy.startExportedGame = oldStartFunc;
  
  		// Rewrite scripts and hook everything up.
  		doInjects();
  		applyAllHooks();
  
  		// Start the game
  		bitsy.startExportedGame.apply(this, arguments);
  	};
  
  	return bitsy.kitsy;
  }
  
  
  function doInjects() {
  	bitsy.kitsy.queuedInjectScripts.forEach(function (injectScript) {
  		inject(injectScript.searchRegex, injectScript.replaceString);
  	});
  	_reinitEngine();
  }
  
  function applyAllHooks() {
  	var allHooks = unique(Object.keys(bitsy.kitsy.queuedBeforeScripts).concat(Object.keys(bitsy.kitsy.queuedAfterScripts)));
  	allHooks.forEach(applyHook);
  }
  
  function applyHook(functionName) {
  	var functionNameSegments = functionName.split('.');
  	var obj = bitsy;
  	while (functionNameSegments.length > 1) {
  		obj = obj[functionNameSegments.shift()];
  	}
  	var lastSegment = functionNameSegments[0];
  	var superFn = obj[lastSegment];
  	var superFnLength = superFn ? superFn.length : 0;
  	var functions = [];
  	// start with befores
  	functions = functions.concat(bitsy.kitsy.queuedBeforeScripts[functionName] || []);
  	// then original
  	if (superFn) {
  		functions.push(superFn);
  	}
  	// then afters
  	functions = functions.concat(bitsy.kitsy.queuedAfterScripts[functionName] || []);
  
  	// overwrite original with one which will call each in order
  	obj[lastSegment] = function () {
  		var returnVal;
  		var args = [].slice.call(arguments);
  		var i = 0;
  
  		function runBefore() {
  			// All outta functions? Finish
  			if (i === functions.length) {
  				return returnVal;
  			}
  
  			// Update args if provided.
  			if (arguments.length > 0) {
  				args = [].slice.call(arguments);
  			}
  
  			if (functions[i].length > superFnLength) {
  				// Assume funcs that accept more args than the original are
  				// async and accept a callback as an additional argument.
  				return functions[i++].apply(this, args.concat(runBefore.bind(this)));
  			} else {
  				// run synchronously
  				returnVal = functions[i++].apply(this, args);
  				if (returnVal && returnVal.length) {
  					args = returnVal;
  				}
  				return runBefore.apply(this, args);
  			}
  		}
  
  		return runBefore.apply(this, arguments);
  	};
  }
  
  function _reinitEngine() {
  	// recreate the script and dialog objects so that they'll be
  	// referencing the code with injections instead of the original
  	bitsy.scriptModule = new bitsy.Script();
  	bitsy.scriptInterpreter = bitsy.scriptModule.CreateInterpreter();
  
  	bitsy.dialogModule = new bitsy.Dialog();
  	bitsy.dialogRenderer = bitsy.dialogModule.CreateRenderer();
  	bitsy.dialogBuffer = bitsy.dialogModule.CreateBuffer();
  }
  
  // Rewrite custom functions' parentheses to curly braces for Bitsy's
  // interpreter. Unescape escaped parentheticals, too.
  function convertDialogTags(input, tag) {
  	return input
  		.replace(new RegExp('\\\\?\\((' + tag + '(\\s+(".*?"|.+?))?)\\\\?\\)', 'g'), function (match, group) {
  			if (match.substr(0, 1) === '\\') {
  				return '(' + group + ')'; // Rewrite \(tag "..."|...\) to (tag "..."|...)
  			}
  			return '{' + group + '}'; // Rewrite (tag "..."|...) to {tag "..."|...}
  		});
  }
  
  
  function addDialogFunction(tag, fn) {
  	var kitsy = kitsyInit();
  	kitsy.dialogFunctions = kitsy.dialogFunctions || {};
  	if (kitsy.dialogFunctions[tag]) {
  		console.warn('The dialog function "' + tag + '" already exists.');
  		return;
  	}
  
  	// Hook into game load and rewrite custom functions in game data to Bitsy format.
  	before('parseWorld', function (game_data) {
  		return [convertDialogTags(game_data, tag)];
  	});
  
  	kitsy.dialogFunctions[tag] = fn;
  }
  
  function injectDialogTag(tag, code) {
  	inject$1(
  		/(var functionMap = new Map\(\);[^]*?)(this.HasFunction)/m,
  		'$1\nfunctionMap.set("' + tag + '", ' + code + ');\n$2'
  	);
  }
  
  /**
   * Adds a custom dialog tag which executes the provided function.
   * For ease-of-use with the bitsy editor, tags can be written as
   * (tagname "parameters") in addition to the standard {tagname "parameters"}
   * 
   * Function is executed immediately when the tag is reached.
   *
   * @param {string}   tag Name of tag
   * @param {Function} fn  Function to execute, with signature `function(environment, parameters, onReturn){}`
   *                       environment: provides access to SetVariable/GetVariable (among other things, see Environment in the bitsy source for more info)
   *                       parameters: array containing parameters as string in first element (i.e. `parameters[0]`)
   *                       onReturn: function to call with return value (just call `onReturn(null);` at the end of your function if your tag doesn't interact with the logic system)
   */
  function addDialogTag(tag, fn) {
  	addDialogFunction(tag, fn);
  	injectDialogTag(tag, 'kitsy.dialogFunctions["' + tag + '"]');
  }
  
  /**
   * Adds a custom dialog tag which executes the provided function.
   * For ease-of-use with the bitsy editor, tags can be written as
   * (tagname "parameters") in addition to the standard {tagname "parameters"}
   * 
   * Function is executed after the dialog box.
   *
   * @param {string}   tag Name of tag
   * @param {Function} fn  Function to execute, with signature `function(environment, parameters){}`
   *                       environment: provides access to SetVariable/GetVariable (among other things, see Environment in the bitsy source for more info)
   *                       parameters: array containing parameters as string in first element (i.e. `parameters[0]`)
   */
  function addDeferredDialogTag(tag, fn) {
  	addDialogFunction(tag, fn);
  	bitsy.kitsy.deferredDialogFunctions = bitsy.kitsy.deferredDialogFunctions || {};
  	var deferred = bitsy.kitsy.deferredDialogFunctions[tag] = [];
  	injectDialogTag(tag, 'function(e, p, o){ kitsy.deferredDialogFunctions["' + tag + '"].push({e:e,p:p}); o(null); }');
  	// Hook into the dialog finish event and execute the actual function
  	after('onExitDialog', function () {
  		while (deferred.length) {
  			var args = deferred.shift();
  			bitsy.kitsy.dialogFunctions[tag](args.e, args.p, args.o);
  		}
  	});
  	// Hook into the game reset and make sure data gets cleared
  	after('clearGameData', function () {
  		deferred.length = 0;
  	});
  }
  
  /**
   * Adds two custom dialog tags which execute the provided function,
   * one with the provided tagname executed after the dialog box,
   * and one suffixed with 'Now' executed immediately when the tag is reached.
   *
   * i.e. helper for the (exit)/(exitNow) pattern.
   *
   * @param {string}   tag Name of tag
   * @param {Function} fn  Function to execute, with signature `function(environment, parameters){}`
   *                       environment: provides access to SetVariable/GetVariable (among other things, see Environment in the bitsy source for more info)
   *                       parameters: array containing parameters as string in first element (i.e. `parameters[0]`)
   */
  function addDualDialogTag(tag, fn) {
  	addDialogTag(tag + 'Now', function (environment, parameters, onReturn) {
  		fn(environment, parameters);
  		onReturn(null);
  	});
  	addDeferredDialogTag(tag, fn);
  }
  
  
  
  // jump function
  function jump(targetDialog) {
  	if (!targetDialog) {
  		console.warn('Tried to jump to dialog, but no target dialog provided');
  		return;
  	}
  	var dialogStr = bitsy.dialog[targetDialog];
  	var dialogId;
  	if (!dialogStr) {
  		dialogStr = targetDialog;
  	} else {
  		dialogId = targetDialog;
  		dialogStr = dialogStr.src;
  	}
  	bitsy.startDialog(dialogStr, dialogId);
  }
  
  addDualDialogTag('jump', function (environment, parameters) {
  	jump(parameters[0]);
  });
  
  }(window));
  