text: /**
  📝
  @file edit dialog from dialog
  @summary edit dialog from dialog (yes really)
  @license MIT
  @author Sean S. LeBlanc
  @version 20.0.0
  @requires Bitsy 7.12
  
  
  @description
  You can use this to edit the dialog of sprites/items through dialog.
  
  (dialog "map, target, newDialog")
  Parameters:
  	map:       Type of image (SPR or ITM)
  	target:    id/name of image to edit
  	newDialog: new dialog text
  
  Examples:
  (dialog "SPR, a, I am not a cat")
  
  HOW TO USE:
  	Copy-paste this script into a new script tag after the Bitsy source code.
  
  TIPS:
  	- The player avatar is always a sprite with id "A"; you can edit your gamedata to give them a name for clarity
  	- You can use the full names or shorthand of image types (e.g. "SPR" and "sprite" will both work)
  */
  (function (bitsy) {
  'use strict';
  
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
  
  bitsy = bitsy || /*#__PURE__*/_interopDefaultLegacy(bitsy);
  
  /**
   * 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.
   * ```js
   * inject(/(some string)/,'injected before $1 injected after');
   * ```
   * @param searcher Regex to search and replace
   * @param replacer Replacer string/fn
   */
  function inject$1(searcher, replacer) {
      // 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];
          if (!scriptTag.textContent)
              continue;
          var matchesSearch = scriptTag.textContent.search(searcher) !== -1;
          var isCurrentScript = scriptTag === document.currentScript;
          if (matchesSearch && !isCurrentScript) {
              code = scriptTag.textContent;
              break;
          }
      }
      // error-handling
      if (!code || !scriptTag) {
          throw new Error('Couldn\'t find "' + searcher + '" in script tags');
      }
      // modify the content
      code = code.replace(searcher, replacer);
      // 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;
      });
  }
  // Ex: inject(/(names.sprite.set\( name, id \);)/, '$1console.dir(names)');
  /** test */
  function kitsyInject(searcher, replacer) {
      if (!kitsy.queuedInjectScripts.some(function (script) {
          return searcher.toString() === script.searcher.toString() && replacer === script.replacer;
      })) {
          kitsy.queuedInjectScripts.push({
              searcher: searcher,
              replacer: replacer,
          });
      }
      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$1(targetFuncName, beforeFn) {
      kitsy.queuedBeforeScripts[targetFuncName] = kitsy.queuedBeforeScripts[targetFuncName] || [];
      kitsy.queuedBeforeScripts[targetFuncName].push(beforeFn);
  }
  // Ex: after('load_game', function run() { alert('Loaded!'); });
  function after$1(targetFuncName, afterFn) {
      kitsy.queuedAfterScripts[targetFuncName] = kitsy.queuedAfterScripts[targetFuncName] || [];
      kitsy.queuedAfterScripts[targetFuncName].push(afterFn);
  }
  function applyInjects() {
      kitsy.queuedInjectScripts.forEach(function (injectScript) {
          inject$1(injectScript.searcher, injectScript.replacer);
      });
  }
  function applyHooks(root) {
      var allHooks = unique(Object.keys(kitsy.queuedBeforeScripts).concat(Object.keys(kitsy.queuedAfterScripts)));
      allHooks.forEach(applyHook.bind(this, root || window));
  }
  function applyHook(root, functionName) {
      var functionNameSegments = functionName.split('.');
      var obj = root;
      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(kitsy.queuedBeforeScripts[functionName] || []);
      // then original
      if (superFn) {
          functions.push(superFn);
      }
      // then afters
      functions = functions.concat(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)));
              }
              // run synchronously
              returnVal = functions[i++].apply(this, args);
              if (returnVal && returnVal.length) {
                  args = returnVal;
              }
              return runBefore.apply(this, args);
          }
          return runBefore.apply(this, arguments);
      };
  }
  /**
  @file kitsy-script-toolkit
  @summary Monkey-patching toolkit to make it easier and cleaner to run code before and after functions or to inject new code into script tags
  @license WTFPL (do WTF you want)
  @author Original by mildmojo; modified by Sean S. LeBlanc
  @version 20.0.0
  @requires Bitsy 7.12
  
  */
  var kitsy = (window.kitsy = window.kitsy || {
      queuedInjectScripts: [],
      queuedBeforeScripts: {},
      queuedAfterScripts: {},
      inject: kitsyInject,
      before: before$1,
      after: after$1,
      /**
       * Applies all queued `inject` calls.
       *
       * An object that instantiates an class modified via injection will still refer to the original class,
       * so make sure to reinitialize globals that refer to injected scripts before calling `applyHooks`.
       */
      applyInjects,
      /** Apples all queued `before`/`after` calls. */
      applyHooks,
  });
  
  var hooked = kitsy.hooked;
  if (!hooked) {
  	kitsy.hooked = true;
  	var oldStartFunc = bitsy.startExportedGame;
  	bitsy.startExportedGame = function doAllInjections() {
  		// Only do this once.
  		bitsy.startExportedGame = oldStartFunc;
  
  		// Rewrite scripts
  		kitsy.applyInjects();
  
  		// 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();
  		bitsy.renderer = new bitsy.TileRenderer(bitsy.tilesize);
  
  		// Hook everything
  		kitsy.applyHooks();
  
  		// reset callbacks using hacked functions
  		bitsy.bitsyOnUpdate(bitsy.update);
  		bitsy.bitsyOnQuit(bitsy.stopGame);
  		bitsy.bitsyOnLoad(bitsy.load_game);
  
  		// Start the game
  		bitsy.startExportedGame.apply(this, arguments);
  	};
  }
  
  /** @see kitsy.inject */
  var inject = kitsy.inject;
  /** @see kitsy.before */
  var before = kitsy.before;
  /** @see kitsy.after */
  var after = kitsy.after;
  
  // 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) {
  	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 (gameData) {
  		return [convertDialogTags(gameData, tag)];
  	});
  
  	kitsy.dialogFunctions[tag] = fn;
  }
  
  function injectDialogTag(tag, code) {
  	inject(/(var functionMap = \{\};[^]*?)(this.HasFunction)/m, '$1\nfunctionMap["' + 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 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;
  	});
  }
  
  /**
  @file utils
  @summary miscellaneous bitsy utilities
  @author Sean S. LeBlanc
  @version 20.0.0
  @requires Bitsy 7.12
  
  */
  
  /*
  Helper for getting image by name or id
  
  Args:
  	name: id or name of image to return
  	 map: map of images (e.g. `sprite`, `tile`, `item`)
  
  Returns: the image in the given map with the given name/id
   */
  function getImage(name, map) {
  	var id = Object.prototype.hasOwnProperty.call(map, name)
  		? name
  		: Object.keys(map).find(function (e) {
  				return map[e].name === name;
  		  });
  	return map[id];
  }
  
  
  
  // map of maps
  var maps;
  after('load_game', function () {
  	maps = {
  		spr: bitsy.sprite,
  		sprite: bitsy.sprite,
  		itm: bitsy.item,
  		item: bitsy.item,
  	};
  });
  
  function editDialog(environment, parameters) {
  	// parse parameters
  	var params = parameters[0].split(/,\s?/);
  	params[0] = (params[0] || '').toLowerCase();
  	var mapId = params[0];
  	var tgtId = params[1];
  	var newDialog = params[2] || '';
  
  	if (!mapId || !tgtId) {
  		throw new Error('Image expects three parameters: "map, target, newDialog", but received: "' + params.join(', ') + '"');
  	}
  
  	// get objects
  	var mapObj = maps[mapId];
  	if (!mapObj) {
  		throw new Error('Invalid map "' + mapId + '". Try "SPR" or "ITM" instead.');
  	}
  	var tgtObj = getImage(tgtId, mapObj);
  	if (!tgtObj) {
  		throw new Error('Target "' + tgtId + '" was not the id/name of a ' + mapId + '.');
  	}
  	bitsy.dialog[tgtObj.dlg].src = newDialog;
  	bitsy.scriptInterpreter.Compile(tgtObj.dlg, newDialog);
  }
  
  // hook up the dialog tag
  addDeferredDialogTag('dialog', editDialog);
  
  })(window);
  