// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  THE JELLI GAME ENGINE
  Programmed by Go Shoemake <3
  -----------------------------
  Implemented through several modules in Literate CoffeeScript.
  Source code and resources are available at https://github.com/marrus-sh/jelli/.
  Written for the CoffeeScript 1.10.0 compiler.
   */

  /*
  MIT License
  
  Copyright (c) 2016 Margaret "Go" Shoemake
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
   */

  /*
  How To Use:
  
  The `Game` constructor creates a new game, which is automatically assigned to `document.game`.
  You can choose a different document for game rendering by passing it in as an attribute.
  A document can have only one game assigned to it at a time, and this game is non-configurable.
  
  A sample loading script might look like this:
  
      window.addEventListener("load", function () {new Game(document);}, false);
   */


}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  CONTROL
  Keyboard input tracking; mouse and touch support
  ---------------------
   */
  var Control, Poke, PokeList,
    slice = [].slice;

  Poke = function(elt, e, n, x, y, width, height) {
    var rect;
    if (!(elt instanceof Element)) {
      elt = null;
    }
    if (typeof e !== "object") {
      e = null;
    }
    rect = elt != null ? elt.getBoundingClientRect() : void 0;
    if (isNaN(x = Number(x))) {
      x = 0;
    }
    if (isNaN(y = Number(y))) {
      y = 0;
    }
    if (isNaN(width = Number(width))) {
      width = (elt.width != null ? elt.width : elt.clientWidth);
    }
    if (isNaN(height = Number(height))) {
      height = (elt.height != null ? elt.height : elt.clientHeight);
    }
    Object.defineProperties(this, {
      number: {
        value: n
      },
      origin_height: {
        value: height
      },
      origin_width: {
        value: width
      },
      origin_x: {
        value: x
      },
      origin_y: {
        value: y
      },
      target: {
        value: elt
      }
    });
    Object.defineProperties(this, {
      start_x: {
        value: ((e != null ? e.pageX : void 0) - rect.left - this.origin_x) * this.origin_width / elt.clientWidth
      },
      start_y: {
        value: ((e != null ? e.pageY : void 0) - rect.top - this.origin_y) * this.origin_height / elt.clientHeight
      }
    });
    return Object.defineProperties(this, {
      x: {
        value: this.start_x,
        writable: true
      },
      y: {
        value: this.start_y,
        writable: true
      }
    });
  };

  Poke.prototype = Object.create(Object.prototype, {
    updateWith: {
      value: function(e) {
        var rect;
        if (!(this.target instanceof Element && typeof e === "object")) {
          return;
        }
        rect = this.target.getBoundingClientRect();
        this.x = ((e != null ? e.pageX : void 0) - rect.left - this.origin_x) * this.origin_width / this.target.clientWidth;
        return this.y = ((e != null ? e.pageY : void 0) - rect.top - this.origin_y) * this.origin_height / this.target.clientHeight;
      }
    }
  });

  Object.freeze(Poke.prototype);

  PokeList = function(elt, x, y, width, height) {
    if (!(elt instanceof Element)) {
      elt = document.body;
    }
    if (isNaN(x = Number(x))) {
      x = 0;
    }
    if (isNaN(y = Number(y))) {
      y = 0;
    }
    if (isNaN(width = Number(width))) {
      width = (elt.width != null ? elt.width : elt.clientWidth);
    }
    if (isNaN(height = Number(height))) {
      height = (elt.height != null ? elt.height : elt.clientHeight);
    }
    return Object.defineProperties(this, {
      height: {
        value: height
      },
      target: {
        value: elt
      },
      width: {
        value: width
      },
      x: {
        value: x
      },
      y: {
        value: y
      }
    });
  };

  PokeList.prototype = Object.create(Object.prototype, {
    deleteItem: {
      value: function(n) {
        var id, poke, results;
        results = [];
        for (id in this) {
          poke = this[id];
          if (poke.number === n) {
            results.push(delete this[id]);
          }
        }
        return results;
      }
    },
    item: {
      value: function(n) {
        var id, poke;
        for (id in this) {
          poke = this[id];
          if (poke.number === n) {
            return poke;
          }
        }
        return null;
      }
    },
    "new": {
      value: function(id, e, n) {
        return this[id] = new Poke(this.target, e, n, this.x, this.y, this.width, this.height);
      }
    }
  });

  Object.freeze(PokeList.prototype);

  Control = function(elt, x, y, width, height) {
    var ref;
    if (elt == null) {
      elt = document.body;
    }
    if (!(this instanceof Control)) {
      return;
    }
    if (!(elt instanceof Element)) {
      elt = document.body;
    }
    if (isNaN(x = Number(x))) {
      x = 0;
    }
    if (isNaN(y = Number(y))) {
      y = 0;
    }
    if (isNaN(width = Number(width))) {
      width = (elt.width != null ? elt.width : elt.clientWidth);
    }
    if (isNaN(height = Number(height))) {
      height = (elt.height != null ? elt.height : elt.clientHeight);
    }
    Object.defineProperties(this, {
      "target": {
        value: elt
      },
      "clicks": {
        value: new PokeList(elt, x, y, width, height),
        enumerable: true
      },
      "codes": {
        value: {}
      },
      "height": {
        value: height
      },
      "keys": {
        value: {}
      },
      "ownerDocument": {
        value: ((ref = this.target) != null ? ref.ownerDocument : void 0) || document
      },
      "touches": {
        value: new PokeList(elt, x, y, width, height),
        enumerable: true
      },
      "width": {
        value: width
      },
      "x": {
        value: x
      },
      "y": {
        value: y
      }
    });
    window.addEventListener("keydown", this, false);
    window.addEventListener("keyup", this, false);
    window.addEventListener("contextmenu", this, false);
    window.addEventListener("touchstart", this, false);
    window.addEventListener("touchend", this, false);
    window.addEventListener("touchmove", this, false);
    window.addEventListener("touchcancel", this, false);
    window.addEventListener("mousedown", this, false);
    window.addEventListener("mouseup", this, false);
    window.addEventListener("mousemove", this, false);
  };

  Control.prototype = Object.create(Object.prototype, {
    add: {
      value: function(name) {
        if (name != null) {
          this.keys[name] = false;
        }
        return this;
      }
    },
    addCodes: {
      value: function() {
        var code, codes, k, len, name;
        name = arguments[0], codes = 2 <= arguments.length ? slice.call(arguments, 1) : [];
        if ((name != null) && (this.keys[name] != null)) {
          for (k = 0, len = codes.length; k < len; k++) {
            code = codes[k];
            this.codes[code] = name;
          }
        }
        return this;
      }
    },
    getName: {
      value: function(code) {
        if ((code != null) && (this.codes[code] != null)) {
          return this.codes[code];
        }
      }
    },
    handleEvent: {
      value: function(e) {
        var click, code, i, j, k, l, len, len1, len2, len3, len4, m, n, new_touch, o, p, rect, ref, ref1, ref10, ref11, ref12, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, touch, touch_inside;
        if (!(e instanceof Event)) {
          return;
        }
        switch (e.type) {
          case "contextmenu":
            e.preventDefault();
            break;
          case "keydown":
            ref = [e.code, e.key, e.keyIdentifier, e.keyCode];
            for (k = 0, len = ref.length; k < len; k++) {
              code = ref[k];
              if (this.isCodeDefined(code)) {
                this.toggleCode(code, true);
              }
            }
            break;
          case "keyup":
            ref1 = [e.code, e.key, e.keyIdentifier, e.keyCode];
            for (l = 0, len1 = ref1.length; l < len1; l++) {
              code = ref1[l];
              if (this.isCodeDefined(code)) {
                this.toggleCode(code, false);
              }
            }
            break;
          case "mousedown":
            rect = (ref2 = this.target) != null ? ref2.getBoundingClientRect() : void 0;
            if (rect && (rect.left < (ref3 = e.pageX) && ref3 < rect.right) && (rect.top < (ref4 = e.pageY) && ref4 < rect.bottom)) {
              this.clicks["new"](e.button, e, e.button);
            }
            break;
          case "mousemove":
            ref5 = this.clicks;
            for (i in ref5) {
              click = ref5[i];
              click.updateWith(e);
            }
            break;
          case "mouseup":
            delete this.clicks[e.button];
            break;
          case "touchcancel":
          case "touchend":
            ref6 = e.changedTouches;
            for (m = 0, len2 = ref6.length; m < len2; m++) {
              touch = ref6[m];
              delete this.touches[touch.identifier];
            }
            break;
          case "touchmove":
            ref7 = e.changedTouches;
            for (o = 0, len3 = ref7.length; o < len3; o++) {
              touch = ref7[o];
              this.touches[touch.identifier].updateWith(touch);
            }
            break;
          case "touchstart":
            rect = (ref8 = this.target) != null ? ref8.getBoundingClientRect() : void 0;
            touch_inside = false;
            ref9 = e.changedTouches;
            for (p = 0, len4 = ref9.length; p < len4; p++) {
              new_touch = ref9[p];
              if (!(rect && (rect.left < (ref10 = touch.pageX) && ref10 < rect.right) && (rect.top < (ref11 = touch.pageY) && ref11 < rect.bottom))) {
                continue;
              }
              touch_inside = true;
              n = null;
              j = 0;
              while (n !== j) {
                n = j;
                ref12 = this.touches;
                for (i in ref12) {
                  touch = ref12[i];
                  if (j === touch.number) {
                    j++;
                  }
                }
              }
              this.touches["new"](new_touch.identifier, touch, n);
            }
            if (touch_inside) {
              e.preventDefault();
            }
        }
      }
    },
    isActive: {
      value: function(name) {
        if ((name != null) && (this.keys[name] != null)) {
          return !!this.keys[name];
        }
      }
    },
    isDefined: {
      value: function(name) {
        if (name != null) {
          return this.keys[name] != null;
        }
      }
    },
    isCodeActive: {
      value: function(code) {
        var name;
        if ((code != null) && ((name = this.codes[code]) != null) && (this.keys[name] != null)) {
          return !!this.keys[name];
        }
      }
    },
    isCodeDefined: {
      value: function(code) {
        var name;
        if ((code != null) && ((name = this.codes[code]) != null)) {
          return this.keys[name] != null;
        }
      }
    },
    remove: {
      value: function(name) {
        var code, k, len;
        if (name != null) {
          delete this.keys[name];
        }
        for (k = 0, len = codes.length; k < len; k++) {
          code = codes[k];
          if ((name != null) && this.codes[code] === name) {
            delete this.codes[code];
          }
        }
        return this;
      }
    },
    removeCodes: {
      value: function() {
        var code, codes, k, len;
        codes = 1 <= arguments.length ? slice.call(arguments, 0) : [];
        for (k = 0, len = codes.length; k < len; k++) {
          code = codes[k];
          delete this.codes[code];
        }
        return this;
      }
    },
    toggle: {
      value: function(name, to) {
        if ((name != null) && (this.keys[name] != null)) {
          return this.keys[name] = (to != null) && to || (to == null) && !this.keys[name];
        }
      }
    },
    toggleCode: {
      value: function(code, to) {
        var name;
        if ((code != null) && (name = this.codes[code]) && (this.keys[name] != null)) {
          return this.keys[name] = (to != null) && to || (to == null) && !this.keys[name];
        }
      }
    }
  });

  Object.freeze(Control.prototype);

  Control.Poke = Object.freeze(Poke);

  Control.PokeList = Object.freeze(PokeList);

  this.Control = Object.freeze(Control);

}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  DATA
  The Jelli Game Engine
  ---------------------
   */
  var Data, TypedArray, array_size, byte_depth, getIndexOfDataArray, makeArrayFrom, setIndexOfDataArray,
    slice = [].slice;

  if ("Int8Array" in window) {
    TypedArray = Object.getPrototypeOf(Int8Array);
  }

  array_size = [0, 8, 8, 16, 8, 16, 32, 8, 8, 32, 32];

  byte_depth = [0, 8, 4, 5, 2, 3, 5, 1, 1, 3, 3];

  getIndexOfDataArray = function(a, d, i) {
    return a[Math.floor(i / byte_depth[d])] >> d * (i % byte_depth[d]) & Math.pow(2, d) - 1;
  };

  setIndexOfDataArray = function(a, d, i, n) {
    return a[Math.floor(i / byte_depth[d])] = a[Math.floor(i / byte_depth[d])] & ~(Math.pow(2, d) - 1 << d * (i % byte_depth[d])) | (n & Math.pow(2, d) - 1) << d * (i % byte_depth[d]);
  };

  makeArrayFrom = function(something, map_function, this_arg) {
    var array_data, computed_array, first_point, item, j, k, length, ref, second_point;
    if (something == null) {
      something = [];
    }
    array_data = Object(something);
    if (array_data.length == null) {
      array_data = (typeof array_data.valueOf() === "number" || (array_data.valueOf() instanceof Number) ? [array_data] : String(array_data));
    }
    length = array_data.length < 0 ? 0 : array_data.length >>> 0;
    computed_array = [];
    j = 0;
    k = 0;
    while (k < length) {
      item = typeof map_function === "function" || (map_function instanceof Function) ? map_function.call((this_arg != null ? this_arg : this), array_data[k]) : array_data[k];
      if ((1 <= (ref = item.length) && ref <= 2) && (typeof item === "string" || (item instanceof String))) {
        first_point = item.charCodeAt(0);
        if ((0xD800 <= first_point && first_point <= 0xDBFF)) {
          if (item.length === 2) {
            second_point = item.charCodeAt(1);
            item = (0xDC00 <= second_point && second_point <= 0xDFFF) ? (first_point - 0xD800) * 0x400 + second_point - 0xDC00 + 0x10000 : 0;
          } else {
            second_point = typeof map_function === "function" || (map_function instanceof Function) ? map_function.call((this_arg != null ? this_arg : this), array_data[k + 1]) : array_data[k + 1];
            if (typeof second_point === "string" || (second_point instanceof String)) {
              second_point = second_point.length === 1 ? second_point.charCodeAt(0) : 0;
            } else {
              if (isNaN(second_point = Number(second_point))) {
                second_point = 0;
              }
            }
            if ((0xDC00 <= second_point && second_point <= 0xDFFF)) {
              item = (first_point - 0xD800) * 0x400 + second_point - 0xDC00 + 0x10000;
              k++;
            } else {
              item = first_point;
            }
          }
        } else {
          item = item.length === 1 ? first_point : 0;
        }
      }
      if (isNaN(item = Number(item))) {
        item = 0;
      }
      computed_array[j++] = item;
      k++;
    }
    return computed_array;
  };

  Data = function(unit_size, length_or_data, byte_offset, length_for_buffer) {
    var given_data, index, internal_array, internal_byte_depth, internal_byte_length, internal_byte_size, l, ref, this_array;
    if (!((this_array = this) instanceof Data)) {
      this_array = Object.create(Data.prototype);
    }
    this_array.unitSize = unit_size < 0 ? 0 : unit_size >>> 0;
    if (this_array.unitSize > 32) {
      this_array.unitSize = 32;
    }
    if ((typeof ArrayBuffer !== "undefined" && ArrayBuffer !== null) && length_or_data instanceof ArrayBuffer) {
      this_array.buffer = length_or_data;
      if ((this_array.byteOffset = byte_offset) < 0) {
        this_array.byteOffset = 0;
      }
      if ((this_array.byteOffset >>>= 0) > this_array.buffer.byteLength) {
        this_array.byteOffset = this_array.buffer.byteLength;
      }
      this_array.length = length_for_buffer < 0 ? 0 : length_for_buffer >>> 0;
      internal_byte_depth = this_array.unitSize < 11 ? byte_depth[this_array.unitSize] : 1;
      internal_byte_size = this_array.unitSize < 11 ? array_size[this_array.unitSize] : this_array.unitSize < 17 ? 16 : 32;
      internal_byte_length = Math.ceil(this_array.length / internal_byte_depth) * internal_byte_size / 8;
      if (internal_byte_length > this_array.buffer.byteLength - this_array.byteOffset) {
        internal_byte_length = this_array.buffer.byteLength - this_array.byteOffset;
      }
      internal_array = new ((function() {
        if (TypedArray != null) {
          switch (internal_byte_size) {
            case 0:
              return function() {};
            case 8:
              return Uint8Array;
            case 16:
              return Uint16Array;
            default:
              return Uint32Array;
          }
        } else {
          return Array;
        }
      })())(this_array.buffer, this_array.byteOffset, internal_byte_length);
      if (internal_array.length * internal_byte_depth < this_array.length) {
        this_array.length = internal_array.length * internal_byte_depth;
      }
      this_array.byteLength = internal_array.byteLength;
      given_data = [];
    } else {
      if ((length_or_data != null) && (length_or_data instanceof Array || isNaN(length_or_data))) {
        given_data = makeArrayFrom(length_or_data);
        this_array.length = given_data.length;
      } else {
        given_data = [];
        this_array.length = length_or_data < 0 ? 0 : length_or_data >>> 0;
      }
      internal_byte_depth = this_array.unitSize < 11 ? byte_depth[this_array.unitSize] : 1;
      internal_byte_size = this_array.unitSize < 11 ? array_size[this_array.unitSize] : this_array.unitSize < 17 ? 16 : 32;
      internal_array = new ((function() {
        if (TypedArray != null) {
          switch (internal_byte_size) {
            case 0:
              return function() {};
            case 8:
              return Uint8Array;
            case 16:
              return Uint16Array;
            default:
              return Uint32Array;
          }
        } else {
          return Array;
        }
      })())(byte_depth ? Math.ceil(this_array.length / internal_byte_depth) : 0);
      this_array.buffer = internal_array.buffer;
      this_array.byteOffset = 0;
      this_array.byteLength = internal_array.byteLength;
    }
    if (this_array.length) {
      for (index = l = 0, ref = this_array.length - 1; 0 <= ref ? l <= ref : l >= ref; index = 0 <= ref ? ++l : --l) {
        Object.defineProperty(this_array, index, {
          get: getIndexOfDataArray.bind(this_array, internal_array, this_array.unitSize, index),
          set: setIndexOfDataArray.bind(this_array, internal_array, this_array.unitSize, index),
          enumerable: true
        });
        if (given_data[index] != null) {
          this_array[index] = given_data[index];
        }
      }
    }
    this_array.constructor = Data;
    Object.defineProperties(this_array, {
      buffer: {
        enumerable: false
      },
      constructor: {
        enumerable: false
      },
      byteLength: {
        enumerable: false
      },
      byteOffset: {
        enumerable: false
      },
      length: {
        enumerable: false
      },
      unitSize: {
        enumerable: false
      }
    });
    return Object.freeze(this_array);
  };

  Data.prototype = Object.create(Array.prototype, {
    set: function(given_data, offset) {
      var index;
      if (!(this instanceof Data)) {
        return;
      }
      given_data = makeArrayFrom(given_data);
      offset = offset < 0 ? 0 : offset >>> 0;
      index = 0;
      while (index < given_data.length && index + offset < this.length) {
        this[index + offset] = given_data[index];
        index++;
      }
    },
    subarray: function() {
      throw new TypeError("subarray not supported for Data objects");
    }
  });

  Object.freeze(Data.prototype);

  Data.from = function(unit_size, given_data, map_function, this_arg) {
    return Data.call(this, unit_size, makeArrayFrom(given_data, map_function, this_arg));
  };

  Data.of = function() {
    var unit_size, values;
    unit_size = arguments[0], values = 2 <= arguments.length ? slice.call(arguments, 1) : [];
    return Data.call(this, unit_size, values);
  };

  Object.defineProperties(Data, {
    "name": {
      value: "Data"
    },
    "length": {
      value: 3
    },
    "BYTES_PER_ELEMENT": {
      value: NaN
    }
  });

  this.Data = Object.freeze(Data);

}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  LETTERS
  Sprite-based text processing and rendering
  ------------------------------------------
   */
  var Letter, LetterBlock, LetterString, Letters, drawLetter,
    slice = [].slice;

  drawLetter = function(letters, index, context, x, y) {
    var height, i, j, width;
    if (!(letters instanceof Letters && index < letters.size && context instanceof CanvasRenderingContext2D)) {
      return;
    }
    if (isNaN(index = Number(index))) {
      index = 0;
    }
    if (isNaN(x = Math.round(x))) {
      x = 0;
    }
    if (isNaN(y = Math.round(y))) {
      y = 0;
    }
    i = index % letters.width;
    j = Math.floor(index / letters.width);
    if (letters.canvas instanceof HTMLCanvasElement && !isNaN(i) && !isNaN(j) && (width = Number(letters.letter_width)) && (height = Number(letters.letter_height))) {
      return context.drawImage(letters.canvas, i * width, j * height, width, height, x, y, width, height);
    }
  };

  Letter = function(letters, index) {
    if (!(letters instanceof Letters)) {
      letters = null;
    }
    if (isNaN(index = Number(index))) {
      index = 0;
    }
    return Object.defineProperties(this, {
      canvas: {
        value: (letters ? letters.canvas : null)
      },
      draw: {
        value: drawLetter.bind(this, letters, index)
      },
      height: {
        value: (letters ? letters.letter_height : 0)
      },
      index: {
        value: index
      },
      letters: {
        value: letters
      },
      width: {
        value: (letters ? letters.letter_width : 0)
      }
    });
  };

  Letter.prototype = Object.create(Object.prototype, {
    draw: {
      value: function() {}
    }
  });

  Object.freeze(Letter.prototype);

  LetterString = function(letters, data) {
    var delIndex, drawIndex, index, k, len, letter, q;
    if (!(letters instanceof Letters)) {
      letters = null;
    }
    data = String(data);
    delIndex = 0;
    drawIndex = 0;
    index = 0;
    Object.defineProperties(this, {
      delIndex: {
        get: function() {
          return delIndex;
        },
        set: function(n) {
          if (!isNaN(n = Math.round(n))) {
            return delIndex = n;
          }
        }
      },
      drawIndex: {
        get: function() {
          return drawIndex;
        },
        set: function(n) {
          if (!isNaN(n = Math.round(n))) {
            return drawIndex = n;
          }
        }
      },
      index: {
        get: function() {
          return index;
        },
        set: function(n) {
          if (!isNaN(n = Math.round(n))) {
            return index = n;
          }
        }
      }
    });
    Object.defineProperties(this, {
      data: {
        value: data
      },
      length: {
        value: data.length
      },
      letters: {
        value: letters
      }
    });
    for (q = k = 0, len = data.length; k < len; q = ++k) {
      letter = data[q];
      Object.defineProperty(this, q, {
        value: letters != null ? letters.item(data.charCodeAt(q)) : void 0,
        enumerable: true
      });
    }
    return this;
  };

  LetterString.prototype = Object.create(Object.prototype, {
    advance: {
      value: function(amount) {
        var i;
        if (amount == null) {
          amount = 1;
        }
        i = this.index;
        i += isNaN(amount = Math.round(amount)) ? 1 : amount;
        if (i < 0) {
          i = 0;
        }
        if (i > this.length) {
          i = this.length;
        }
        this.index = i;
        if (this.delIndex > i) {
          this.delIndex = i;
        }
        return i;
      }
    },
    clear: {
      value: function() {
        return this.index = this.delIndex = 0;
      }
    },
    draw: {
      value: function(context, x, y) {
        var height, width;
        if (!(context instanceof CanvasRenderingContext2D && this.letters instanceof Letters && (width = Number(this.letters.letter_width)) && (height = Number(this.letters.letter_height)))) {
          return;
        }
        if (isNaN(x = Math.round(x))) {
          x = 0;
        }
        if (isNaN(y = Math.round(y))) {
          y = 0;
        }
        if (this.drawIndex > this.length) {
          this.drawIndex = this.length;
        }
        if (this.drawIndex < 0) {
          this.drawIndex = 0;
        }
        if (this.delIndex > this.index) {
          this.delIndex = this.index;
        }
        if (this.delIndex < 0) {
          this.delIndex = 0;
        }
        if (this.drawIndex > this.delIndex) {
          context.clearRect(x + this.delIndex * (width + this.letters.letter_spacing), y, (this.drawIndex - this.delIndex) * (width + this.letters.letter_spacing), height + this.letters.letter_spacing);
          this.drawIndex = this.delIndex;
        }
        while (this.drawIndex < this.length && this.drawIndex < this.index) {
          if (this[this.drawIndex] instanceof Letter) {
            this[this.drawIndex].draw(context, x + this.drawIndex * (width + this.letters.letter_spacing), y);
          }
          this.drawIndex++;
        }
        return this.delIndex = this.drawIndex;
      }
    },
    fill: {
      value: function() {
        return this.index = this.length;
      }
    },
    item: {
      value: function(n) {
        if (this[n] instanceof Letter) {
          return this[n];
        } else {
          return null;
        }
      }
    }
  });

  Object.freeze(LetterString.prototype);

  LetterBlock = function() {
    var context, k, len, letters, multiGetter, multiSetter, q, string, strings, x, y;
    letters = arguments[0], context = arguments[1], x = arguments[2], y = arguments[3], strings = 5 <= arguments.length ? slice.call(arguments, 4) : [];
    if (!(context instanceof CanvasRenderingContext2D)) {
      context = null;
    }
    if (!(letters instanceof Letters)) {
      letters = null;
    }
    if (isNaN(x = Math.round(x))) {
      x = 0;
    }
    if (isNaN(y = Math.round(y))) {
      y = 0;
    }
    for (q = k = 0, len = strings.length; k < len; q = ++k) {
      string = strings[q];
      Object.defineProperty(this, q, {
        value: (string instanceof LetterString ? string : new LetterString(letters, string)),
        enumerable: true
      });
    }
    Object.defineProperties(this, {
      context: {
        value: context
      },
      height: {
        value: strings.length
      },
      letters: {
        value: letters
      }
    });
    multiGetter = function(prop) {
      var i, n;
      i = 0;
      n = 0;
      while (i < this.height && this[i][prop] === this[i].length) {
        n += this[i].length;
        i++;
      }
      if (i < this.height) {
        return n + this[i][prop];
      } else {
        return n;
      }
    };
    multiSetter = function(prop, n) {
      var i, results;
      i = 0;
      if (isNaN(n = Number(n))) {
        n = 0;
      }
      while (i < this.height && n > this[i].length) {
        n -= this[i][prop] = this[i].length;
        i++;
      }
      if (i < this.height) {
        this[i][prop] = n;
      }
      results = [];
      while (++i < this.height) {
        results.push(this[i][prop] = 0);
      }
      return results;
    };
    Object.defineProperties(this, {
      delIndex: {
        get: multiGetter.bind(this, "delIndex"),
        set: multiSetter.bind(this, "delIndex")
      },
      drawIndex: {
        get: multiGetter.bind(this, "drawIndex"),
        set: multiSetter.bind(this, "drawIndex")
      },
      index: {
        get: multiGetter.bind(this, "index"),
        set: multiSetter.bind(this, "index")
      }
    });
    Object.defineProperty(this, "length", {
      get: function() {
        var i, n;
        i = 0;
        n = 0;
        while (i < this.height) {
          n += this[i++].length;
        }
        return n;
      }
    });
    return Object.defineProperties(this, {
      x: {
        get: function() {
          return x;
        },
        set: function(n) {
          if (!isNaN(n)) {
            x = Number(n);
            this.delIndex = 0;
            return this.drawIndex = 0;
          }
        }
      },
      y: {
        get: function() {
          return y;
        },
        set: function(n) {
          if (!isNaN(n)) {
            y = Number(n);
            this.delIndex = 0;
            return this.drawIndex = 0;
          }
        }
      }
    });
  };

  LetterBlock.prototype = Object.create(LetterString.prototype, {
    draw: {
      value: function() {
        var index, iy, line;
        iy = this.y;
        for (index in this) {
          line = this[index];
          if (line instanceof LetterString) {
            line.draw(this.context, this.x, iy);
          }
          iy += this.letters.letter_height + this.letters.letter_spacing;
        }
      }
    },
    item: {
      value: function(n) {
        var i;
        i = 0;
        if (isNaN(n = Number(n))) {
          return null;
        }
        while ((this[i] != null) && n > this[i].length) {
          n -= this[i++].length;
        }
        if (this[i] != null) {
          return this[i].item(n);
        } else {
          return null;
        }
      }
    },
    line: {
      value: function(n) {
        if (this[n] instanceof LetterString) {
          return this[n];
        } else {
          return null;
        }
      }
    }
  });

  Object.freeze(LetterBlock.prototype);

  Letters = function(source, letter_width, letter_height, letter_spacing, doc) {
    var canvas, color, context, getIndex, index, memory, source_height, source_width;
    if (doc == null) {
      doc = document;
    }
    if (!(source instanceof HTMLImageElement || source instanceof SVGImageElement || source instanceof HTMLCanvasElement || (typeof createImageBitmap !== "undefined" && createImageBitmap !== null) && source instanceof ImageBitmap)) {
      source = void 0;
    }
    if (isNaN(letter_width = Number(letter_width))) {
      letter_width = 0;
    }
    if (isNaN(letter_height = Number(letter_height))) {
      letter_height = 0;
    }
    if (isNaN(letter_spacing = Number(letter_spacing))) {
      letter_spacing = 1;
    }
    if (!(doc instanceof Document)) {
      doc = document;
    }
    if (source instanceof Element && source.ownerDocument !== doc) {
      source = doc.importNode(source, true);
    }
    if (!((source != null) && !isNaN(source_width = Number(source.naturalWidth ? source.naturalWidth : source.width)))) {
      source_width = 0;
    }
    if (!((source != null) && !isNaN(source_height = Number(source.naturalHeight ? source.naturalHeight : source.height)))) {
      source_height = 0;
    }
    if (source != null) {
      canvas = doc.createElement("canvas");
      canvas.width = Math.floor(source_width / letter_width) * letter_width;
      canvas.height = Math.floor(source_height / letter_height) * letter_height;
      context = canvas.getContext("2d");
      if (!(source instanceof HTMLImageElement && !source.complete)) {
        context.drawImage(source, 0, 0);
      }
    } else {
      canvas = context = null;
    }
    Object.defineProperties(this, {
      canvas: {
        value: canvas
      },
      context: {
        value: context
      },
      height: {
        value: Math.floor(source_height / letter_height)
      },
      letter_height: {
        value: letter_height
      },
      letter_width: {
        value: letter_width
      },
      letter_spacing: {
        value: letter_spacing
      },
      size: {
        value: Math.floor(source_height / letter_height) * Math.floor(source_width / letter_width)
      },
      source: {
        value: source
      },
      width: {
        value: Math.floor(source_width / letter_width)
      }
    });
    color = Letters.NO_COLOR;
    Object.defineProperty(this, "color", {
      get: function() {
        return color;
      },
      set: function(n) {
        if (n === Letters.NO_COLOR) {
          color = n;
          if (this.context instanceof CanvasRenderingContext2D) {
            this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height);
            if (!(this.source instanceof HTMLImageElement && !this.source.complete)) {
              return this.context.drawImage(this.source, 0, 0);
            }
          }
        } else {
          color = String(n);
          if (this.context instanceof CanvasRenderingContext2D) {
            this.context.globalCompositeOperation = "source-in";
            this.context.fillStyle = color;
            this.context.fillRect(0, 0, this.context.canvas.width, this.context.canvas.height);
            return this.context.globalCompositeOperation = "source-over";
          }
        }
      }
    });
    index = this.size;
    memory = [];
    getIndex = function(i) {
      if (memory[i] != null) {
        return memory[i];
      } else {
        return memory[i] = new Letter(this, i);
      }
    };
    while (index-- > 0) {
      Object.defineProperty(this, index, {
        get: getIndex.bind(this, index)
      });
    }
    return this;
  };

  Letters.prototype = Object.create(Object.prototype, {
    clearColor: {
      value: function() {
        return this.color = Letters.NO_COLOR;
      }
    },
    createBlock: {
      value: function() {
        var context, data, x, y;
        context = arguments[0], x = arguments[1], y = arguments[2], data = 4 <= arguments.length ? slice.call(arguments, 3) : [];
        return new (LetterBlock.bind.apply(LetterBlock, [null, this, context, x, y].concat(slice.call(data))))();
      }
    },
    createString: {
      value: function(data) {
        return new LetterString(this, data);
      }
    },
    item: {
      value: function(i) {
        if (this[i] instanceof Letter) {
          return this[i];
        } else {
          return null;
        }
      }
    }
  });

  Object.freeze(Letters.prototype);

  Object.defineProperty(Letters, "NO_COLOR", {
    value: typeof Symbol !== "undefined" && Symbol !== null ? Symbol("none") : Object.freeze(Object.create(null))
  });

  Letters.Block = Object.freeze(LetterBlock);

  Letters.Letter = Object.freeze(Letter);

  Letters.String = Object.freeze(LetterString);

  this.Letters = Object.freeze(Letters);

}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  SCREEN
  Convenient canvas and context packaging
  ---------------------------------------
   */
  var Screen;

  Screen = function(canvas, context) {
    var doc;
    if (context == null) {
      context = "2d";
    }
    if (!(canvas instanceof HTMLCanvasElement)) {
      doc = document;
      canvas = doc.getElementById(canvas);
      if (!(canvas instanceof HTMLCanvasElement)) {
        canvas = void 0;
      }
    } else {
      doc = canvas.ownerDocument;
    }
    return Object.defineProperties(this, {
      canvas: {
        value: canvas,
        enumerable: true
      },
      context: {
        value: (canvas ? canvas.getContext(context) : void 0),
        enumerable: true
      },
      height: {
        get: function() {
          if (this.canvas) {
            return this.canvas.height;
          }
        },
        set: function(n) {
          if (this.canvas) {
            return this.canvas.height = n;
          }
        }
      },
      ownerDocument: {
        value: doc
      },
      width: {
        get: function() {
          if (this.canvas) {
            return this.canvas.width;
          }
        },
        set: function(n) {
          if (this.canvas) {
            return this.canvas.width = n;
          }
        }
      }
    });
  };

  Screen.prototype = Object.create(Object.prototype, {
    clear: {
      value: function() {
        if (!(this.canvas instanceof HTMLCanvasElement)) {
          return;
        }
        switch (false) {
          case !(this.context instanceof CanvasRenderingContext2D):
            this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
            break;
          case !(this.context instanceof WebGLRenderingContext):
            this.context.clear(this.context.COLOR_BUFFER_BIT | context.DEPTH_BUFFER_BIT);
            break;
          default:
            this.canvas.width = this.canvas.width;
        }
      }
    }
  });

  Object.freeze(Screen.prototype);

  this.Screen = Object.freeze(Screen);

}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  SHEET
  Sprite-sheet managing
  ---------------------
   */
  var Sheet, Sprite, drawSprite;

  drawSprite = function(sheet, start_index, context, x, y, frame) {
    var height, i, image, j, source, width;
    if (frame == null) {
      frame = 0;
    }
    if (!(sheet instanceof Sheet && start_index < sheet.size && context instanceof CanvasRenderingContext2D)) {
      return;
    }
    if (isNaN(start_index = Number(start_index))) {
      start_index = 0;
    }
    if (isNaN(x = Math.round(x))) {
      x = 0;
    }
    if (isNaN(y = Math.round(y))) {
      y = 0;
    }
    if (isNaN(frame = Number(frame))) {
      frame = 0;
    }
    start_index += frame;
    i = start_index % sheet.width;
    j = Math.floor(start_index / sheet.width);
    source = sheet.source;
    image = sheet.image;
    if ((source instanceof HTMLImageElement && source.complete || source instanceof SVGImageElement || source instanceof HTMLCanvasElement || (typeof createImageBitmap !== "undefined" && createImageBitmap !== null) && (image instanceof ImageBitmap || source instanceof ImageBitmap)) && !isNaN(i) && !isNaN(j) && (width = Number(sheet.sprite_width)) && (height = Number(sheet.sprite_height))) {
      return context.drawImage(((typeof createImageBitmap !== "undefined" && createImageBitmap !== null) && image instanceof ImageBitmap ? image : source), i * width, j * height, width, height, x, y, width, height);
    }
  };

  Sprite = function(sheet, index, length) {
    if (length == null) {
      length = 1;
    }
    if (!(sheet instanceof Sheet)) {
      sheet = null;
    }
    if (isNaN(index = Number(index))) {
      index = 0;
    }
    if (isNaN(length = Number(length)) || length <= 0) {
      length = 1;
    }
    Object.defineProperty(this, "draw", {
      value: drawSprite.bind(this, sheet, index)
    });
    this.height = sheet ? sheet.sprite_height : 0;
    this.index = index;
    this.frames = length;
    this.sheet = sheet;
    this.width = sheet ? sheet.sprite_width : 0;
    return Object.freeze(this);
  };

  Sprite.prototype = {
    draw: function() {}
  };

  Object.freeze(Sprite.prototype);

  Sheet = function(source, sprite_width, sprite_height) {
    var image, source_height, source_width;
    if (!(source instanceof HTMLImageElement || source instanceof SVGImageElement || source instanceof HTMLCanvasElement || (typeof createImageBitmap !== "undefined" && createImageBitmap !== null) && source instanceof ImageBitmap)) {
      source = null;
    }
    if (isNaN(sprite_width = Number(sprite_width))) {
      sprite_width = 0;
    }
    if (isNaN(sprite_height = Number(sprite_height))) {
      sprite_height = 0;
    }
    if (!((source != null) && !isNaN(source_width = Number(source.naturalWidth != null ? source.naturalWidth : source.width)))) {
      source_width = 0;
    }
    if (!((source != null) && !isNaN(source_height = Number(source.naturalHeight != null ? source.naturalHeight : source.height)))) {
      source_height = 0;
    }
    this.height = Math.floor(source_height / sprite_height);
    this.source = source;
    this.sprite_height = sprite_height;
    this.sprite_width = sprite_width;
    this.width = Math.floor(source_width / sprite_width);
    this.size = this.width * this.height;
    image = null;
    Object.defineProperty(this, "image", {
      get: function() {
        return image;
      }
    });
    if (typeof createImageBitmap !== "undefined" && createImageBitmap !== null) {
      createImageBitmap(source).then(function(img) {
        return image = img;
      });
    }
    return Object.freeze(this);
  };

  Sheet.prototype = {
    drawIndex: function(context, index, x, y) {
      return drawSprite(this, index, context, x, y);
    },
    getSprite: function(index, length) {
      if (length == null) {
        length = 1;
      }
      return new Sprite(this, index, length);
    }
  };

  Object.freeze(Sheet.prototype);

  Sheet.draw = function(context, sprite, x, y, frame) {
    if (frame == null) {
      frame = 0;
    }
    if (sprite instanceof Sprite) {
      return sprite.draw(context, x, y, frame);
    }
  };

  Sheet.drawSheetAtIndex = function(context, sheet, index, x, y) {
    if (sheet instanceof Sheet) {
      return sheet.drawIndex(context, index, x, y);
    }
  };

  Sheet.Sprite = Object.freeze(Sprite);

  this.Sheet = Object.freeze(Sheet);

}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  TILESET
  Maps and collision-checking
  ---------------------------
   */
  var Tilemap, Tileset, collision_constants, decode64;

  decode64 = function(base64) {
    var code, data, i, j, len, n, placeholders, values;
    code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/-_";
    values = {};
    for (i = j = 0, len = code.length; j < len; i = ++j) {
      n = code[i];
      values[n] = i;
    }
    if ((base64 = String(base64)).length % 4) {
      return;
    }
    placeholders = (base64[base64.length - 2] === "=") + (base64[base64.length - 1] === "=");
    data = typeof Uint8Array !== "undefined" && Uint8Array !== null ? new Uint8Array(3 * base64.length / 4 - placeholders) : new Array(3 * base64.length / 4 - placeholders);
    i = 0;
    while (i < base64.length / 4) {
      n = values[base64[4 * i]] << 18 | values[base64[4 * i + 1]] << 12 | values[base64[4 * i + 2]] << 6 | values[base64[4 * i + 3]];
      data[3 * i] = n >> 16 & 0xFF;
      data[3 * i + 1] = n >> 8 & 0xFF;
      data[3 * i + 2] = n & 0xFF;
      i++;
    }
    return data;
  };

  Tilemap = function(tileset, context, map, tiles_wide, x, y, origin_x, origin_y) {
    var tiles_tall;
    if (!(tileset instanceof Tileset)) {
      tileset = null;
    }
    if (!(context instanceof CanvasRenderingContext2D)) {
      context = null;
    }
    map = decode64(map);
    if (isNaN(tiles_wide = Math.ceil(tiles_wide))) {
      tiles_wide = 0;
    }
    tiles_tall = Math.ceil(map.length / tiles_wide);
    if (isNaN(origin_x = Math.round(origin_x))) {
      origin_x = 0;
    }
    if (isNaN(origin_y = Math.round(origin_y))) {
      origin_y = 0;
    }
    if (x === Tilemap.CENTER && (context != null) && (tileset != null)) {
      x = Math.floor((context.canvas.width - tileset.tile_width * tiles_wide) / 2) - origin_x;
    }
    if (isNaN(x = Math.round(x))) {
      x = 0;
    }
    if (y === Tilemap.CENTER && (context != null) && (tileset != null)) {
      y = Math.floor((context.canvas.height - tileset.tile_height * tiles_tall) / 2) - origin_y;
    }
    if (isNaN(y = Math.round(y))) {
      y = 0;
    }
    this.context = context;
    this.map = map;
    if (tileset) {
      this.tile_height = tileset.tile_height;
    }
    if (tileset) {
      this.tile_width = tileset.tile_width;
    }
    this.tiles_tall = tiles_tall;
    this.tiles_wide = tiles_wide;
    this.tileset = tileset;
    Object.defineProperties(this, {
      origin_x: {
        get: function() {
          return origin_x;
        },
        set: function(n) {
          if (!isNaN(n = Math.round(n))) {
            return origin_x = n;
          }
        }
      },
      origin_y: {
        get: function() {
          return origin_y;
        },
        set: function(n) {
          if (!isNaN(n = Math.round(n))) {
            return origin_y = n;
          }
        }
      },
      x: {
        get: function() {
          return x;
        },
        set: function(n) {
          if (!isNaN(n = Math.round(n))) {
            return x = n;
          }
        }
      },
      y: {
        get: function() {
          return y;
        },
        set: function(n) {
          if (!isNaN(n = Math.round(n))) {
            return y = n;
          }
        }
      }
    });
    return Object.freeze(this);
  };

  Tilemap.prototype = Object.create(Object.prototype, {
    collides: {
      value: function(sx, sy) {
        var collision, x, y;
        if (!(this.tileset instanceof Tileset && !(isNaN(sx = Number(sx)) || isNaN(sy = Number(sy))))) {
          return 0x0;
        }
        x = isNaN(this.x) ? sx : sx - this.x;
        y = isNaN(this.y) ? sy : sy - this.y;
        if (x < 0 || x >= this.tile_width * this.tiles_wide || y < 0 || y >= this.tile_height * this.tiles_tall) {
          return 0x0;
        }
        collision = this.tileset.getCollision(this.map[Math.floor(x / this.tile_width) + Math.floor(y / this.tile_height) * this.tiles_wide]);
        return collision & 1 * (1 + (x % this.tile_width >= this.tile_width / 2)) * (1 + 3 * (y % this.tile_height >= this.tile_height / 2));
      }
    },
    draw: {
      value: function() {
        var i, j, ref;
        if (!(this.context instanceof CanvasRenderingContext2D && this.tileset instanceof Tileset && !(isNaN(this.x) || isNaN(this.y) || isNaN(this.origin_x) || isNaN(this.origin_y)) && Number(this.tile_width) && Number(this.tile_height) && Number(this.tiles_wide) && typeof this.map === "object")) {
          return;
        }
        for (i = j = 0, ref = this.map.length; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {
          this.tileset.draw(this.context, this.map[i], Number(this.x) - Number(this.origin_x) + (i % this.tiles_wide) * this.tile_width, Number(this.y) - Number(this.origin_y) + Math.floor(i / this.tiles_wide) * this.tile_height);
        }
      }
    },
    getCollisionEdge: {
      value: function(edge, sx, sy) {
        var at, collision, corner, i, ix, iy, x, y;
        if (edge !== Tilemap.BOTTOM_EDGE && edge !== Tilemap.LEFT_EDGE && edge !== Tilemap.RIGHT_EDGE && edge !== Tilemap.TOP_EDGE || isNaN(sx = Number(sx)) || isNaN(sy = Number(sy))) {
          return;
        }
        at = Tileset.collisions;
        x = Math.round(isNaN(this.x) ? sx : sx - this.x);
        y = Math.round(isNaN(this.y) ? sy : sy - this.y);
        ix = Math.floor(x / this.tile_width);
        iy = Math.floor(y / this.tile_height);
        i = ix + iy * this.tiles_wide;
        if (x % (this.tile_width / 2) === 0 || y % (this.tile_height / 2) === 0 || x < 0 || x >= this.tile_width * this.tiles_wide || y < 0 || y >= this.tile_height * this.tiles_tall) {
          corner = collision = at.NO_COLLISION;
        } else {
          collision = this.tileset.getCollision(this.map[i]);
          corner = this.collides(sx, sy);
        }
        if (!corner) {
          return (edge === Tilemap.LEFT_EDGE || edge === Tilemap.RIGHT_EDGE ? sx : sy);
        }
        switch (edge) {
          case Tilemap.BOTTOM_EDGE:
            return (corner === at.TOPLEFT && collision & at.BOTTOMLEFT || corner === at.TOPRIGHT && collision & at.BOTTOMRIGHT || corner === at.BOTTOMLEFT || corner === at.BOTTOMRIGHT ? iy * this.tile_height + this.tile_height + this.y : iy * this.tile_height + this.tile_height / 2 + this.y);
          case Tilemap.LEFT_EDGE:
            return (corner === at.TOPRIGHT && collision & at.TOPLEFT || corner === at.BOTTOMRIGHT && collision & at.BOTTOMLEFT || corner === at.TOPLEFT || corner === at.BOTTOMLEFT ? ix * this.tile_width + this.x : ix * this.tile_width + this.tile_width / 2 + this.x);
          case Tilemap.RIGHT_EDGE:
            return (corner === at.TOPLEFT && collision & at.TOPRIGHT || corner === at.BOTTOMLEFT && collision & at.BOTTOMRIGHT || corner === at.TOPRIGHT || corner === at.BOTTOMRIGHT ? ix * this.tile_width + this.tile_width + this.x : ix * this.tile_width + this.tile_width / 2 + this.x);
          case Tilemap.TOP_EDGE:
            return (corner === at.BOTTOMLEFT && collision & at.TOPLEFT || corner === at.BOTTOMRIGHT && collision & at.TOPRIGHT || corner === at.TOPLEFT || corner === at.TOPRIGHT ? iy * this.tile_height + this.y : iy * this.tile_height + this.tile_height / 2 + this.y);
        }
      }
    }
  });

  Object.freeze(Tilemap.prototype);

  Object.defineProperties(Tilemap, {
    BOTTOM_EDGE: {
      value: typeof Symbol !== "undefined" && Symbol !== null ? Symbol("bottom") : Object.freeze(Object.create(null))
    },
    CENTER: {
      value: typeof Symbol !== "undefined" && Symbol !== null ? Symbol("center") : Object.freeze(Object.create(null))
    },
    LEFT_EDGE: {
      value: typeof Symbol !== "undefined" && Symbol !== null ? Symbol("left") : Object.freeze(Object.create(null))
    },
    RIGHT_EDGE: {
      value: typeof Symbol !== "undefined" && Symbol !== null ? Symbol("right") : Object.freeze(Object.create(null))
    },
    TOP_EDGE: {
      value: typeof Symbol !== "undefined" && Symbol !== null ? Symbol("top") : Object.freeze(Object.create(null))
    }
  });

  Tileset = function(sheet, tile_width, tile_height, collisions, draw) {
    if (isNaN(tile_width = Number(tile_width))) {
      tile_width = 0;
    }
    if (isNaN(tile_height = Number(tile_height))) {
      tile_height = 0;
    }
    if (!(typeof draw === "function" || draw instanceof Function)) {
      draw = null;
    }
    this.collisions = decode64(collisions);
    this.drawFunction = draw;
    this.sheet = sheet;
    this.tile_height = tile_height;
    this.tile_width = tile_width;
    return Object.freeze(this);
  };

  Tileset.prototype = Object.create(Object.prototype, {
    draw: {
      value: function(context, index, x, y) {
        if (typeof this.drawFunction === "function" || this.drawFunction instanceof Function) {
          return this.drawFunction(context, this.sheet, index, x, y);
        }
      }
    },
    getMap: {
      value: function(context, map, tiles_wide, x, y, origin_x, origin_y) {
        return new Tilemap(this, context, map, tiles_wide, x, y, origin_x, origin_y);
      }
    },
    getCollision: {
      value: function(index) {
        if (isNaN(index)) {
          return 0;
        } else {
          return (this.collisions[Math.floor(index / 2)] >> 4 * ((index + 1) % 2)) & 0xF;
        }
      }
    }
  });

  Object.freeze(Tileset.prototype);

  collision_constants = Object.create(Object.prototype, {
    EMPTY: {
      value: 0x0,
      enumerable: true
    },
    NO_COLLISION: {
      value: 0
    },
    SECTOR_00: {
      value: 0x1,
      enumerable: true
    },
    SECTOR_01: {
      value: 0x2,
      enumerable: true
    },
    SECTOR_02: {
      value: 0x4,
      enumerable: true
    },
    SECTOR_03: {
      value: 0x8,
      enumerable: true
    },
    SECTOR_04: {
      value: 0x10,
      enumerable: true
    },
    SECTOR_05: {
      value: 0x20,
      enumerable: true
    },
    SECTOR_06: {
      value: 0x40,
      enumerable: true
    },
    SECTOR_07: {
      value: 0x80,
      enumerable: true
    },
    SECTOR_08: {
      value: 0x100,
      enumerable: true
    },
    SECTOR_09: {
      value: 0x200,
      enumerable: true
    },
    SECTOR_10: {
      value: 0x400,
      enumerable: true
    },
    SECTOR_11: {
      value: 0x800,
      enumerable: true
    },
    SECTOR_12: {
      value: 0x1000,
      enumerable: true
    },
    SECTOR_13: {
      value: 0x2000,
      enumerable: true
    },
    SECTOR_14: {
      value: 0x4000,
      enumerable: true
    },
    SECTOR_15: {
      value: 0x8000,
      enumerable: true
    },
    SECTOR_16: {
      value: 0x10000,
      enumerable: true
    },
    SECTOR_17: {
      value: 0x20000,
      enumerable: true
    },
    SECTOR_18: {
      value: 0x40000,
      enumerable: true
    },
    SECTOR_19: {
      value: 0x80000,
      enumerable: true
    },
    SECTOR_20: {
      value: 0x100000,
      enumerable: true
    },
    SECTOR_21: {
      value: 0x200000,
      enumerable: true
    },
    SECTOR_22: {
      value: 0x400000,
      enumerable: true
    },
    SECTOR_23: {
      value: 0x800000,
      enumerable: true
    },
    SECTOR_24: {
      value: 0x1000000,
      enumerable: true
    },
    SECTOR_25: {
      value: 0x2000000,
      enumerable: true
    },
    SECTOR_26: {
      value: 0x4000000,
      enumerable: true
    },
    SECTOR_27: {
      value: 0x8000000,
      enumerable: true
    },
    SECTOR_28: {
      value: 0x10000000,
      enumerable: true
    },
    SECTOR_29: {
      value: 0x20000000,
      enumerable: true
    },
    SECTOR_30: {
      value: 0x40000000,
      enumerable: true
    },
    SECTOR_31: {
      value: 0x80000000,
      enumerable: true
    },
    TOPLEFT: {
      value: 1
    },
    TOPRIGHT: {
      value: 2
    },
    BOTTOMLEFT: {
      value: 4
    },
    BOTTOMRIGHT: {
      value: 8
    }
  });

  Object.defineProperty(Tileset, "collisions", {
    value: Object.freeze(collision_constants)
  });

  Tileset.Map = Object.freeze(Tilemap);

  this.Tileset = Object.freeze(Tileset);

}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  INTRODUCTION
  The Jelli Game Engine
  ---------------------
   */


}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  JELLI
  The Jelli Game Engine
  ---------------------
   */
  var Jelli;

  Jelli = function() {
    return Object.defineProperty(this, "functions", {
      value: null,
      writable: true
    });
  };

  Jelli.prototype = Object.create(Object.prototype, {
    run: {
      value: function(name) {
        if ((this.functions != null) && (typeof this.functions[name] === "function" || this.functions[name] instanceof Function)) {
          return this.functions[name].call(this);
        }
      }
    }
  });

  Object.freeze(Jelli.prototype);

  this.Jelli = Object.freeze(Jelli);

}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  GAME
  The Jelli Game Engine
  ---------------------
   */
  var Game;

  Game = function(doc) {
    var area, data, item, j, k, l, len, len1, len2, len3, len4, m, name, o, ref, ref1, ref2, ref3, ref4, ref5, ref6, resized, screen, view;
    if (doc == null) {
      doc = document;
    }
    Jelli.call(this);
    if (!(doc instanceof Document && (doc.game == null))) {
      return;
    }
    Object.defineProperty(doc, "game", {
      value: this
    });
    data = doc.createElement("div");
    ref = doc.getElementsByClassName("DATA");
    for (j = 0, len = ref.length; j < len; j++) {
      item = ref[j];
      data.appendChild(item);
    }
    Object.defineProperties(this, {
      characters: {
        value: new Collection(this, Character)
      },
      data: {
        value: data
      },
      document: {
        value: doc
      },
      functions: {
        value: document.body.functions
      },
      images: {
        value: new Collection(this, PlacementImage)
      },
      letters: {
        value: {}
      },
      screens: {
        value: {}
      },
      sheets: {
        value: {}
      },
      texts: {
        value: new Collection(this, Text)
      },
      tilesets: {
        value: {}
      },
      views: {
        value: {}
      },
      window: {
        value: doc.defaultView || window
      }
    });
    area = null;
    resized = true;
    Object.defineProperties(this, {
      area: {
        get: function() {
          return area;
        },
        set: function(n) {
          if (n instanceof Area) {
            return area = n;
          } else {
            return this.loadArea(n);
          }
        }
      },
      resized: {
        get: function() {
          return resized;
        },
        set: function(n) {
          return resized = !!n;
        }
      }
    });
    ref1 = doc.getElementsByClassName("VIEW");
    for (k = 0, len1 = ref1.length; k < len1; k++) {
      view = ref1[k];
      this.views[view.id] = new View(this, view);
      ref2 = this.views[view.id].screens;
      for (name in ref2) {
        screen = ref2[name];
        this.screens[name] = screen;
      }
    }
    Object.freeze(this.views);
    Object.freeze(this.screens);
    ref3 = data.getElementsByClassName("LETTERS");
    for (l = 0, len2 = ref3.length; l < len2; l++) {
      item = ref3[l];
      this.letters[item.id] = new Letters(item, item.getAttribute("data-sprite-width"), item.getAttribute("data-sprite-height"), item.getAttribute("data-letter-spacing"), doc);
    }
    Object.freeze(this.letters);
    ref4 = data.getElementsByClassName("SHEET");
    for (m = 0, len3 = ref4.length; m < len3; m++) {
      item = ref4[m];
      this.sheets[item.id] = new Sheet(item, item.getAttribute("data-sprite-width"), item.getAttribute("data-sprite-height"));
    }
    Object.freeze(this.sheets);
    ref5 = data.getElementsByClassName("TILESET");
    for (o = 0, len4 = ref5.length; o < len4; o++) {
      item = ref5[o];
      this.tilesets[item.id] = new Tileset(new Sheet(item, item.getAttribute("data-sprite-width"), item.getAttribute("data-sprite-height")), item.getAttribute("data-sprite-width"), item.getAttribute("data-sprite-height"), (ref6 = item.getAttribute("data-collisions")) != null ? ref6.trim() : void 0, Sheet.drawSheetAtIndex);
    }
    Object.freeze(this.tilesets);
    this.run("init");
    Object.seal(this);
    this.window.addEventListener("resize", this, false);
    this.window.requestAnimationFrame(this.draw.bind(this));
    return this.step();
  };

  Game.prototype = Object.create(Jelli.prototype, {
    clearScreen: {
      value: function(id) {
        return this.screens[id].canvas.setAttribute("data-clear", "");
      }
    },
    draw: {
      value: function() {
        var i, ref, ref1, ref2, ref3, ref4, screen, text, view;
        if (this.resized) {
          ref = this.views;
          for (i in ref) {
            view = ref[i];
            view.layout();
          }
        }
        ref1 = this.screens;
        for (i in ref1) {
          screen = ref1[i];
          if (screen.canvas.hasAttribute("data-clear")) {
            screen.clear();
            screen.canvas.removeAttribute("data-clear");
            continue;
          }
          switch (screen.canvas.getAttribute("data-type")) {
            case "area":
              if (this.area instanceof Area && this.area.clear) {
                screen.clear();
              }
              break;
            case "animation":
              screen.clear();
          }
        }
        if (this.area instanceof Area) {
          this.area.draw();
        }
        if ((ref2 = this.characters) != null) {
          ref2.doForEach(function(character) {
            return character.draw();
          });
        }
        if ((ref3 = this.images) != null) {
          ref3.doForEach(function(image) {
            return image.draw();
          });
        }
        ref4 = this.texts;
        for (i in ref4) {
          text = ref4[i];
          text.draw();
        }
        this.resized = false;
        return this.window.requestAnimationFrame(this.draw.bind(this));
      }
    },
    getDataElement: {
      value: function(className, id) {
        var elt, elts, qs;
        elts = className ? this.data.getElementsByClassName(className) : this.data.getElementsByTagName("*");
        if (typeof id === "number" || id instanceof Number) {
          elt = elts.item(id);
        }
        if (elt != null) {
          return elt;
        }
        qs = className ? "#" + id + "." + className : "#" + id;
        if (elts instanceof HTMLCollection && (typeof elts.namedItem === "function" || elts.namedItem instanceof Function)) {
          return elts.namedItem(id);
        } else {
          return this.data.querySelector(qs);
        }
        return this.window.requestAnimationFrame(this.draw.bind(this));
      }
    },
    getDocElement: {
      value: function(className, id) {
        var elt, elts, qs;
        elts = className ? document.getElementsByClassName(className) : document.getElementsByTagName("*");
        if (typeof id === "number" || id instanceof Number) {
          elt = elts.item(id);
        }
        if (elt != null) {
          return elt;
        }
        qs = className ? "#" + id + "." + className : "#" + id;
        if (elts instanceof HTMLCollection && (typeof elts.namedItem === "function" || elts.namedItem instanceof Function)) {
          return elts.namedItem(id);
        } else {
          return document.querySelector(qs);
        }
      }
    },
    handleEvent: {
      value: function(e) {
        switch (e.type) {
          case "resize":
            return this.resized = true;
        }
      }
    },
    loadArea: {
      value: function(index) {
        return this.area = new Area(this, index);
      }
    },
    step: {
      value: function() {
        var ref;
        this.run("step");
        if ((ref = this.characters) != null) {
          ref.doForEach(function(character) {
            return character.step();
          });
        }
        if (this.area instanceof Area) {
          this.area.step();
        }
        this.run("then");
        return this.window.setTimeout(this.step.bind(this), 1000 / 60);
      }
    }
  });

  Object.freeze(Game.prototype);

  Game.defineFunctions = function(element, function_object) {
    if (!(element instanceof Element)) {
      element = document.getElementById(element);
    }
    return element.functions = Object.freeze(function_object);
  };

  Game.getSymbol = function(name) {
    if (typeof Symbol !== "undefined" && Symbol !== null) {
      return Symbol(name);
    } else {
      return Object.freeze(Object.create(null, {
        toString: {
          value: String.bind(null, name)
        }
      }));
    }
  };

  this.Game = Object.freeze(Game);

}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  AREA
  The Jelli Game Engine
  ---------------------
   */
  var Area;

  Area = function(game, id) {
    var clear, elt, i, j, k, l, len, len1, len2, map, ref, ref1, ref2, ref3, ref4, ref5, ref6, x, y;
    Jelli.call(this);
    if (!(game instanceof Game)) {
      game = null;
    }
    elt = (game != null ? game.getDataElement("AREA", id) : void 0) || null;
    Object.defineProperty(this, "game", {
      value: game
    });
    Object.defineProperties(this, {
      characters: {
        value: new Collection(this, Character)
      },
      functions: {
        value: elt != null ? elt.functions : void 0
      },
      images: {
        value: new Collection(this, PlacementImage)
      },
      maps: {
        value: []
      }
    });
    Object.defineProperty(this, "id", {
      get: function() {
        return id;
      },
      set: function(n) {
        return this.game.area = n;
      }
    });
    clear = true;
    Object.defineProperty(this, "clear", {
      get: function() {
        return clear;
      },
      set: function(n) {
        return clear = !!n;
      }
    });
    x = 0;
    y = 0;
    Object.defineProperties(this, {
      x: {
        get: function() {
          return x;
        },
        set: function(n) {
          var j, len, map, ref;
          if (isNaN(x = Number(n))) {
            return;
          }
          ref = this.maps;
          for (j = 0, len = ref.length; j < len; j++) {
            map = ref[j];
            map.origin_x = x;
          }
          return this.clear = true;
        }
      },
      y: {
        get: function() {
          return y;
        },
        set: function(n) {
          var j, len, map, ref;
          if (isNaN(y = Number(n))) {
            return;
          }
          ref = this.maps;
          for (j = 0, len = ref.length; j < len; j++) {
            map = ref[j];
            map.origin_y = y;
          }
          return this.clear = true;
        }
      }
    });
    ref = (elt != null ? elt.getElementsByClassName("MAP") : void 0) || [];
    for (i = j = 0, len = ref.length; j < len; i = ++j) {
      map = ref[i];
      this.maps[i] = game != null ? (ref1 = game.tilesets[map.getAttribute("data-tileset")]) != null ? ref1.getMap(game != null ? (ref2 = game.screens[map.getAttribute("data-screen")]) != null ? ref2.context : void 0 : void 0, map.textContent.trim(), map.getAttribute("data-mapwidth"), map.getAttribute("data-dx"), map.getAttribute("data-dy"), x, y) : void 0 : void 0;
    }
    Object.freeze(this.maps);
    ref4 = (elt != null ? (ref3 = elt.getAttribute("data-characters")) != null ? ref3.split(/\s+/) : void 0 : void 0) || [];
    for (k = 0, len1 = ref4.length; k < len1; k++) {
      i = ref4[k];
      this.characters.load(i);
    }
    ref6 = (elt != null ? (ref5 = elt.getAttribute("data-images")) != null ? ref5.split(/\s+/) : void 0 : void 0) || [];
    for (l = 0, len2 = ref6.length; l < len2; l++) {
      i = ref6[l];
      this.images.load(i);
    }
    this.run("init");
    return Object.seal(this);
  };

  Area.prototype = Object.create(Jelli.prototype, {
    draw: {
      value: function(context) {
        var j, len, map, ref, ref1, ref2;
        if (this.clear) {
          ref = this.maps;
          for (j = 0, len = ref.length; j < len; j++) {
            map = ref[j];
            map.draw();
          }
        }
        if ((ref1 = this.characters) != null) {
          ref1.doForEach(function(character) {
            return character.draw();
          });
        }
        if ((ref2 = this.images) != null) {
          ref2.doForEach(function(image) {
            return image.draw();
          });
        }
        return this.clear = false;
      }
    },
    step: {
      value: function() {
        var ref;
        this.run("step");
        if ((ref = this.characters) != null) {
          ref.doForEach(function(character) {
            return character.step();
          });
        }
        return this.run("then");
      }
    }
  });

  Object.freeze(Area.prototype);

  this.Area = Object.freeze(Area);

}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  COLLECTION
  The Jelli Game Engine
  ---------------------
   */
  var Collection, addNameless, iterateNameless,
    slice = [].slice;

  addNameless = function(nameless, item) {
    if (!(typeof nameless === "object" && typeof item === "object" && (nameless.next != null))) {
      return;
    }
    nameless[nameless.next] = item;
    Object.defineProperty(nameless[nameless.next], "kill", {
      value: this.kill.bind(nameless, nameless.next)
    });
    return nameless[nameless.next++];
  };

  iterateNameless = function(nameless, fn) {
    var i, item;
    if (!(typeof nameless === "object" && (typeof fn === "function" || fn instanceof Function))) {
      return;
    }
    for (i in nameless) {
      item = nameless[i];
      if ((this.Type == null) || item instanceof this.Type) {
        fn(item);
      }
    }
  };

  Collection = function(parent, constructor) {
    var area, game, nameless;
    if (parent instanceof Area) {
      area = parent;
      game = parent.game;
    } else if (parent instanceof Game) {
      game = parent;
    }
    Object.defineProperties(this, {
      area: {
        value: area || null
      },
      Type: {
        value: typeof constructor === "function" || constructor instanceof Function ? constructor : null
      },
      game: {
        value: game || null
      },
      parent: {
        value: typeof parent === "object" ? parent : null
      }
    });
    nameless = {};
    Object.defineProperty(nameless, "next", {
      value: 0,
      writable: true
    });
    return Object.defineProperties(this, {
      "\uD83D\uDE36+": {
        value: addNameless.bind(this, nameless)
      },
      "doForNameless": {
        value: iterateNameless.bind(this, nameless)
      }
    });
  };

  Collection.prototype = Object.create(Object.prototype, {
    doForEach: {
      value: function(fn) {
        var key, value;
        if (!(typeof fn === "function" || fn instanceof Function)) {
          return;
        }
        for (key in this) {
          value = this[key];
          if ((this.Type == null) || value instanceof this.Type) {
            fn(value, key);
          }
        }
        this.doForNameless(fn);
        return this;
      }
    },
    kill: {
      value: function(name) {
        var instance, ref;
        if ((ref = Object.getOwnPropertyDescriptor(this, name)) != null ? ref.configurable : void 0) {
          instance = this[name];
          delete this[name];
        } else {
          instance = null;
        }
        return instance;
      }
    },
    load: {
      value: function() {
        var args, name;
        name = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
        if (this[name] == null) {
          Object.defineProperty(this, name, {
            configurable: true,
            enumerable: true,
            value: typeof this.Type === "function" || this.Type instanceof Function ? (function(func, args, ctor) {
              ctor.prototype = func.prototype;
              var child = new ctor, result = func.apply(child, args);
              return Object(result) === result ? result : child;
            })(this.Type, [this, name].concat(slice.call(args)), function(){}) : null
          });
          return Object.defineProperty(this[name], "kill", {
            value: this.kill.bind(this, name)
          });
        } else {
          return null;
        }
      }
    },
    loadNameless: {
      value: function() {
        var args;
        args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
        return this["\uD83D\uDE36+"](typeof this.Type === "function" || this.Type instanceof Function ? (function(func, args, ctor) {
          ctor.prototype = func.prototype;
          var child = new ctor, result = func.apply(child, args);
          return Object(result) === result ? result : child;
        })(this.Type, [this].concat(slice.call(args)), function(){}) : null);
      }
    }
  });

  Object.freeze(Collection.prototype);

  this.Collection = Object.freeze(Collection);

}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  UNIT
  The Jelli Game Engine
  ---------------------
   */
  var Unit;

  Unit = function(game, screen, id, x, y, width, height, origin_x, origin_y) {
    Jelli.call(this);
    if (!(game instanceof Game)) {
      game = null;
    }
    if (!(screen instanceof Screen)) {
      screen = null;
    }
    id = String(id);
    if (isNaN(width = Number(width))) {
      width = 0;
    }
    if (isNaN(height = Number(height))) {
      height = 0;
    }
    if (isNaN(origin_x = Number(origin_x))) {
      origin_x = width / 2;
    }
    if (isNaN(origin_y = Number(origin_y))) {
      origin_y = height / 2;
    }
    if (isNaN(x = Number(x))) {
      x = origin_x;
    }
    if (isNaN(y = Number(y))) {
      y = origin_y;
    }
    Object.defineProperties(this, {
      area: {
        get: function() {
          return game.area;
        }
      },
      game: {
        value: game || null
      },
      height: {
        value: height
      },
      id: {
        value: id,
        enumerable: true
      },
      origin_x: {
        value: origin_x
      },
      origin_y: {
        value: origin_y
      },
      screen: {
        value: screen
      },
      width: {
        value: width
      },
      x: {
        enumerable: true,
        get: function() {
          return x;
        },
        set: function(n) {
          if (!isNaN(n)) {
            return x = Number(n);
          }
        }
      },
      y: {
        enumerable: true,
        get: function() {
          return y;
        },
        set: function(n) {
          if (!isNaN(n)) {
            return y = Number(n);
          }
        }
      }
    });
    Object.defineProperties(this, {
      screen_x: {
        get: (function(_this) {
          return function() {
            var ref;
            return x - ((ref = _this.area) != null ? ref.x : void 0);
          };
        })(this),
        set: (function(_this) {
          return function(n) {
            var ref;
            if (!isNaN(n)) {
              return x = Number(n) + ((ref = _this.area) != null ? ref.x : void 0);
            }
          };
        })(this)
      },
      screen_y: {
        get: (function(_this) {
          return function() {
            var ref;
            return y - ((ref = _this.area) != null ? ref.y : void 0);
          };
        })(this),
        set: (function(_this) {
          return function(n) {
            var ref;
            if (!isNaN(n)) {
              return y = Number(n) + ((ref = _this.area) != null ? ref.y : void 0);
            }
          };
        })(this)
      }
    });
    Object.defineProperty(this, "kill", {
      value: function() {},
      writable: true
    });
    Object.defineProperty(this, "edges", {
      value: Object.create(null, {
        top: {
          enumerable: true,
          get: function() {
            return y - origin_y;
          },
          set: function(n) {
            if (!isNaN(n)) {
              return y = Number(n) + origin_y;
            }
          }
        },
        bottom: {
          enumerable: true,
          get: function() {
            return y - origin_y + height;
          },
          set: function(n) {
            if (!isNaN(n)) {
              return y = Number(n) - height + origin_y;
            }
          }
        },
        left: {
          enumerable: true,
          get: function() {
            return x - origin_x;
          },
          set: function(n) {
            if (!isNaN(n)) {
              return x = Number(n) + origin_x;
            }
          }
        },
        right: {
          enumerable: true,
          get: function() {
            return x - origin_x + width;
          },
          set: function(n) {
            if (!isNaN(n)) {
              return x = Number(n) - width + origin_x;
            }
          }
        },
        screen_top: {
          enumerable: true,
          get: (function(_this) {
            return function() {
              return y - origin_y - _this.area.y;
            };
          })(this),
          set: (function(_this) {
            return function(n) {
              if (!isNaN(n)) {
                return y = Number(n) + origin_y + _this.area.y;
              }
            };
          })(this)
        },
        screen_bottom: {
          enumerable: true,
          get: (function(_this) {
            return function() {
              return y - origin_y + height - _this.area.y;
            };
          })(this),
          set: (function(_this) {
            return function(n) {
              if (!isNaN(n)) {
                return y = Number(n) - height + origin_y + _this.area.y;
              }
            };
          })(this)
        },
        screen_left: {
          enumerable: true,
          get: (function(_this) {
            return function() {
              return x - origin_x - _this.area.x;
            };
          })(this),
          set: (function(_this) {
            return function(n) {
              if (!isNaN(n)) {
                return x = Number(n) + origin_x + _this.area.x;
              }
            };
          })(this)
        },
        screen_right: {
          enumerable: true,
          get: (function(_this) {
            return function() {
              return x - origin_x + width - _this.area.x;
            };
          })(this),
          set: (function(_this) {
            return function(n) {
              if (!isNaN(n)) {
                return x = Number(n) - width + origin_x + _this.area.x;
              }
            };
          })(this)
        }
      })
    });
    return Object.freeze(this.edges);
  };

  Unit.prototype = Object.create(Jelli.prototype, {
    draw: {
      value: function() {}
    },
    setPosition: {
      value: function(x, y) {
        this.x = x;
        return this.y = y;
      }
    }
  });

  Object.freeze(Unit.prototype);

  this.Unit = Object.freeze(Unit);

}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  PLACEMENTIMAGE
  The Jelli Game Engine
  ---------------------
   */
  var PlacementImage,
    slice = [].slice;

  PlacementImage = function() {
    var collection, elt, game, id, name, optional_args, placed, source_height, source_width, x, y;
    collection = arguments[0], name = arguments[1], optional_args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
    switch (optional_args.length) {
      case 0:
        id = name;
        break;
      case 1:
        if (typeof optional_args[0] === "boolean" || optional_args[0] instanceof Boolean) {
          id = name;
          placed = optional_args[0];
        } else {
          id = optional_args[0];
        }
        break;
      case 2:
        if (typeof optional_args[1] === "boolean" || optional_args[1] instanceof Boolean) {
          id = optional_args[0];
          placed = optional_args[1];
        } else {
          id = name;
          if (isNaN(x = Number(optional_args[0]))) {
            x = null;
          }
          if (isNaN(y = Number(optional_args[1]))) {
            y = null;
          }
        }
        break;
      case 3:
        if (typeof optional_args[2] === "boolean" || optional_args[2] instanceof Boolean) {
          id = name;
          if (isNaN(x = Number(optional_args[0]))) {
            x = null;
          }
          if (isNaN(y = Number(optional_args[1]))) {
            y = null;
          }
          placed = optional_args[2];
        } else {
          id = optional_args[0];
          if (isNaN(x = Number(optional_args[1]))) {
            x = null;
          }
          if (isNaN(y = Number(optional_args[2]))) {
            y = null;
          }
          placed = false;
        }
        break;
      default:
        id = optional_args[0];
        if (isNaN(x = Number(optional_args[1]))) {
          x = null;
        }
        if (isNaN(y = Number(optional_args[2]))) {
          y = null;
        }
        placed = optional_args[3];
    }
    placed = !!placed;
    if (!(collection instanceof Collection)) {
      collection = null;
    }
    game = collection != null ? collection.game : null;
    elt = (game != null ? game.getDataElement("IMAGE", id) : void 0) || null;
    if (!(elt instanceof HTMLImageElement || elt instanceof SVGImageElement || elt instanceof HTMLCanvasElement || (typeof createImageBitmap !== "undefined" && createImageBitmap !== null) && elt instanceof ImageBitmap)) {
      elt = null;
    }
    if (!((elt != null) && !isNaN(source_width = Number(elt.naturalWidth != null ? elt.naturalWidth : source.width)))) {
      source_width = 0;
    }
    if (!((elt != null) && !isNaN(source_height = Number(elt.naturalHeight != null ? elt.naturalHeight : source.height)))) {
      source_height = 0;
    }
    Unit.call(this, game, game.screens[elt.getAttribute("data-screen")], id, x, y, source_width, source_height, elt != null ? elt.getAttribute("data-origin-x") : void 0, elt != null ? elt.getAttribute("data-origin-y") : void 0);
    Object.defineProperties(this, {
      placed: {
        get: function() {
          return placed;
        },
        set: function(n) {
          return placed = !!n;
        }
      },
      source: {
        value: elt
      }
    });
    return Object.seal(this);
  };

  PlacementImage.prototype = Object.create(Unit.prototype, {
    draw: {
      value: function() {
        if (this.placed && this.screen instanceof Screen && (this.source instanceof HTMLImageElement && this.source.complete || this.source instanceof SVGImageElement || this.source instanceof HTMLCanvasElement || (typeof createImageBitmap !== "undefined" && createImageBitmap !== null) && (this.source instanceof ImageBitmap || this.source instanceof ImageBitmap))) {
          return this.screen.context.drawImage(this.source, Math.floor(this.edges.screen_left), Math.floor(this.edges.screen_top));
        }
      }
    },
    togglePlacement: {
      value: function(n) {
        if (n != null) {
          return this.placed = !!n;
        } else {
          return this.placed = !this.placed;
        }
      }
    }
  });

  Object.freeze(PlacementImage.prototype);

  this.PlacementImage = Object.freeze(PlacementImage);

}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  CHARACTER
  The Jelli Game Engine
  ---------------------
   */
  var Character,
    slice = [].slice;

  Character = function() {
    var collection, current_sprite, direction, elt, frame, game, i, id, index, len, name, optional_args, ref, ref1, ref2, ref3, ref4, ref5, sprite, sprite_list, sprites, velocity, x, y;
    collection = arguments[0], name = arguments[1], optional_args = 3 <= arguments.length ? slice.call(arguments, 2) : [];
    switch (optional_args.length) {
      case 0:
        id = name;
        break;
      case 1:
        id = optional_args[0];
        break;
      case 2:
        id = name;
        if (isNaN(x = Number(optional_args[0]))) {
          x = null;
        }
        if (isNaN(y = Number(optional_args[1]))) {
          y = null;
        }
        break;
      case 3:
        id = name;
        if (isNaN(x = Number(optional_args[0]))) {
          x = null;
        }
        if (isNaN(y = Number(optional_args[1]))) {
          y = null;
        }
        current_sprite = optional_args[2];
        break;
      default:
        id = optional_args[0];
        if (isNaN(x = Number(optional_args[1]))) {
          x = null;
        }
        if (isNaN(y = Number(optional_args[2]))) {
          y = null;
        }
        current_sprite = optional_args[3];
    }
    if (current_sprite == null) {
      current_sprite = 0;
    }
    if (!(collection instanceof Collection)) {
      collection = null;
    }
    game = collection != null ? collection.game : null;
    elt = (game != null ? game.getDataElement("CHARACTER", id) : void 0) || null;
    sprites = {};
    sprite_list = (game != null ? game.getDataElement("SPRITELIST", elt != null ? elt.getAttribute("data-sprites") : void 0) : void 0) || null;
    ref = (sprite_list != null ? sprite_list.getElementsByClassName("SPRITE") : void 0) || [];
    for (index = i = 0, len = ref.length; i < len; index = ++i) {
      sprite = ref[index];
      sprites[index] = (game != null ? (ref1 = game.sheets[sprite_list.dataset.sheet]) != null ? ref1.getSprite(sprite.dataset.index, sprite.dataset.length) : void 0 : void 0) || null;
      if (sprite.hasAttribute("title") && isNaN(sprite.getAttribute("title"))) {
        sprites[sprite.getAttribute("title")] = sprites[index];
      }
    }
    Object.freeze(sprites);
    Unit.call(this, game, game.screens[elt != null ? elt.getAttribute("data-screen") : void 0], id, x, y, (sprite_list != null ? sprite_list.dataset.boxWidth : void 0) || ((ref2 = sprites[0]) != null ? ref2.width : void 0), (sprite_list != null ? sprite_list.dataset.boxHeight : void 0) || ((ref3 = sprites[0]) != null ? ref3.height : void 0), sprite_list != null ? sprite_list.dataset.originX : void 0, sprite_list != null ? sprite_list.dataset.originY : void 0);
    Object.defineProperties(this, {
      box_x: {
        value: isNaN(sprite_list != null ? sprite_list.dataset.boxX : void 0) ? 0 : Number(sprite_list != null ? sprite_list.dataset.boxX : void 0)
      },
      box_y: {
        value: isNaN(sprite_list != null ? sprite_list.dataset.boxY : void 0) ? 0 : Number(sprite_list != null ? sprite_list.dataset.boxY : void 0)
      },
      collides: {
        value: (function() {
          switch (elt != null ? elt.dataset.collides : void 0) {
            case void 0:
              return Character.collisions.DOES_NOT_COLLIDE;
            case "map":
              return Character.collisions.MAP;
            case "character":
              return Character.collisions.CHARACTER;
            default:
              return Character.collisions.ALL;
          }
        })()
      },
      functions: {
        value: elt != null ? elt.functions : void 0
      },
      max_velocity: {
        value: isNaN(elt != null ? elt.dataset.speedcap : void 0) ? Character.NO_MAX_VELOCITY : Number(elt != null ? elt.dataset.speedcap : void 0)
      },
      sprite_height: {
        value: ((ref4 = sprites[0]) != null ? ref4.height : void 0) || 0
      },
      sprite_width: {
        value: (ref5 = sprites[0]) != null ? ref5.width : void 0
      },
      sprites: {
        value: sprites
      },
      step: {
        value: this.run.bind(this, "step")
      }
    });
    if (!this.sprites[current_sprite]) {
      current_sprite = 0;
    }
    direction = null;
    frame = 0;
    velocity = 0;
    Object.defineProperties(this, {
      direction: {
        get: function() {
          return direction;
        },
        set: function(n) {
          return direction = isNaN(n) ? null : Number(n);
        },
        enumerable: true
      },
      frame: {
        get: function() {
          return frame;
        },
        set: function(n) {
          if (!(isNaN(n = Number(n)) || n < 0 || n >= this.sprites[this.sprite].frames)) {
            return frame = n;
          }
        },
        enumerable: true
      },
      sprite: {
        get: function() {
          return current_sprite;
        },
        set: function(n) {
          if (this.sprites[n] != null) {
            return current_sprite = n;
          }
        },
        enumerable: true
      },
      velocity: {
        get: function() {
          return velocity;
        },
        set: function(n) {
          if (!isNaN(n)) {
            return velocity = Number(n);
          }
        },
        enumerable: true
      }
    });
    this.run("init");
    return Object.seal(this);
  };

  Character.prototype = Object.create(Unit.prototype, {
    draw: {
      value: function() {
        var ref, ref1;
        return (ref = this.sprites[this.sprite]) != null ? ref.draw((ref1 = this.screen) != null ? ref1.context : void 0, this.edges.screen_left - this.box_x, this.edges.screen_top - this.box_y, this.frame) : void 0;
      }
    },
    getCollisionEdge: {
      value: function(edge, x, y) {
        if (!((edge === Tileset.Map.BOTTOM_EDGE || edge === Tileset.Map.LEFT_EDGE || edge === Tileset.Map.RIGHT_EDGE || edge === Tileset.Map.TOP_EDGE) && !isNaN(x = Number(x)) && !isNaN(y = Number(y)))) {
          return;
        }
        if (!(this.collides & Character.collisions.CHARACTER && (Math.round(this.edges.left) < x && x < Math.round(this.edges.right)) && (Math.round(this.edges.top) < y && y < Math.round(this.edges.bottom)))) {
          switch (edge) {
            case Tileset.Map.LEFT_EDGE:
            case Tileset.Map.RIGHT_EDGE:
              return x;
            case Tileset.Map.TOP_EDGE:
            case Tileset.Map.BOTTOM_EDGE:
              return y;
          }
        }
        switch (edge) {
          case Tileset.Map.LEFT_EDGE:
            return Math.round(this.edges.left);
          case Tileset.Map.RIGHT_EDGE:
            return Math.round(this.edges.right);
          case Tileset.Map.TOP_EDGE:
            return Math.round(this.edges.top);
          case Tileset.Map.BOTTOM_EDGE:
            return Math.round(this.edges.bottom);
        }
      }
    },
    target: {
      value: function(cx, cy) {
        return this.targetBy(cx - this.x, cy - this.y);
      }
    },
    targetBy: {
      value: function(dx, dy) {
        var d, i, ix, iy, j, k, l, len, len1, len2, len3, m, map, o, ref, ref1, ref2, ref3, s, t;
        d = Math.sqrt(dx * dx + dy * dy);
        if (!(this.area instanceof Area && this.area.characters instanceof Collection && this.area.maps instanceof Object)) {
          return;
        }
        ix = this.x;
        iy = this.y;
        if (this.max_velocity !== Character.NO_MAX_VELOCITY && d > this.max_velocity) {
          dx = dx / d * this.max_velocity;
          dy = dy / d * this.max_velocity;
        }
        if (dx > 0) {
          s = this.edges.right + dx;
          if (this.collides & Character.collisions.MAP) {
            ref = this.area.maps;
            for (i = 0, len = ref.length; i < len; i++) {
              map = ref[i];
              k = Math.floor(this.height / (map.tile_height / 2)) + 2;
              j = -1;
              while (++j <= k) {
                t = map.getCollisionEdge(Tileset.Map.LEFT_EDGE, s, this.edges.top + j * this.height / k);
                if (s > t) {
                  s = t;
                }
              }
            }
          }
          if (this.collides & Character.collisions.CHARACTER) {
            this.area.characters.doForEach((function(_this) {
              return function(some) {
                var results;
                if (_this === some || !(some.collides & Character.collisions.CHARACTER)) {
                  return;
                }
                k = Math.floor(_this.height / some.height) + 2;
                j = -1;
                results = [];
                while (++j <= k) {
                  t = some.getCollisionEdge(Tileset.Map.LEFT_EDGE, s, _this.edges.top + j * _this.height / k);
                  if (s > t) {
                    results.push(s = t);
                  } else {
                    results.push(void 0);
                  }
                }
                return results;
              };
            })(this));
          }
          if (s > this.edges.right) {
            this.edges.right = s;
          }
        }
        if (dx < 0) {
          s = this.edges.left + dx;
          if (this.collides & Character.collisions.MAP) {
            ref1 = this.area.maps;
            for (l = 0, len1 = ref1.length; l < len1; l++) {
              map = ref1[l];
              k = Math.floor(this.height / (map.tile_height / 2)) + 2;
              j = -1;
              while (++j <= k) {
                t = map.getCollisionEdge(Tileset.Map.RIGHT_EDGE, s, this.edges.top + j * this.height / k);
                if (s < t) {
                  s = t;
                }
              }
            }
          }
          if (this.collides & Character.collisions.CHARACTER) {
            this.area.characters.doForEach((function(_this) {
              return function(some) {
                var results;
                if (_this === some || !(some.collides & Character.collisions.CHARACTER)) {
                  return;
                }
                k = Math.floor(_this.height / some.height) + 2;
                j = -1;
                results = [];
                while (++j <= k) {
                  t = some.getCollisionEdge(Tileset.Map.RIGHT_EDGE, s, _this.edges.top + j * _this.height / k);
                  if (s < t) {
                    results.push(s = t);
                  } else {
                    results.push(void 0);
                  }
                }
                return results;
              };
            })(this));
          }
          if (s < this.edges.left) {
            this.edges.left = s;
          }
        }
        if (dy > 0) {
          s = this.edges.bottom + dy;
          if (this.collides & Character.collisions.MAP) {
            ref2 = this.area.maps;
            for (m = 0, len2 = ref2.length; m < len2; m++) {
              map = ref2[m];
              k = Math.floor(this.width / (map.tile_width / 2)) + 2;
              j = -1;
              while (++j <= k) {
                t = map.getCollisionEdge(Tileset.Map.TOP_EDGE, this.edges.left + j * this.width / k, s);
                if (s > t) {
                  s = t;
                }
              }
            }
          }
          if (this.collides & Character.collisions.CHARACTER) {
            this.area.characters.doForEach((function(_this) {
              return function(some) {
                var results;
                if (_this === some || !(some.collides & Character.collisions.CHARACTER)) {
                  return;
                }
                k = Math.floor(_this.width / some.width) + 2;
                j = -1;
                results = [];
                while (++j <= k) {
                  t = some.getCollisionEdge(Tileset.Map.TOP_EDGE, _this.edges.left + j * _this.width / k, s);
                  if (s > t) {
                    results.push(s = t);
                  } else {
                    results.push(void 0);
                  }
                }
                return results;
              };
            })(this));
          }
          if (s > this.edges.bottom) {
            this.edges.bottom = s;
          }
        }
        if (dy < 0) {
          s = this.edges.top + dy;
          if (this.collides & Character.collisions.MAP) {
            ref3 = this.area.maps;
            for (o = 0, len3 = ref3.length; o < len3; o++) {
              map = ref3[o];
              k = Math.floor(this.width / (map.tile_width / 2)) + 2;
              j = -1;
              while (++j <= k) {
                t = map.getCollisionEdge(Tileset.Map.BOTTOM_EDGE, this.edges.left + j * this.width / k, s);
                if (s < t) {
                  s = t;
                }
              }
            }
          }
          if (this.collides & Character.collisions.CHARACTER) {
            this.area.characters.doForEach((function(_this) {
              return function(some) {
                var results;
                if (_this === some || !(some.collides & Character.collisions.CHARACTER)) {
                  return;
                }
                k = Math.floor(_this.width / some.width) + 2;
                j = -1;
                results = [];
                while (++j <= k) {
                  t = some.getCollisionEdge(Tileset.Map.BOTTOM_EDGE, _this.edges.left + j * _this.width / k, s);
                  if (s < t) {
                    results.push(s = t);
                  } else {
                    results.push(void 0);
                  }
                }
                return results;
              };
            })(this));
          }
          if (s < this.edges.top) {
            this.edges.top = s;
          }
        }
        dx = this.x - ix;
        dy = this.y - iy;
        this.direction = Math.atan2(dx, -dy);
        return this.velocity = Math.sqrt(dx * dx + dy * dy);
      }
    }
  });

  Object.freeze(Character.prototype);

  Character.collisions = {
    DOES_NOT_COLLIDE: 0x0,
    MAP: 0x1,
    CHARACTER: 0x2,
    ALL: 0x3
  };

  Object.freeze(Character.collisions);

  Object.defineProperty(Character, "NO_MAX_VELOCITY", {
    value: typeof Symbol !== "undefined" && Symbol !== null ? Symbol("Infinity") : Object.freeze(Object.create(null))
  });

  this.Character = Object.freeze(Character);

}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  TEXT
  The Jelli Game Engine
  ---------------------
   */
  var Text;

  Text = function(collection, name, screen, letters_name, text, isBase64) {
    var area, context, game, letters, ref;
    if (isBase64 == null) {
      isBase64 = false;
    }
    if (!(collection instanceof Collection)) {
      collection = null;
    }
    game = collection != null ? collection.game : null;
    area = collection != null ? collection.area : null;
    if (game != null) {
      context = (ref = game.screens[screen]) != null ? ref.context : void 0;
      letters = game.letters[letters_name];
      if (isBase64) {
        text = game.window.atob(text);
      }
    } else {
      context = letters = null;
    }
    Object.defineProperty(this, "block", {
      value: letters != null ? letters.createBlock.apply(letters, [context, 0, 0].concat(text.split(letters.source.getAttribute("data-linefeed")))) : void 0
    });
    Object.defineProperties(this, {
      advance: {
        value: this.block.advance.bind(this.block)
      },
      clear: {
        value: this.block.clear.bind(this.block)
      },
      fill: {
        value: this.block.fill.bind(this.block)
      },
      item: {
        value: this.block.item.bind(this.block)
      },
      line: {
        value: this.block.line.bind(this.block)
      }
    });
    Object.defineProperties(this, {
      delIndex: {
        get: function() {
          return this.block.delIndex;
        },
        set: function(n) {
          return this.block.delIndex = n;
        }
      },
      drawIndex: {
        get: function() {
          return this.block.drawIndex;
        },
        set: function(n) {
          return this.block.drawIndex = n;
        }
      },
      index: {
        get: function() {
          return this.block.index;
        },
        set: function(n) {
          return this.block.index = n;
        }
      },
      length: {
        value: this.block.length
      },
      x: {
        get: function() {
          return this.block.x;
        },
        set: function(n) {
          return this.block.x = n;
        }
      },
      y: {
        get: function() {
          return this.block.y;
        },
        set: function(n) {
          return this.block.y = n;
        }
      }
    });
    Object.defineProperties(this, {
      color: {
        value: null,
        writable: true
      },
      context: {
        value: context,
        enumerable: true
      },
      kill: {
        value: null,
        writable: true
      }
    });
    return Object.seal(this);
  };

  Text.prototype = Object.create(Object.prototype, {
    draw: {
      value: function() {
        var ref, ref1;
        if (!(((ref = this.block) != null ? ref.letters : void 0) instanceof Letters)) {
          return;
        }
        if (this.color) {
          this.block.letters.color = (ref1 = this.block.letters.source) != null ? ref1.getAttribute("data-palette-" + this.color) : void 0;
        } else {
          this.block.letters.clearColor();
        }
        return this.block.draw();
      }
    }
  });

  Object.freeze(Text.prototype);

  this.Text = Object.freeze(Text);

}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
  "use strict";

  /*
  VIEW
  The Jelli Game Engine
  ---------------------
   */
  var View;

  View = function(game, elt) {
    var first_screen, i, len, name, ref, ref1, screen;
    if (!(game instanceof Game)) {
      game = null;
    }
    if (!(elt instanceof Element)) {
      elt = (game != null ? game.getDocElement(elt) : void 0) || null;
    }
    if (isNaN(this.height = Number(elt != null ? elt.getAttribute("data-height") : void 0))) {
      this.height = 0;
    }
    this.screens = {};
    this.target = elt;
    if (isNaN(this.width = Number(elt != null ? elt.getAttribute("data-width") : void 0))) {
      this.width = 0;
    }
    if (elt != null) {
      elt.textContent = "";
    }
    elt.style.visibility = "hidden";
    first_screen = null;
    ref1 = (elt != null ? (ref = elt.getAttribute("data-screens")) != null ? ref.split(/\s+/) : void 0 : void 0) || [];
    for (i = 0, len = ref1.length; i < len; i++) {
      name = ref1[i];
      screen = game != null ? game.getDataElement("SCREEN", name) : void 0;
      if (screen) {
        this.screens[name] = new Screen(elt.appendChild(screen), "2d");
        if (first_screen == null) {
          first_screen = this.screens[name];
        }
      }
    }
    Object.freeze(this.screens);
    this.control = new Control((first_screen != null ? first_screen.canvas : elt), 0, 0, this.width, this.height);
    return Object.freeze(this);
  };

  View.prototype = Object.create(Object.prototype, {
    layout: {
      value: function() {
        var canvas, client_height, client_width, name, ref, scaled_height, scaled_width, screen;
        if (!(this.target instanceof Element)) {
          return;
        }
        if (this.target.ownerDocument.defaultView.getComputedStyle(this.target).position === "static") {
          this.target.style.position = "relative";
        }
        this.target.style.minWidth = this.width + "px";
        this.target.style.minHeight = this.height + "px";
        client_width = this.target.clientWidth;
        client_height = this.target.clientHeight;
        ref = this.screens;
        for (name in ref) {
          screen = ref[name];
          canvas = screen.canvas;
          if (canvas.width !== this.width) {
            canvas.width = this.width;
          }
          if (canvas.height !== this.height) {
            canvas.height = this.height;
          }
          if (client_width / client_height < this.width / this.height) {
            scaled_width = client_width < this.width ? client_width : this.width * Math.floor(client_width / this.width);
            scaled_height = Math.floor(this.height * scaled_width / this.width);
          } else {
            scaled_height = client_height < this.height ? client_height : this.height * Math.floor(client_height / this.height);
            scaled_width = Math.floor(this.width * scaled_height / this.height);
          }
          canvas.style.display = "block";
          canvas.style.position = "absolute";
          canvas.style.margin = "0";
          canvas.style.border = "none";
          canvas.style.padding = "0";
          canvas.style.width = scaled_width + "px";
          canvas.style.height = scaled_height + "px";
          canvas.style.top = "calc(50% - " + (scaled_height / 2) + "px)";
          canvas.style.left = "calc(50% - " + (scaled_width / 2) + "px)";
        }
        this.target.style.visibility = "";
      }
    }
  });

  Object.freeze(View.prototype);

  this.View = Object.freeze(View);

}).call(this);
