@bitsy/hecks
Version:
a collection of re-usable scripts for bitsy game maker
856 lines (736 loc) ⢠26.6 kB
JavaScript
/**
š
@file online
@summary multiplayer bitsy
@license MIT
@version 2.1.4
@requires 5.5
@author Sean S. LeBlanc
@description
Provides the groundwork for running a small online multiplayer bitsy game.
Running it requires running a copy of this server: https://github.com/seleb/web-rtc-mesh
Server notes:
- The actual game data is sent using peer-to-peer data channels;
the server just hosts client code and negotaties initial connections.
(i.e. it uses very little bandwidth)
- A single server can host multiple games simultaneously
- If you're not sure how to setup/use the server, ask for help!
This hack also includes the hacks for editing images/dialog at runtime through dialog.
This provides the (image), (imageNow), (imagePal), (imagePalNow), and (dialog) commands.
In the online hack, these will automatically trigger a sprite update so that updates to the avatar will be reflected for other players.
See the respective hacks for more info on how to use the commands.
Note on dialog: You can use scripting in the dialog, but it will execute on the other players' games, accessing *their* variables.
HOW TO USE:
1. Copy-paste this script into a script tag after the bitsy source
2. Edit `hackOptions.host` below to point to your server (depending on hosting, you may need to use `ws://` instead of `wss://`)
3. Edit other hackOptions as needed
*/
this.hacks = this.hacks || {};
this.hacks.online = (function (exports,bitsy) {
;
var hackOptions = {
host: "wss://your signalling server",
// room: "custom room", // sets the room on the server to use; otherwise, uses game title
immediateMode: true, // if true, teleports players to their reported positions; otherwise, queues movements and lets bitsy handle the walking (note: other players pick up items like this)
ghosts: false, // if true, sprites from players who disconnected while you were online won't go away until you restart
debug: false, // if true, includes web-rtc-mesh debug logs in console
};
bitsy = bitsy && bitsy.hasOwnProperty('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 '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 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 = map.hasOwnProperty(name) ? name : Object.keys(map).find(function (e) {
return map[e].name == name;
});
return map[id];
}
/**
* 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)
@version 4.0.0
@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();
kitsy.queuedInjectScripts.push({
searchRegex: searchRegex,
replaceString: replaceString
});
}
// 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;
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]) {
throw new Error('The dialog function "' + tag + '" already exists.');
}
// 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;
}
/**
* 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);
inject$1(
/(var functionMap = new Map\(\);)/,
'$1functionMap.set("' + 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] = [];
inject$1(
/(var functionMap = new Map\(\);)/,
'$1functionMap.set("' + 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);
}
/**
ā
@file javascript dialog
@summary execute arbitrary javascript from dialog
@license MIT
@version 3.2.2
@requires Bitsy Version: 4.5, 4.6
@author Sean S. LeBlanc
@description
Lets you execute arbitrary JavaScript from dialog (including inside conditionals).
If you're familiar with the Bitsy source, this will let you write one-shot hacks
for a wide variety of situations.
Usage:
(js "<JavaScript code to evaluate after dialog is closed>")
(jsNow "<JavaScript code to evaluate immediately>")
Examples:
move a sprite:
(js "sprite['a'].x = 10;")
edit palette colour:
(js "getPal(curPal())[0] = [255,0,0];renderImages();")
place an item next to player:
(js "room[curRoom].items.push({id:'0',x:player().x+1,y:player().y});")
verbose facimile of exit-from-dialog:
(js "var _onExitDialog=onExitDialog;onExitDialog=function(){player().room=curRoom='3';_onExitDialog.apply(this,arguments);onExitDialog=_onExitDialog;};")
HOW TO USE:
1. Copy-paste into a script tag after the bitsy source
2. Add (js "<code>") to your dialog as needed
NOTE: This uses parentheses "()" instead of curly braces "{}" around function
calls because the Bitsy editor's fancy dialog window strips unrecognized
curly-brace functions from dialog text. To keep from losing data, write
these function calls with parentheses like the examples above.
For full editor integration, you'd *probably* also need to paste this
code at the end of the editor's `bitsy.js` file. Untested.
*/
var indirectEval$1 = eval;
function executeJs(environment, parameters) {
indirectEval$1(parameters[0]);
}
addDualDialogTag('js', executeJs);
/**
@file edit image at runtime
@summary API for updating image data at runtime.
@author Sean S. LeBlanc
@description
Adds API for updating sprite, tile, and item data at runtime.
Individual frames of image data in bitsy are 8x8 1-bit 2D arrays in yx order
e.g. the default player is:
[
[0,0,0,1,1,0,0,0],
[0,0,0,1,1,0,0,0],
[0,0,0,1,1,0,0,0],
[0,0,1,1,1,1,0,0],
[0,1,1,1,1,1,1,0],
[1,0,1,1,1,1,0,1],
[0,0,1,0,0,1,0,0],
[0,0,1,0,0,1,0,0]
]
*/
/*
Args:
id: string id or name
frame: animation frame (0 or 1)
map: map of images (e.g. `sprite`, `tile`, `item`)
Returns: a single frame of a image data
*/
function getImageData(id, frame, map) {
return bitsy.renderer.GetImageSource(getImage(id, map).drw)[frame];
}
/*
Updates a single frame of image data
Args:
id: string id or name
frame: animation frame (0 or 1)
map: map of images (e.g. `sprite`, `tile`, `item`)
newData: new data to write to the image data
*/
function setImageData(id, frame, map, newData) {
var drawing = getImage(id, map);
var drw = drawing.drw;
var img = bitsy.renderer.GetImageSource(drw).slice();
img[frame] = newData;
bitsy.renderer.SetImageSource(drw, img);
}
function setSpriteData(id, frame, newData) {
setImageData(id, frame, bitsy.sprite, newData);
}
/**
š
@file edit image from dialog
@summary edit sprites, items, and tiles from dialog
@license MIT
@version 1.2.3
@requires 5.3
@author Sean S. LeBlanc
@description
You can use this to edit the image data of sprites (including the player avatar), items, and tiles through dialog.
Image data can be replaced with data from another image, and the palette index can be set.
(image "map, target, source")
Parameters:
map: Type of image (SPR, TIL, or ITM)
target: id/name of image to edit
source: id/name of image to copy
(imageNow "map, target, source")
Same as (image), but applied immediately instead of after dialog is closed.
(imagePal "map, target, palette")
Parameters:
map: Type of image (SPR, TIL, or ITM)
target: id/name of image to edit
source: palette index (0 is bg, 1 is tiles, 2 is sprites/items, anything higher requires editing your game data to include more)
(imagePalNow "map, target, palette")
Same as (imagePal), but applied immediately instead of after dialog is closed.
Examples:
(image "SPR, A, a")
(imageNow "TIL, a, floor")
(image "ITM, a, b")
(imagePal "SPR, A, 1")
(imagePalNow "TIL, floor, 2")
HOW TO USE:
1. Copy-paste this script into a new script tag after the Bitsy source code.
It should appear *before* any other mods that handle loading your game
data so it executes *after* them (last-in first-out).
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)
- The "source" images don't have to be placed anywhere; so long as they exist in the gamedata they'll work
- This is a destructive operation! Unless you have a copy of an overwritten image, you won't be able to get it back during that run
NOTE: This uses parentheses "()" instead of curly braces "{}" around function
calls because the Bitsy editor's fancy dialog window strips unrecognized
curly-brace functions from dialog text. To keep from losing data, write
these function calls with parentheses like the examples above.
For full editor integration, you'd *probably* also need to paste this
code at the end of the editor's `bitsy.js` file. Untested.
*/
// map of maps
var maps;
after('load_game', function () {
maps = {
spr: bitsy.sprite,
sprite: bitsy.sprite,
til: bitsy.tile,
tile: bitsy.tile,
itm: bitsy.item,
item: bitsy.item,
};
});
function editImage(environment, parameters) {
var i;
// parse parameters
var params = parameters[0].split(/,\s?/);
params[0] = (params[0] || "").toLowerCase();
var mapId = params[0];
var tgtId = params[1];
var srcId = params[2];
if (!mapId || !tgtId || !srcId) {
throw new Error('Image expects three parameters: "map, target, source", but received: "' + params.join(', ') + '"');
}
// get objects
var mapObj = maps[mapId];
if (!mapObj) {
throw new Error('Invalid map "' + mapId + '". Try "SPR", "TIL", or "ITM" instead.');
}
var tgtObj = getImage(tgtId, mapObj);
if (!tgtObj) {
throw new Error('Target "' + tgtId + '" was not the id/name of a ' + mapId + '.');
}
var srcObj = getImage(srcId, mapObj);
if (!srcObj) {
throw new Error('Source "' + srcId + '" was not the id/name of a ' + mapId + '.');
}
// copy animation from target to source
tgtObj.animation = {
frameCount: srcObj.animation.frameCount,
isAnimated: srcObj.animation.isAnimated,
frameIndex: srcObj.animation.frameIndex
};
for (i = 0; i < srcObj.animation.frameCount; ++i) {
setImageData(tgtId, i, mapObj, getImageData(srcId, i, mapObj));
}
}
function editPalette(environment, parameters) {
// parse parameters
var params = parameters[0].split(/,\s?/);
params[0] = (params[0] || "").toLowerCase();
var mapId = params[0];
var tgtId = params[1];
var palId = params[2];
if (!mapId || !tgtId || !palId) {
throw new Error('Image expects three parameters: "map, target, palette", but received: "' + params.join(', ') + '"');
}
// get objects
var mapObj = maps[mapId];
if (!mapObj) {
throw new Error('Invalid map "' + mapId + '". Try "SPR", "TIL", or "ITM" instead.');
}
var tgtObj = getImage(tgtId, mapObj);
if (!tgtObj) {
throw new Error('Target "' + tgtId + '" was not the id/name of a ' + mapId + '.');
}
var palObj = parseInt(palId);
if (isNaN(palObj)) {
throw new Error('Palette "' + palId + '" was not a number.');
}
// set palette
tgtObj.col = palObj;
// update images in cache
bitsy.renderImageForAllPalettes(tgtObj);
}
// hook up the dialog tags
addDualDialogTag('image', editImage);
addDualDialogTag('imagePal', editPalette);
/**
š
@file edit dialog from dialog
@summary edit dialog from dialog (yes really)
@license MIT
@version 1.1.1
@author Sean S. LeBlanc
@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: id/name of image to edit
Note: this hack disables bitsy's script caching.
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)
*/
// map of maps
var maps$1;
after('load_game', function () {
maps$1 = {
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$1[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] = newDialog;
}
// hook up the dialog tag
addDeferredDialogTag('dialog', editDialog);
// disable bitsy's dialog caching
inject(/startDialog\(dialogStr,dialogId\);/g, 'startDialog(dialogStr);');
// download the client script
// bitsy starts onload, so adding it to the head
// is enough to delay game startup until it's loaded/errored
var clientScript = document.createElement("script");
clientScript.src = hackOptions.host.replace(/^ws/, "http") + "/client.js";
clientScript.onload = function () {
console.log("online available!");
};
clientScript.onerror = function (error) {
console.error("online not available!", error);
};
document.head.appendChild(clientScript);
var client;
function onData(event) {
var spr;
var data = event.data;
switch (data.e) {
case "move":
spr = bitsy.sprite[event.from];
if (spr) {
// move sprite
if (hackOptions.immediateMode) {
// do it now
spr.x = data.x;
spr.y = data.y;
spr.room = data.room;
} else {
// let bitsy handle it later
spr.walkingPath.push({
x: data.x,
y: data.y
});
}
} else {
// got a move from an unknown player,
// so ask them who they are
client.send(event.from, {
e: "gimmeSprite"
});
}
break;
case "gimmeSprite":
// send a sprite update to specific peer
client.send(event.from, getSpriteUpdate());
break;
case "sprite":
// update a sprite
var longname = "SPR_" + event.from;
spr = bitsy.sprite[event.from] = {
animation: {
frameCount: data.data.length,
frameIndex: 0,
isAnimated: data.data.length > 1
},
col: data.col,
dlg: longname,
drw: longname,
inventory: {},
name: event.from,
walkingPath: [],
x: data.x,
y: data.y,
room: data.room
};
bitsy.dialog[longname] = data.dlg;
bitsy.renderer.SetImageSource(longname, data.data);
for (var frame = 0; frame < data.data.length; ++frame) {
setSpriteData(event.from, frame, data.data[frame]);
}
break;
}
}
function onClose(event) {
if (event.error) {
console.error('Connection closed due to error:', event.error);
}
if (!hackOptions.ghosts) {
delete bitsy.sprite[event.id];
}
}
after("startExportedGame", function () {
if (!window.Client) {
console.error("Couldn't retrieve client; running game offline");
}
client = new window.Client.default({
host: hackOptions.host,
room: hackOptions.room || bitsy.title,
});
client.on(window.Client.DATA, onData);
client.on(window.Client.CLOSE, onClose);
client.setDebug(hackOptions.debug);
});
after("movePlayer", moveSprite);
after("onready", function () {
// tell everyone who you are
// and ask who they are 1s after starting
setTimeout(function () {
if (client) {
updateSprite();
client.broadcast({
e: "gimmeSprite"
});
}
}, 1000);
});
// tell everyone where you are
function moveSprite() {
var p = bitsy.player();
client.broadcast({
e: "move",
x: p.x,
y: p.y,
room: p.room
});
}
// tell everyone who you are
function updateSprite() {
client.broadcast(getSpriteUpdate());
}
// helper to create a sprite update based on the player avatar
function getSpriteUpdate() {
var p = bitsy.player();
return {
e: "sprite",
data: bitsy.renderer.GetImageSource(p.drw),
x: p.x,
y: p.y,
room: p.room,
dlg: bitsy.dialog[p.dlg],
col: p.col
};
}
// trigger sprite updates after these dialog functions
[
'image',
'imageNow',
'imagePal',
'imagePalNow',
'dialog'
].forEach(function (tag) {
var original = bitsy.kitsy.dialogFunctions[tag];
bitsy.kitsy.dialogFunctions[tag] = function () {
original.apply(this, arguments);
updateSprite();
};
});
exports.hackOptions = hackOptions;
return exports;
}({},window));