Skip to content

Instantly share code, notes, and snippets.

@casaval
Forked from jacopocolo/guide.md
Created December 17, 2020 18:10
Show Gist options
  • Save casaval/901b76020de1079ad30c185c3c06693a to your computer and use it in GitHub Desktop.
Save casaval/901b76020de1079ad30c185c3c06693a to your computer and use it in GitHub Desktop.
Making a Bitsy game compatible with the Nintendo 3ds browser

The Nintendo 3ds browser is capable of running Bitsy games! And it's a delightful Bitsy machine.

To make games compatible, we just need to teach the 3ds browser to speak the language Bitsy is written in: modern Javascript!

To do that we need two things:

  1. The html page that contains your game
  2. A <script> </script> html element we'll add at the top of the page that translates modern Javascript to somewhat Javascript.

The whole script element is here. Just select the whole thing and copy.

Then open your Bitsy html with either Notepad or a code editor like Atom and paste the code above the line that says <script type="text/bitsyGameData" id="exportedGameData">.

That's it! Save and publish your game as before. The Nintendo 3ds browser should be able to play it, even from itch.io.

(Credit where credit is due: these are all polyfill functions I found already made online. The assign and includes polyfill are from developer.mozilla.org, the map polyfill is by Eric Wendelin.)

@casaval
Copy link
Author

casaval commented Dec 17, 2020

<script type="text/javascript"> //assign polyfill if (!Object.assign) { Object.defineProperty(Object, 'assign', { enumerable: false, configurable: true, writable: true, value: function(target, firstSource) { 'use strict'; if (target === undefined || target === null) { throw new TypeError('Cannot convert first argument to object'); } var to = Object(target); for (var i = 1; i < arguments.length; i++) { var nextSource = arguments[i]; if (nextSource === undefined || nextSource === null) { continue; } nextSource = Object(nextSource); var keysArray = Object.keys(Object(nextSource)); for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { var nextKey = keysArray[nextIndex]; var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); if (desc !== undefined && desc.enumerable) { to[nextKey] = nextSource[nextKey]; } } } return to; } }); } //includes polyfill if (!Array.prototype.includes) { Array.prototype.includes = function(searchElement /*, fromIndex*/) { 'use strict'; if (this == null) { throw new TypeError('Array.prototype.includes called on null or undefined'); } var O = Object(this); var len = parseInt(O.length, 10) || 0; if (len === 0) { return false; } var n = parseInt(arguments[1], 10) || 0; var k; if (n >= 0) { k = n; } else { k = len + n; if (k < 0) {k = 0;} } var currentElement; while (k < len) { currentElement = O[k]; if (searchElement === currentElement || (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN return true; } k++; } return false; }; } //map polyfill /** * Copyright 2012 Eric Wendelin - MIT License * * es6-map-shim.js is a DESTRUCTIVE shim that follows the latest Map specification * as closely as possible. It is destructive in the sense that it overrides native implementations. * * IMPORTANT: Currently, get(), set(), has() and delete() are all O(n) operations. * Normally, they would be O(1). Therefore it is NOT recommended to use this with * a large dataset or in any production environment. * * This library assumes ES5 functionality: Object.create, Object.defineProperty, * Array.indexOf, Function.bind and others. */ (function(module) { function Map(iterable) { var _items = []; var _keys = []; var _values = []; // Object.is polyfill, courtesy of @WebReflection var is = Object.is || function(a, b) { return a === b ? a !== 0 || 1 / a == 1 / b : a != a && b != b; }; // More reliable indexOf, courtesy of @WebReflection var betterIndexOf = function(value) { if(value != value || value === 0) { for(var i = this.length; i-- && !is(this[i], value);); } else { i = [].indexOf.call(this, value); } return i; }; /** * MapIterator used for iterating over all entries in given map. * * @param map {Map} to iterate * @param kind {String} identifying what to yield. Possible values * are 'keys', 'values' and 'keys+values' * @constructor */ var MapIterator = function MapIterator(map, kind) { var _index = 0; return Object.create({}, { next: { value: function() { // check if index is within bounds if (_index < map.items().length) { switch(kind) { case 'keys': return map.keys()[_index++]; case 'values': return map.values()[_index++]; case 'keys+values': return [].slice.call(map.items()[_index++]); default: throw new TypeError('Invalid iterator type'); } } // TODO: make sure I'm interpreting the spec correctly here throw new Error('Stop Iteration'); } }, iterator: { value: function() { return this; } }, toString: { value: function() { return '[object Map Iterator]'; } } }); }; var _set = function(key, value) { // check if key exists and overwrite var index = betterIndexOf.call(_keys, key); if (index > -1) { _items[index][1] = value; _values[index] = value; } else { _items.push([key, value]); _keys.push(key); _values.push(value); } }; var setItem = function(item) { if (item.length !== 2) { throw new TypeError('Invalid iterable passed to Map constructor'); } _set(item[0], item[1]); }; // FIXME: accommodate any class that defines an @@iterator method that returns // an iterator object that produces two element array-like objects if (Array.isArray(iterable)) { iterable.forEach(setItem); } else if (iterable !== undefined) { throw new TypeError('Invalid Map'); } return Object.create(MapPrototype, { /** * @return {Array} all entries in the Map, in order */ items:{ value:function() { return [].slice.call(_items); } }, /** * @return {Array} all keys in the Map, in order */ keys:{ value:function() { return [].slice.call(_keys); } }, /** * @return {Array} all values in the Map, in order */ values:{ value:function() { return [].slice.call(_values); } }, /** * Given a key, indicate whether that key exists in this Map. * * @param key {Object} expected key * @return {Boolean} true if key in Map */ has:{ value:function(key) { // TODO: double-check how spec reads about null values var index = betterIndexOf.call(_keys, key); return index > -1; } }, /** * Given a key, retrieve the value associated with that key (or undefined). * * @param key {Object} * @return {Object} value associated with key or undefined */ get:{ value:function(key) { var index = betterIndexOf.call(_keys, key); return index > -1 ? _values[index] : undefined; } }, /** * Add or overwrite entry associating key with value. Always returns undefined. * * @param key {Object} anything * @param value {Object} also anything */ set:{ value: _set }, /** * Return the number of entries in this Map. * * @return {Number} number of entries */ size:{ get:function() { return _items.length; } }, /** * Remove all entries in this Map. Returns undefined. */ clear:{ value:function() { _keys.length = _values.length = _items.length = 0; } }, /** * Delete entry with given key, if it exists. * * @param key {Object} any possible key * @return {Boolean} true if an entry was deleted */ 'delete':{ value:function(key) { var index = betterIndexOf.call(_keys, key); if (index > -1) { _keys.splice(index, 1); _values.splice(index, 1); _items.splice(index, 1); return true; } return false; } }, /** * Given a callback function and optional context, invoke the callback on all * entries in this Map. * * @param callbackFn {Function} */ forEach:{ value:function(callbackfn /*, thisArg*/) { if (typeof callbackfn != 'function') { throw new TypeError('Invalid callback function given to forEach'); } function tryNext() { try { return iter.next(); } catch(e) { return undefined; } } var iter = this.iterator(); var current = tryNext(); var next = tryNext(); while(current !== undefined) { callbackfn.apply(arguments[1], [current[1], current[0], this]); current = next; next = tryNext(); } } }, /** * Return a MapIterator object for this map. */ iterator:{ value: function() { return new MapIterator(this, 'keys+values'); } }, toString:{ value: function() { return '[Object Map]'; } } }); } var notInNode = module == 'undefined'; var window = notInNode ? this : global; var module = notInNode ? {} : exports; var MapPrototype = Map.prototype; Map.prototype = MapPrototype = Map(); window.Map = module.Map = window.Map || Map; }.call(this, typeof exports)); </script>

@WebReflection
Copy link

gosh there's some very old polyfill of mine going on in here 😂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment