diff --git a/assets/selector.ase b/assets/selector.ase
new file mode 100644
index 0000000000000000000000000000000000000000..f09020287528617628db3fc02d1cb763b39d1a24
Binary files /dev/null and b/assets/selector.ase differ
diff --git a/assets/tilemap.ase b/assets/tilemap.ase
index c559a196e2b36890b647307e6d650a7a7e491979..e545cf9a93ce4fedf02adda9e6c9a90f6b484759 100644
Binary files a/assets/tilemap.ase and b/assets/tilemap.ase differ
diff --git a/dist/game.js b/dist/game.js
index 494c9c9c366570cf13418ad7e75126cff6914199..9805237bbdb06b174dc8633022e8c61d9d35c988 100644
--- a/dist/game.js
+++ b/dist/game.js
@@ -1,12 +1,15 @@
 (() => {
   // src/js/config.js
   var GAME_TITLE = "Untitled JS13K23 Game.";
-  var WIDTH = 160;
-  var HEIGHT = 144;
+  var WIDTH = 256;
+  var HEIGHT = 240;
+  var SCALE = 2 << 4;
 
   // src/js/utils.js
-  var pi = Math.PI;
-  var convertTileToScreen = (x, y) => ({x: x << 4, y: y << 4});
+  var params = new URLSearchParams(location.search);
+  var convertTileToScreen = (x, y) => ({x: x * SCALE, y: y * SCALE});
+  var getParameter = (key) => key ? params.get(key) : 0;
+  var hash = window.location.hash.split("?")[0].slice(1);
 
   // src/js/canvas.js
   var Canvas = class {
@@ -27,13 +30,12 @@
       this.ctx.fillRect(0, 0, this.width, this.height);
     }
     drawImage(image, x, y, width = image.width, height = image.height) {
-      console.debug("drawImage", image, x, y, width, height);
       this.ctx.drawImage(image, x - this.cX, y - this.cY, width, height);
     }
     sliceImage(img, x, y, w, h, cropX, cropY, cropW, cropH, direction = 0) {
       this.ctx.save();
       this.ctx.translate(x + w / 2 - this.cX, y + h / 2 - this.cY);
-      this.ctx.rotate(direction * pi / 180);
+      this.ctx.rotate(direction);
       this.ctx.drawImage(img, cropX, cropY, cropW, cropH, -w / 2, -h / 2, w, h);
       this.ctx.restore();
     }
@@ -61,20 +63,21 @@
       this.fontChars = "abcdefghijklmnopqrstuvwxyz1234567890.,!?:;)(~>";
       this.canvas = canvas2;
     }
-    drawLetter(letter, x, y, substituteOK = 0) {
+    drawLetter(letter, x, y, substituteOK = 1) {
+      let {canvas: canvas2, fontWidth, fontHeight} = this;
       let index = this.fontChars.indexOf(letter.toLowerCase());
       if (index == -1) {
         if (!substituteOK)
           return;
-        this.canvas.drawText(letter, x, y, "#ffffff", 7, "monospace");
+        canvas2.drawText(letter, x, y, "#ffffff", 7, "monospace");
       }
-      let sx = index * this.fontWidth;
+      let sx = index * fontWidth;
       let sy = 0;
       let yOffset = 0;
       if (letter == ",") {
         yOffset = -1;
       }
-      this.canvas.sliceImage(this.fontimg, x, y + yOffset, this.fontWidth, this.fontHeight, sx, sy, this.fontWidth, this.fontHeight);
+      canvas2.sliceImage(this.fontimg, x + canvas2.cX, y + yOffset + canvas2.cY, fontWidth, fontHeight, sx, sy, fontWidth, fontHeight);
     }
     render(text2, x, y) {
       let heightOffset = 0;
@@ -141,12 +144,12 @@
   var whichKeyDown = () => Object.keys(KEYS).filter((code) => _isKeyDown(code));
 
   // src/js/game.js
-  console.debug(convertTileToScreen(1, 1));
   var assets = {
     images: {
       splash: "../img/splash1.webp",
       font: "../img/hampsterfont.webp",
-      tiles: "../img/t.webp"
+      tiles: "../img/t.webp",
+      selector: "../img/selector.webp"
     },
     spritesheets: {
       player: [
@@ -155,18 +158,17 @@
         {x: 32},
         {x: 48}
       ]
-    },
-    tilesets: {
-      castle: [
-        {x: 0, y: 0},
-        {x: 16, y: 0}
-      ]
     }
   };
+  var tileTypes = {
+    1: 1,
+    2: 2
+  };
   var running = 1;
   var currentFrame = 0;
   var targetFrames = 60;
   var lastFrameTime = performance.now();
+  var debug = getParameter("debug") || 0;
   var rooms = [];
   var debugStatuses = [];
   var canvas = new Canvas("c", WIDTH, HEIGHT);
@@ -195,7 +197,7 @@
       if (rooms[i].name == name)
         return i;
     }
-    throw new Error("Room not found:" + name);
+    throw new Error("Room not found:" + name + ". Are you sure it's pushed?");
   };
   var changeRoom = (index) => {
     currentRoom = rooms[index];
@@ -220,68 +222,124 @@
       changeRoom(searchForRoom("menu"));
   };
   var menuRoom = new Room("menu");
-  var menuOptions = [
+  menuRoom.options = [
     {label: "Start Game", action: (_) => {
       changeRoom(searchForRoom("game"));
-    }},
-    {label: "Debug Room", action: (_) => changeRoom(searchForRoom("debug"))},
-    {label: "Reload", action: (_) => {
-      running = 0;
-      location.reload();
     }}
   ];
-  var menuIndex = 0;
+  menuRoom.index = 0;
   menuRoom.draw = () => {
     canvas.fill("black");
   };
   menuRoom.drawGUI = () => {
     text.render(GAME_TITLE, 8, 7 * 4);
-    for (let i = 0; i < menuOptions.length; i++) {
-      if (i == menuIndex) {
+    for (let i = 0; i < menuRoom.options.length; i++) {
+      if (i == menuRoom.index) {
         text.render(">", 8, 7 * (i + 5));
       }
-      text.render(menuOptions[i].label, 16, 7 * (i + 5));
+      text.render(menuRoom.options[i].label, 16, 7 * (i + 5));
     }
   };
   menuRoom.keyDown = (key) => {
     if (pressedLastFrame.includes(key))
       return;
-    switch (key) {
-      case "ArrowUp":
-        menuIndex--;
-        break;
-      case "ArrowDown":
-        menuIndex++;
-        break;
-      case "Enter":
-        menuOptions[menuIndex].action();
-        break;
+    const keyActions = {
+      ArrowUp: () => menuRoom.index--,
+      ArrowDown: () => menuRoom.index++,
+      Enter: () => menuRoom.options[menuRoom.index].action()
+    };
+    const action = keyActions[key];
+    if (action)
+      action();
+    if (menuRoom.index >= menuRoom.options.length)
+      menuRoom.index = 0;
+    if (menuRoom.index < 0)
+      menuRoom.index = menuRoom.options.length - 1;
+  };
+  var getTileType = (x, y, data) => {
+    for (let i = 0; i < data.tiles.length; i++) {
+      let tile = data.tiles[i];
+      if (tile.x == x && tile.y == y)
+        return tileTypes[tile.id];
     }
-    if (menuIndex >= menuOptions.length)
-      menuIndex = 0;
-    if (menuIndex < 0)
-      menuIndex = menuOptions.length - 1;
+    return 0;
   };
-  var currentLevelData = {
-    tiles: [
-      {id: 1, x: 1, y: 1},
-      {id: 123, x: 2, y: 2}
-    ]
+  var renderTiles = (data) => {
+    for (let i = 0; i < data.tiles.length; i++) {
+      let tile = data.tiles[i];
+      let tileLocation = convertTileToScreen(tile.x, tile.y);
+      let tId = tile.id;
+      if (tile.id == 2) {
+        tId = 3;
+        getTileType(tile.x, tile.y - 1, data) == 2 ? tId += 1 : tId += 0;
+        getTileType(tile.x, tile.y + 1, data) == 2 ? tId += 2 : tId += 0;
+        getTileType(tile.x - 1, tile.y, data) == 2 ? tId += 4 : tId += 0;
+        getTileType(tile.x + 1, tile.y, data) == 2 ? tId += 8 : tId += 0;
+      }
+      canvas.sliceImage(assets.images.tiles, tileLocation.x, tileLocation.y, SCALE, SCALE, tId * 16, 0, 16, 16);
+    }
   };
   var gameRoom = new Room("game");
+  gameRoom.data = {tiles: []};
   gameRoom.draw = () => {
     canvas.fill("black");
-    for (let i = 0; i < currentLevelData.tiles.length; i++) {
-      let tile = currentLevelData.tiles[i];
-      if (tile.id > currentLevelData.length)
-        tile.id = 0;
-      let tileLocation = convertTileToScreen(tile.x, tile.y);
-      canvas.sliceImage(assets.images.tiles, tileLocation.x, tileLocation.y, 16, 16, tile.id * 16, 0, 16, 16);
-    }
+    renderTiles(gameRoom.data);
+  };
+  var levelEditor = new Room("editor");
+  levelEditor.currentTile = {x: 0, y: 0, id: 0};
+  levelEditor.data = {tiles: []};
+  levelEditor.step = (_) => {
+    let tileLocation = convertTileToScreen(levelEditor.currentTile.x, levelEditor.currentTile.y);
+    if (tileLocation.x < canvas.cX)
+      canvas.cX = tileLocation.x;
+    if (tileLocation.x >= canvas.cX + 256)
+      canvas.cX = tileLocation.x - canvas.width;
+    if (tileLocation.y < canvas.cY)
+      canvas.cY = tileLocation.y;
+    if (tileLocation.y > canvas.cY + 224)
+      canvas.cY = tileLocation.y - canvas.height;
+    debugStatuses.push("Current tile:" + levelEditor.currentTile.x + "," + levelEditor.currentTile.y);
+    debugStatuses.push("Current tile ID:" + levelEditor.currentTile.id);
+    debugStatuses.push("Camera:" + canvas.cX + "," + canvas.cY);
+  };
+  levelEditor.keyDown = (key) => {
+    if (pressedLastFrame.includes(key))
+      return;
+    const {currentTile, data} = levelEditor;
+    const {x, y, id} = currentTile;
+    const keyActions = {
+      ArrowUp: () => currentTile.y--,
+      ArrowDown: () => currentTile.y++,
+      ArrowLeft: () => currentTile.x--,
+      ArrowRight: () => currentTile.x++,
+      PageUp: () => currentTile.id++,
+      PageDown: () => currentTile.id--,
+      KeyP: () => console.log(data),
+      Enter: () => {
+        data.tiles = data.tiles.filter((tile) => tile.x !== x || tile.y !== y);
+        data.tiles.push({id, x, y});
+      }
+    };
+    const action = keyActions[key];
+    if (action)
+      action();
+  };
+  levelEditor.draw = () => {
+    canvas.fill("#010101");
+    renderTiles(levelEditor.data);
+    canvas.drawLine(-canvas.width * 100, 0, canvas.width * 100, 0, "white");
+    canvas.drawLine(0, -canvas.height * 100, 0, canvas.height * 100, "white");
+    text.render("(0,0)", 1 - canvas.cX, 1 - canvas.cY);
+    let tileLocation = convertTileToScreen(levelEditor.currentTile.x, levelEditor.currentTile.y);
+    canvas.ctx.globalAlpha = 0.5;
+    canvas.sliceImage(assets.images.tiles, tileLocation.x, tileLocation.y, SCALE, SCALE, levelEditor.currentTile.id * 16, 0, 16, 16);
+    canvas.ctx.globalAlpha = 1;
+    canvas.drawImage(assets.images.selector, tileLocation.x, tileLocation.y, SCALE, SCALE);
   };
   rooms.push(loadingRoom);
   rooms.push(menuRoom);
   rooms.push(gameRoom);
+  rooms.push(levelEditor);
   rooms.push(debugRoom);
   currentRoom = rooms[roomIndex];
   var main = () => {
@@ -294,20 +352,24 @@
       return;
     currentFrame++;
     debugStatuses = [];
+    currentRoom.step();
     currentRoom.draw();
     currentRoom.drawGUI();
     let currentKeys = whichKeyDown();
     for (let i = 0; i < currentKeys.length; i++) {
-      debugStatuses.push(currentKeys[i]);
+      if (debug)
+        debugStatuses.push(currentKeys[i]);
       currentRoom.keyDown(currentKeys[i]);
     }
     pressedLastFrame = currentKeys;
-    text.render("FPS:" + Math.round(1e3 / delta), 0, 0);
-    text.render(currentRoom.name, canvas.width - 8 * currentRoom.name.length, 0);
-    if (currentFrame <= 60 * 5) {
-      debugStatuses.push("Debug mode.");
-      debugStatuses.push("Dimensions:" + canvas.width + "x" + canvas.height);
-      debugStatuses.push("Have fun!");
+    if (debug) {
+      text.render("FPS:" + Math.round(1e3 / delta), 0, 0);
+      text.render(currentRoom.name, canvas.width - 8 * currentRoom.name.length, 0);
+      debugStatuses.push("Debug mode");
+      if (currentFrame <= 60 * 5) {
+        debugStatuses.push("Dimensions:" + canvas.width + "x" + canvas.height);
+        debugStatuses.push("Have fun!");
+      }
     }
     for (let i = 0; i < debugStatuses.length; i++) {
       text.render(debugStatuses[i], 0, canvas.height - 7 * (debugStatuses.length - i));
@@ -324,13 +386,9 @@
         assets.images[image] = img;
       };
     }
-    console.log(assets.images);
     console.log("Images loaded.");
     currentRoom.updateStatus("Loading complete!");
-    canvas.fill("#222034");
-    canvas.drawImage(splash, canvas.width / 2 - splash.width / 2, canvas.height / 2 - splash.height / 2);
     setTimeout(() => {
-      currentRoom = rooms[1];
       main();
     }, 1e3);
   };
diff --git a/dist/game.js.map b/dist/game.js.map
index 831429a02de40f78951b4c5bc29b243057397ba7..7dcd7b8b3b4d1091d48137cd80630cb208003762 100644
--- a/dist/game.js.map
+++ b/dist/game.js.map
@@ -1,7 +1,7 @@
 {
   "version": 3,
   "sources": ["../src/js/config.js", "../src/js/utils.js", "../src/js/canvas.js", "../src/js/text.js", "../src/js/objects.js", "../src/js/keyboard.js", "../src/js/game.js"],
-  "sourcesContent": ["// Holds all the config for your game.\r\n\r\nconst GAME_TITLE = \"Untitled JS13K23 Game.\"\r\n\r\nconst WIDTH = 160; // pixels\r\nconst HEIGHT = 144; // pixels\r\n\r\nexport { GAME_TITLE, WIDTH, HEIGHT };\r\n", "// random shit\r\n\r\nexport const pi = Math.PI;\r\n\r\nexport const convertTileToScreen = (x, y) => ({x: x<<4, y: y<<4});\r\n", "\r\n// Holds canvas, context and adds endpoints for graphics\r\n\r\nimport { pi } from \"./utils.js\"\r\nclass Canvas {\r\n    constructor(id=\"c\", w=128, h=128) {\r\n        this.canvas = document.getElementById(id);\r\n        this.canvas.width = w;\r\n        this.canvas.height = h;\r\n\r\n        this.ctx = this.canvas.getContext(\"2d\");\r\n        this.ctx.imageSmoothingEnabled = false;\r\n        this.ctx.textBaseline = \"top\";\r\n\r\n        this.width = this.canvas.width;\r\n        this.height = this.canvas.height;\r\n\r\n        // camera\r\n        this.cX = 0;\r\n        this.cY = 0;\r\n    }\r\n\r\n    fill(c=\"black\") {\r\n        this.ctx.fillStyle = c;\r\n        this.ctx.fillRect(0, 0, this.width, this.height);\r\n    }\r\n    \r\n    drawImage(image, x, y, width = image.width, height = image.height) {\r\n        console.debug(\"drawImage\", image, x, y, width, height);\r\n        this.ctx.drawImage(image, x-this.cX, y-this.cY, width, height);\r\n    }\r\n\r\n    sliceImage(img, x, y, w, h, cropX, cropY, cropW, cropH, direction=0) {\r\n        // console.debug(\"sliceImage\", img, x, y, w, h, cropX, cropY, cropW, cropH, direction);\r\n        this.ctx.save();\r\n        this.ctx.translate((x+w/2)-this.cX, (y+h/2)-this.cY);\r\n        this.ctx.rotate(direction * pi/180);\r\n        this.ctx.drawImage(img, cropX, cropY, cropW, cropH, -w/2, -h/2, w, h);\r\n        this.ctx.restore();\r\n        // console.log(`${x}, ${y}, ${w}, ${h}, ${cropX}, ${cropY}, ${cropW}, ${cropH}`);\r\n    }\r\n\r\n    drawText(text, x, y, c=\"white\", size=16, font=\"monospace\") {\r\n        this.ctx.fillStyle = c;\r\n        this.ctx.font = `${size}px ${font}`;\r\n        this.ctx.fillText(text, x, y);\r\n    }\r\n\r\n    drawLine(x1, y1, x2, y2, c=\"white\", w=1) {\r\n        this.ctx.strokeStyle = c;\r\n        this.ctx.lineWidth = w;\r\n        this.ctx.beginPath();\r\n        this.ctx.moveTo(x1-this.cX, y1-this.cY);\r\n        this.ctx.lineTo(x2-this.cX, y2-this.cY);\r\n        this.ctx.stroke();\r\n    }\r\n}\r\n\r\nexport { Canvas };\r\n", "// draws text to the screen by splicing a font sheet.\r\n\r\nclass TextRenderer {\r\n    constructor(canvas, fontimg) {\r\n        this.fontimg = fontimg; // MUST BE AN IMAGE OBJECT\r\n        this.fontWidth = 7;\r\n        this.fontHeight = 7;\r\n        this.fontChars = \"abcdefghijklmnopqrstuvwxyz1234567890.,!?:;)(~>\";\r\n        this.canvas = canvas;\r\n    }\r\n\r\n    drawLetter(letter, x, y, substituteOK=0) {\r\n        let index = this.fontChars.indexOf(letter.toLowerCase());\r\n        if (index == -1) {\r\n            if (!substituteOK) return;\r\n            this.canvas.drawText(letter, x, y, \"#ffffff\", 7, \"monospace\");\r\n        }\r\n        let sx = index * this.fontWidth;\r\n        let sy = 0;\r\n        // draw image to context\r\n        let yOffset = 0;\r\n        // if the letter is \",\", offset it by -1\r\n        if (letter == \",\") {\r\n            yOffset = -1;\r\n        }\r\n        this.canvas.sliceImage(this.fontimg, x, y + yOffset, this.fontWidth, this.fontHeight, sx, sy, this.fontWidth, this.fontHeight);\r\n    }\r\n\r\n    render(text, x, y) {\r\n        let heightOffset = 0;\r\n        let xOffset = 0;\r\n        for (let i = 0; i < text.length; i++) {\r\n            if (text[i] == \"\\n\") {\r\n                heightOffset++;\r\n                xOffset = 0;\r\n                continue;\r\n            }\r\n            this.drawLetter(text[i], x + (xOffset * this.fontWidth), y + (heightOffset * this.fontHeight));\r\n            xOffset++;\r\n        }\r\n    }\r\n\r\n    throwPanic = (err) => {\r\n        // This function is called when an error is caught but unhandled.\r\n        // It'll show the error on-screen.\r\n    \r\n        this.canvas.fill(\"#00000080\") // 50% black\r\n        \r\n        this.render(err, 0, 0);\r\n        throw err;\r\n    }\r\n}\r\n\r\nexport { TextRenderer };", "class Object {\r\n    draw() {}\r\n    step() {}\r\n}\r\n\r\n\r\nclass Room extends Object {\r\n    constructor(name=\"\") {\r\n        super();\r\n        this.objects = [];\r\n        this.name = name; // needs to be unique, otherwise the searching code will just use the first one it finds.\r\n    }\r\n\r\n    draw() {\r\n        for (let i = 0; i < this.objects.length; i++) {\r\n            this.objects[i].draw();\r\n        }\r\n    }\r\n\r\n    drawGUI() {\r\n\r\n    }\r\n\r\n    keyDown(key) {\r\n    }\r\n\r\n    keyUp(key) {\r\n    }\r\n\r\n\r\n\r\n    step() {\r\n        for (let i = 0; i < this.objects.length; i++) {\r\n            this.objects[i].step();\r\n        }\r\n    }\r\n}\r\n\r\n\r\n\r\nexport { Object, Room };", "// Manages keyboard inputs. Code may have been stolen from herebefrogs/gamejam-boilerplate, under MIT license.\r\n\r\n/** Keyboard input\r\n * Record time at which each key gets pressed\r\n * and provide utilities to queries which keys are pressed or released\r\n *\r\n * Note: importing any public function of this module\r\n * will install the keyboard event listeners\r\n */\r\n\r\n/* private */\r\n\r\n// time at which each key was pressed\r\n// key = KeyboardEvent.code\r\n// value = time in ms at which keyboard event was first emitted (repeats are filtered out)\r\nconst KEYS = {};\r\n\r\nconst _isKeyDown = code => KEYS[code] || 0;\r\n\r\nconst _releaseKey = code => delete KEYS[code];\r\n\r\naddEventListener('keydown', e => {\r\n  // prevent itch.io from scrolling the page up/down\r\n  e.preventDefault();\r\n\r\n  if (!e.repeat) {\r\n    KEYS[e.code] = performance.now();\r\n  }\r\n});\r\n\r\naddEventListener('keyup', e => _releaseKey(e.code));\r\n\r\n\r\n\r\n\r\n/* public API */\r\n\r\n// returns the most recent key pressed amongst the array passed as argument (or 0 if none were)\r\nexport const isKeyDown = (...codes) => Math.max(...codes.map(code => _isKeyDown(code)))\r\n\r\n// retuns the list of keys currently pressed\r\nexport const whichKeyDown = () => Object.keys(KEYS).filter(code => _isKeyDown(code));\r\n\r\n// returns if any key is currently pressed\r\nexport const anyKeyDown = () => whichKeyDown().length;\r\n\r\n// return true if a key can be released (must be currently pressed) or false if it can't\r\n// note: this \"consumes\" the key pressed by releasing it (only if it was pressed)\r\nexport const isKeyUp = code => _isKeyDown(code) ? _releaseKey(code) : false;", "\r\nimport { WIDTH, HEIGHT, GAME_TITLE } from \"./config.js\";\r\nimport { Canvas } from \"./canvas.js\";\r\nimport { TextRenderer } from \"./text.js\";\r\nimport { Room, Object } from \"./objects.js\";\r\nimport { whichKeyDown } from \"./keyboard.js\";\r\nimport { convertTileToScreen } from \"./utils.js\";\r\n\r\nconsole.debug(convertTileToScreen(1, 1));\r\n\r\nlet assets = {\r\n    images: {\r\n        splash: \"../img/splash1.webp\",\r\n        font: \"../img/hampsterfont.webp\",\r\n        tiles: \"../img/t.webp\"\r\n    },\r\n    spritesheets: {\r\n        player: [\r\n            {x: 0}, // looking up\r\n            {x: 16}, // looking right\r\n            {x: 32}, // looking down\r\n            {x: 48} // looking left\r\n        ]\r\n    },\r\n    tilesets: {\r\n        castle: [\r\n            {x: 0, y: 0}, // ???\r\n            {x: 16, y: 0}, // floor\r\n        ]\r\n    }\r\n\r\n}\r\n\r\nlet running = 1;\r\n\r\nlet currentFrame = 0;\r\nlet targetFrames = 60;\r\n\r\nlet lastFrameTime = performance.now();\r\n\r\nlet rooms = [];\r\nlet debugStatuses = [];\r\nlet canvas = new Canvas(\"c\", WIDTH, HEIGHT);\r\nlet text;\r\n\r\nlet pressedLastFrame = [];\r\ncanvas.fill(\"#222034\");\r\n\r\nlet splash = new Image();\r\nsplash.src = assets.images.splash;\r\nsplash.onload = () => {\r\n    canvas.drawImage(splash, canvas.width / 2 - splash.width / 2, canvas.height / 2 - splash.height / 2);\r\n    let font = new Image();\r\n    font.src = assets.images.font;\r\n    font.onload = () => {\r\n        console.log(\"font loaded\")\r\n        text = new TextRenderer(canvas, font);\r\n        window.onerror = (e) => {\r\n            running = 0;\r\n            text.throwPanic(e);\r\n        }\r\n    }\r\n}\r\n\r\n// Entity class is here becuase otherwise every entity would need the canvas passed into it\r\nclass Entity extends Object {\r\n    constructor(x=0, y=0, spritesheet=null, sprite=null) {\r\n        super();\r\n        this.x = x;\r\n        this.y = y;\r\n        this.sprite = sprite;\r\n        this.spritesheet = spritesheet;\r\n    }\r\n\r\n    draw() {\r\n        canvas.drawImage(this.sprite, this.x, this.y);\r\n    }\r\n}\r\n\r\n// Create all the game rooms\r\nlet roomIndex = 0;\r\nlet currentRoom = rooms[roomIndex];\r\n\r\nlet searchForRoom = (name) => {\r\n    // returns the room's index in the rooms array\r\n    for (let i = 0; i < rooms.length; i++) {\r\n        if (rooms[i].name == name) return i;\r\n    } throw new Error(\"Room not found:\"+name);\r\n}\r\n\r\nconst changeRoom = (index) => {\r\n    currentRoom = rooms[index];\r\n    roomIndex = index;\r\n}\r\n\r\nconst loadingRoom = new Room(\"loading\");\r\nloadingRoom.updateStatus = (status) => {\r\n    console.log(status);\r\n    canvas.fill(\"#222034\");\r\n    canvas.drawImage(splash, canvas.width / 2 - splash.width / 2, canvas.height / 2 - splash.height / 2);\r\n    text.render(status, 0, 0);\r\n}\r\n\r\nconst debugRoom = new Room(\"debug\");\r\ndebugRoom.draw = () => {\r\n    canvas.fill(\"black\");\r\n}\r\ndebugRoom.drawGUI = () => {\r\n    debugStatuses.push(\"Current Frame:\"+currentFrame+`(~${Math.round((currentFrame/targetFrames)*100)/100} sec)`);\r\n}\r\ndebugRoom.keyDown = (key) => {\r\n    if (key == \"Escape\") changeRoom(searchForRoom(\"menu\"));\r\n}\r\n\r\nconst menuRoom = new Room(\"menu\");\r\nlet menuOptions = [\r\n    {\"label\": \"Start Game\", \"action\": _ => {changeRoom(searchForRoom(\"game\"))}},\r\n    {\"label\": \"Debug Room\", \"action\": _ => changeRoom(searchForRoom(\"debug\"))},\r\n    {\"label\": \"Reload\", \"action\": _ => {running = 0; location.reload();}}\r\n];\r\nlet menuIndex = 0;\r\n\r\nmenuRoom.draw = () => {\r\n    canvas.fill(\"black\");\r\n}\r\nmenuRoom.drawGUI = () => {\r\n    text.render(GAME_TITLE, 8, 7*4);\r\n    for (let i = 0; i < menuOptions.length; i++) {\r\n        if (i == menuIndex) {\r\n            text.render(\">\", 8, 7*(i+5));\r\n        }\r\n        text.render(menuOptions[i].label, 16, 7*(i+5));\r\n    }\r\n}\r\nmenuRoom.keyDown = (key) => {\r\n    if (pressedLastFrame.includes(key)) return;\r\n    switch (key) {\r\n        case \"ArrowUp\":\r\n            menuIndex--;\r\n            break;\r\n        case \"ArrowDown\":\r\n            menuIndex++;\r\n            break;\r\n        case \"Enter\":\r\n            menuOptions[menuIndex].action();\r\n            break;\r\n    }\r\n    if (menuIndex >= menuOptions.length) menuIndex = 0;\r\n    if (menuIndex < 0) menuIndex = menuOptions.length-1;\r\n}\r\n\r\nlet currentLevelData = {\r\n    tiles: [\r\n        {id: 1, x: 1, y: 1}, // floor at tile coords 1, 1\r\n        {id:123, x: 2, y: 2},\r\n    ]\r\n}\r\n\r\nconst gameRoom = new Room(\"game\"); \r\ngameRoom.draw = () => {\r\n    canvas.fill(\"black\");\r\n    for (let i = 0; i < currentLevelData.tiles.length; i++) {\r\n        let tile = currentLevelData.tiles[i];\r\n        if (tile.id > currentLevelData.length) tile.id = 0;\r\n        let tileLocation = convertTileToScreen(tile.x, tile.y);\r\n        canvas.sliceImage(assets.images.tiles, tileLocation.x, tileLocation.y, 16, 16, tile.id*16, 0, 16, 16);\r\n    }\r\n\r\n    \r\n}\r\n\r\nrooms.push(loadingRoom);\r\nrooms.push(menuRoom);\r\nrooms.push(gameRoom);\r\nrooms.push(debugRoom);\r\n\r\ncurrentRoom = rooms[roomIndex];\r\n\r\nlet main = () => { // main game loop\r\n    if (!running) return;\r\n    requestAnimationFrame(main);\r\n    let now = performance.now();\r\n    let delta = now - lastFrameTime;\r\n\r\n    if (delta < 1000 / targetFrames) return;\r\n\r\n    currentFrame++;\r\n    debugStatuses = [];\r\n\r\n    currentRoom.draw();\r\n    currentRoom.drawGUI();\r\n\r\n    let currentKeys = whichKeyDown();\r\n    for (let i = 0; i < currentKeys.length; i++) {\r\n        debugStatuses.push(currentKeys[i]);\r\n        currentRoom.keyDown(currentKeys[i]);\r\n    }\r\n    \r\n    pressedLastFrame = currentKeys;    \r\n    \r\n    text.render(\"FPS:\" + Math.round(1000 / delta), 0, 0);\r\n    text.render(currentRoom.name, canvas.width-8*(currentRoom.name.length), 0);\r\n\r\n    if (currentFrame <= 60*5) {\r\n        debugStatuses.push(\"Debug mode.\");\r\n        debugStatuses.push(\"Dimensions:\"+canvas.width+\"x\"+canvas.height);\r\n        debugStatuses.push(\"Have fun!\");\r\n    }\r\n\r\n    for (let i = 0; i < debugStatuses.length; i++) {\r\n        text.render(debugStatuses[i], 0, canvas.height-7*(debugStatuses.length-i));\r\n    }\r\n\r\n    lastFrameTime = now;\r\n    }\r\n\r\nlet init = () => {\r\n    // begin loading all the assets.\r\n    currentRoom.updateStatus(\"Loading images...\");\r\n    for (let image in assets.images) {\r\n        currentRoom.updateStatus(\"Loading image \" + image);\r\n        let img = new Image();\r\n        img.src = assets.images[image];\r\n        img.onload = () => {\r\n            assets.images[image] = img;\r\n        }\r\n    }\r\n    console.log(assets.images);\r\n    console.log(\"Images loaded.\")\r\n    currentRoom.updateStatus(\"Loading complete!\");\r\n\r\n    canvas.fill(\"#222034\");\r\n    canvas.drawImage(splash, canvas.width / 2 - splash.width / 2, canvas.height / 2 - splash.height / 2);\r\n    setTimeout(() => {\r\n        currentRoom = rooms[1];\r\n        main();\r\n    }, 1000);\r\n}\r\n\r\nwindow.onload = () => {\r\n    document.title = GAME_TITLE;\r\n    init();\r\n}\r\n"],
-  "mappings": "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",
+  "sourcesContent": ["// Holds all the config for your game.\r\n\r\nexport const GAME_TITLE = \"Untitled JS13K23 Game.\"\r\n\r\nexport const WIDTH = 256; // pixels\r\nexport const HEIGHT = 240; // pixels\r\n\r\nexport const SCALE = 2<<4;\r\n\r\n", "// random shit\r\n\r\nimport { SCALE } from \"./config\";\r\n\r\nconst params = new URLSearchParams(location.search);\r\n\r\nexport const pi = Math.PI;\r\nexport const convertTileToScreen = (x, y) => ({x: x*SCALE, y: y*SCALE});\r\nexport const getParameter = key => key ? params.get(key) : 0;\r\nexport const hash = window.location.hash.split(\"?\")[0].slice(1);\r\n\r\n// The following code is responsible for building levels.\r\nconst lineToCoords=(x0,y0,x1,y1,id=2)=>{const c=[];let dx=Math.abs(x1-x0),dy=Math.abs(y1-y0),sx=(x0<x1)?1:-1,sy=(y0<y1)?1:-1,err=dx-dy;while(true){c.push({x:x0,y:y0,id:id});if(x0===x1&&y0===y1)break;const e2=2*err;if(e2>-dy){err-=dy;x0+=sx}if(e2<dx){err+=dx;y0+=sy}}return c};\r\n\r\nconst generateBox = (x, y, width, height, fillTileId=1) => {\r\n    const box = [];\r\n    \r\n    for (let y = y; y < height; y++) {\r\n        for (let x = x; x < width; x++) {\r\n            box.push({ x, y, id: fillTileId });\r\n        }\r\n    }\r\n    \r\n    return box;\r\n}\r\n\r\nexport const generateRoom = (x,y,width,height,lineId=2,fillId=1) => {\r\n    let top = lineToCoords(x, y, x+width, y, lineId);\r\n    let bottom = lineToCoords(x, y+height, x+width, y+height, lineId);\r\n    let left = lineToCoords(x, y, x, y+height, lineId);\r\n    let right = lineToCoords(x+width, y, x+width, y+height, lineId);\r\n\r\n    let fill = generateBox(x+1, y+1, x+width-1, y+height-1, fillId);\r\n\r\n    return [...top, ...bottom, ...left, ...right, ...fill];\r\n}\r\n", "\r\n// Holds canvas, context and adds endpoints for graphics\r\n\r\nimport { pi } from \"./utils.js\"\r\nclass Canvas {\r\n    constructor(id=\"c\", w=128, h=128) {\r\n        this.canvas = document.getElementById(id);\r\n        this.canvas.width = w;\r\n        this.canvas.height = h;\r\n\r\n        this.ctx = this.canvas.getContext(\"2d\");\r\n        this.ctx.imageSmoothingEnabled = false;\r\n        this.ctx.textBaseline = \"top\";\r\n\r\n        this.width = this.canvas.width;\r\n        this.height = this.canvas.height;\r\n\r\n        // camera\r\n        this.cX = 0;\r\n        this.cY = 0;\r\n    }\r\n\r\n    fill(c=\"black\") {\r\n        this.ctx.fillStyle = c;\r\n        this.ctx.fillRect(0, 0, this.width, this.height);\r\n    }\r\n    \r\n    drawImage(image, x, y, width = image.width, height = image.height) {\r\n        this.ctx.drawImage(image, x-this.cX, y-this.cY, width, height);\r\n    }\r\n\r\n    sliceImage(img, x, y, w, h, cropX, cropY, cropW, cropH, direction=0) {\r\n        // console.debug(\"sliceImage\", img, x, y, w, h, cropX, cropY, cropW, cropH, direction);\r\n        this.ctx.save();\r\n        this.ctx.translate((x+w/2)-this.cX, (y+h/2)-this.cY);\r\n        this.ctx.rotate(direction);\r\n        this.ctx.drawImage(img, cropX, cropY, cropW, cropH, -w/2, -h/2, w, h);\r\n        this.ctx.restore();\r\n        // console.log(`${x}, ${y}, ${w}, ${h}, ${cropX}, ${cropY}, ${cropW}, ${cropH}`);\r\n    }\r\n\r\n    drawText(text, x, y, c=\"white\", size=16, font=\"monospace\") {\r\n        this.ctx.fillStyle = c;\r\n        this.ctx.font = `${size}px ${font}`;\r\n        this.ctx.fillText(text, x, y);\r\n    }\r\n\r\n    drawLine(x1, y1, x2, y2, c=\"white\", w=1) {\r\n        this.ctx.strokeStyle = c;\r\n        this.ctx.lineWidth = w;\r\n        this.ctx.beginPath();\r\n        this.ctx.moveTo(x1-this.cX, y1-this.cY);\r\n        this.ctx.lineTo(x2-this.cX, y2-this.cY);\r\n        this.ctx.stroke();\r\n    }\r\n}\r\n\r\nexport { Canvas };\r\n", "// draws text to the screen by splicing a font sheet.\r\n\r\nclass TextRenderer {\r\n    constructor(canvas, fontimg) {\r\n        this.fontimg = fontimg; // MUST BE AN IMAGE OBJECT\r\n        this.fontWidth = 7;\r\n        this.fontHeight = 7;\r\n        this.fontChars = \"abcdefghijklmnopqrstuvwxyz1234567890.,!?:;)(~>\";\r\n        this.canvas = canvas;\r\n    }\r\n\r\n    drawLetter(letter, x, y, substituteOK=1) {\r\n        let { canvas, fontWidth, fontHeight } = this;\r\n\r\n        let index = this.fontChars.indexOf(letter.toLowerCase());\r\n        if (index == -1) {\r\n            if (!substituteOK) return;\r\n            canvas.drawText(letter, x, y, \"#ffffff\", 7, \"monospace\");\r\n        }\r\n        let sx = index * fontWidth;\r\n        let sy = 0;\r\n        // draw image to context\r\n        let yOffset = 0;\r\n        // if the letter is \",\", offset it by -1\r\n        if (letter == \",\") {\r\n            yOffset = -1;\r\n        }\r\n        canvas.sliceImage(this.fontimg, x+canvas.cX, y + yOffset + canvas.cY, fontWidth, fontHeight, sx, sy, fontWidth, fontHeight); \r\n            // canvas.cX and canvas.cY are the camera offsets. we dont want to have text flying off the screen.\r\n            // you can counteract this by specifying x-cX and x-cY when calling this.\r\n    }\r\n\r\n    render(text, x, y) {\r\n        let heightOffset = 0;\r\n        let xOffset = 0;\r\n        for (let i = 0; i < text.length; i++) {\r\n            if (text[i] == \"\\n\") {\r\n                heightOffset++;\r\n                xOffset = 0;\r\n                continue;\r\n            }\r\n            this.drawLetter(text[i], x + (xOffset * this.fontWidth), y + (heightOffset * this.fontHeight));\r\n            xOffset++;\r\n        }\r\n    }\r\n\r\n    throwPanic = (err) => {\r\n        // This function is called when an error is caught but unhandled.\r\n        // It'll show the error on-screen.\r\n    \r\n        this.canvas.fill(\"#00000080\") // 50% black\r\n        \r\n        this.render(err, 0, 0);\r\n        throw err;\r\n    }\r\n}\r\n\r\nexport { TextRenderer };", "class Object {\r\n    draw() {}\r\n    step() {}\r\n}\r\n\r\n\r\nclass Room extends Object {\r\n    constructor(name=\"\") {\r\n        super();\r\n        this.objects = [];\r\n        this.name = name; // needs to be unique, otherwise the searching code will just use the first one it finds.\r\n    }\r\n\r\n    draw() {\r\n        for (let i = 0; i < this.objects.length; i++) {\r\n            this.objects[i].draw();\r\n        }\r\n    }\r\n\r\n    drawGUI() {\r\n\r\n    }\r\n\r\n    keyDown(key) {\r\n    }\r\n\r\n    keyUp(key) {\r\n    }\r\n\r\n\r\n\r\n    step() {\r\n        for (let i = 0; i < this.objects.length; i++) {\r\n            this.objects[i].step();\r\n        }\r\n    }\r\n}\r\n\r\n\r\n\r\nexport { Object, Room };", "// Manages keyboard inputs. Code may have been stolen from herebefrogs/gamejam-boilerplate, under MIT license.\r\n\r\n/** Keyboard input\r\n * Record time at which each key gets pressed\r\n * and provide utilities to queries which keys are pressed or released\r\n *\r\n * Note: importing any public function of this module\r\n * will install the keyboard event listeners\r\n */\r\n\r\n/* private */\r\n\r\n// time at which each key was pressed\r\n// key = KeyboardEvent.code\r\n// value = time in ms at which keyboard event was first emitted (repeats are filtered out)\r\nconst KEYS = {};\r\n\r\nconst _isKeyDown = code => KEYS[code] || 0;\r\n\r\nconst _releaseKey = code => delete KEYS[code];\r\n\r\naddEventListener('keydown', e => {\r\n  // prevent itch.io from scrolling the page up/down\r\n  e.preventDefault();\r\n\r\n  if (!e.repeat) {\r\n    KEYS[e.code] = performance.now();\r\n  }\r\n});\r\n\r\naddEventListener('keyup', e => _releaseKey(e.code));\r\n\r\n\r\n\r\n\r\n/* public API */\r\n\r\n// returns the most recent key pressed amongst the array passed as argument (or 0 if none were)\r\nexport const isKeyDown = (...codes) => Math.max(...codes.map(code => _isKeyDown(code)))\r\n\r\n// retuns the list of keys currently pressed\r\nexport const whichKeyDown = () => Object.keys(KEYS).filter(code => _isKeyDown(code));\r\n\r\n// returns if any key is currently pressed\r\nexport const anyKeyDown = () => whichKeyDown().length;\r\n\r\n// return true if a key can be released (must be currently pressed) or false if it can't\r\n// note: this \"consumes\" the key pressed by releasing it (only if it was pressed)\r\nexport const isKeyUp = code => _isKeyDown(code) ? _releaseKey(code) : false;", "\r\nimport { WIDTH, HEIGHT, GAME_TITLE, SCALE } from \"./config.js\";\r\nimport { Canvas } from \"./canvas.js\";\r\nimport { TextRenderer } from \"./text.js\";\r\nimport { Room, Object } from \"./objects.js\";\r\nimport { whichKeyDown } from \"./keyboard.js\";\r\nimport { convertTileToScreen, getParameter, hash } from \"./utils.js\";\r\n\r\nlet assets = {\r\n    images: {\r\n        splash: \"../img/splash1.webp\",\r\n        font: \"../img/hampsterfont.webp\",\r\n        tiles: \"../img/t.webp\",\r\n        selector: \"../img/selector.webp\",\r\n    },\r\n    spritesheets: {\r\n        player: [\r\n            {x: 0}, // looking up\r\n            {x: 16}, // looking right\r\n            {x: 32}, // looking down\r\n            {x: 48} // looking left\r\n        ]\r\n    }\r\n}\r\n\r\nconst tileTypes = {\r\n    1: 1, // floor\r\n    2: 2, // wall\r\n}\r\n\r\nlet running = 1;\r\n\r\nlet currentFrame = 0;\r\nlet targetFrames = 60;\r\n\r\nlet lastFrameTime = performance.now();\r\n\r\nlet debug = getParameter(\"debug\") || 0;\r\n\r\nlet rooms = [];\r\nlet debugStatuses = [];\r\nlet canvas = new Canvas(\"c\", WIDTH, HEIGHT);\r\nlet text;\r\n\r\nlet pressedLastFrame = [];\r\ncanvas.fill(\"#222034\");\r\n\r\nlet splash = new Image();\r\nsplash.src = assets.images.splash;\r\nsplash.onload = () => {\r\n    canvas.drawImage(splash, canvas.width / 2 - splash.width / 2, canvas.height / 2 - splash.height / 2);\r\n    let font = new Image();\r\n    font.src = assets.images.font;\r\n    font.onload = () => {\r\n        console.log(\"font loaded\")\r\n        text = new TextRenderer(canvas, font);\r\n        window.onerror = (e) => {\r\n            running = 0;\r\n            text.throwPanic(e);\r\n        }\r\n    }\r\n}\r\n\r\n// Entity class is here becuase otherwise every entity would need the canvas passed into it\r\nclass Entity extends Object {\r\n    constructor(x=0, y=0, spritesheet=null, sprite=null) {\r\n        super();\r\n        this.x = x;\r\n        this.y = y;\r\n        this.sprite = sprite;\r\n        this.spritesheet = spritesheet;\r\n    }\r\n\r\n    draw() {\r\n        canvas.drawImage(this.sprite, this.x, this.y);\r\n    }\r\n}\r\n\r\n// Create all the game rooms\r\nlet roomIndex = 0;\r\nlet currentRoom = rooms[roomIndex];\r\n\r\nlet searchForRoom = (name) => {\r\n    // returns the room's index in the rooms array\r\n    for (let i = 0; i < rooms.length; i++) {\r\n        if (rooms[i].name == name) return i;\r\n    } throw new Error(\"Room not found:\"+name+\". Are you sure it's pushed?\");\r\n}\r\n\r\nconst changeRoom = (index) => {\r\n    currentRoom = rooms[index];\r\n    roomIndex = index;\r\n}\r\n\r\nconst loadingRoom = new Room(\"loading\");\r\nloadingRoom.updateStatus = (status) => {\r\n    console.log(status);\r\n    canvas.fill(\"#222034\");\r\n    canvas.drawImage(splash, canvas.width / 2 - splash.width / 2, canvas.height / 2 - splash.height / 2);\r\n    text.render(status, 0, 0);\r\n}\r\n\r\nconst debugRoom = new Room(\"debug\");\r\ndebugRoom.draw = () => {\r\n    canvas.fill(\"black\");\r\n}\r\ndebugRoom.drawGUI = () => {\r\n    debugStatuses.push(\"Current Frame:\"+currentFrame+`(~${Math.round((currentFrame/targetFrames)*100)/100} sec)`);\r\n}\r\ndebugRoom.keyDown = (key) => {\r\n    if (key == \"Escape\") changeRoom(searchForRoom(\"menu\"));\r\n}\r\n\r\nconst menuRoom = new Room(\"menu\");\r\nmenuRoom.options = [\r\n    {\"label\": \"Start Game\", \"action\": _ => {changeRoom(searchForRoom(\"game\"))}},\r\n];\r\nmenuRoom.index = 0;\r\n\r\nmenuRoom.draw = () => {\r\n    canvas.fill(\"black\");\r\n}\r\nmenuRoom.drawGUI = () => {\r\n    text.render(GAME_TITLE, 8, 7*4);\r\n    for (let i = 0; i < menuRoom.options.length; i++) {\r\n        if (i == menuRoom.index) {\r\n            text.render(\">\", 8, 7*(i+5));\r\n        }\r\n        text.render(menuRoom.options[i].label, 16, 7*(i+5));\r\n    }\r\n}\r\nmenuRoom.keyDown = (key) => {\r\n    if (pressedLastFrame.includes(key)) return;\r\n\r\n    const keyActions = {\r\n        ArrowUp: () => menuRoom.index--,\r\n        ArrowDown: () => menuRoom.index++,\r\n        Enter: () => menuRoom.options[menuRoom.index].action(),\r\n    };\r\n\r\n    const action = keyActions[key];\r\n    if (action) action();\r\n    if (menuRoom.index >= menuRoom.options.length) menuRoom.index = 0;\r\n    if (menuRoom.index < 0) menuRoom.index = menuRoom.options.length-1;\r\n}\r\n\r\nconst getTileType = (x, y, data) => {\r\n    for (let i = 0; i < data.tiles.length; i++) {\r\n        let tile = data.tiles[i];\r\n        if (tile.x == x && tile.y == y) return tileTypes[tile.id];\r\n    }\r\n    return 0;\r\n}\r\n    \r\n\r\nconst renderTiles = (data) => {\r\n    for (let i = 0; i < data.tiles.length; i++) {\r\n        let tile = data.tiles[i];\r\n        let tileLocation = convertTileToScreen(tile.x, tile.y);\r\n        let tId = tile.id;\r\n\r\n        if (tile.id == 2) {\r\n            tId = 3;\r\n            // connect walls to each other.\r\n            // check the tile above, below, left, and right of this one. if it is also id 2, then set the corresponding variable to 1\r\n            getTileType(tile.x, tile.y-1, data) == 2 ? tId += 1 : tId += 0;\r\n            getTileType(tile.x, tile.y+1, data) == 2 ? tId += 2 : tId += 0;\r\n            getTileType(tile.x-1, tile.y, data) == 2 ? tId += 4 : tId += 0;\r\n            getTileType(tile.x+1, tile.y, data) == 2 ? tId += 8 : tId += 0;\r\n        }\r\n\r\n        canvas.sliceImage(assets.images.tiles, tileLocation.x, tileLocation.y, SCALE, SCALE, tId*16, 0, 16, 16);\r\n    }\r\n}\r\n\r\nconst gameRoom = new Room(\"game\"); \r\ngameRoom.data = { tiles: [ ] };\r\ngameRoom.draw = () => {\r\n    canvas.fill(\"black\");\r\n\r\n    renderTiles(gameRoom.data);\r\n}\r\n\r\nconst levelEditor = new Room(\"editor\");\r\nlevelEditor.currentTile = { x: 0, y: 0, id: 0 };\r\nlevelEditor.data = { tiles : [] };\r\nlevelEditor.step = _ => {\r\n\r\n    // place the camera at the center of the current tile if it is outside the screen\r\n    let tileLocation = convertTileToScreen(levelEditor.currentTile.x, levelEditor.currentTile.y);\r\n    if (tileLocation.x < canvas.cX) canvas.cX = tileLocation.x;\r\n    if (tileLocation.x >= canvas.cX+256) canvas.cX = tileLocation.x-canvas.width;\r\n    if (tileLocation.y < canvas.cY) canvas.cY = tileLocation.y;\r\n    if (tileLocation.y > canvas.cY+224) canvas.cY = tileLocation.y-canvas.height;\r\n\r\n\r\n    debugStatuses.push(\"Current tile:\"+levelEditor.currentTile.x+\",\"+levelEditor.currentTile.y);\r\n    debugStatuses.push(\"Current tile ID:\"+levelEditor.currentTile.id);\r\n    debugStatuses.push(\"Camera:\"+canvas.cX+\",\"+canvas.cY);\r\n}\r\nlevelEditor.keyDown = (key) => {\r\n    if (pressedLastFrame.includes(key)) return;\r\n\r\n    const { currentTile, data } = levelEditor;\r\n    const { x, y, id } = currentTile;\r\n\r\n    const keyActions = {\r\n        ArrowUp: () => currentTile.y--,\r\n        ArrowDown: () => currentTile.y++,\r\n        ArrowLeft: () => currentTile.x--,\r\n        ArrowRight: () => currentTile.x++,\r\n        PageUp: () => currentTile.id++,\r\n        PageDown: () => currentTile.id--,\r\n        KeyP: () => console.log(data),\r\n        Enter: () => {\r\n            data.tiles = data.tiles.filter(tile => tile.x !== x || tile.y !== y);\r\n            data.tiles.push({ id, x, y });\r\n        },\r\n    };\r\n\r\n    const action = keyActions[key];\r\n    if (action) action();\r\n};\r\n\r\nlevelEditor.draw = () => {\r\n    canvas.fill(\"#010101\");\r\n\r\n    renderTiles(levelEditor.data);\r\n\r\n    canvas.drawLine(-canvas.width*100, 0, canvas.width*100, 0, \"white\");\r\n    canvas.drawLine(0, -canvas.height*100, 0, canvas.height*100, \"white\");\r\n    text.render(\"(0,0)\", 1-canvas.cX, 1-canvas.cY)\r\n\r\n    let tileLocation = convertTileToScreen(levelEditor.currentTile.x, levelEditor.currentTile.y);\r\n    canvas.ctx.globalAlpha = 0.5;\r\n    canvas.sliceImage(assets.images.tiles, tileLocation.x, tileLocation.y, SCALE, SCALE, levelEditor.currentTile.id*16, 0, 16, 16);\r\n    canvas.ctx.globalAlpha = 1;\r\n    canvas.drawImage(assets.images.selector, tileLocation.x, tileLocation.y, SCALE, SCALE);\r\n}\r\n\r\nrooms.push(loadingRoom);\r\nrooms.push(menuRoom);\r\nrooms.push(gameRoom);\r\nrooms.push(levelEditor);\r\nrooms.push(debugRoom);\r\n\r\ncurrentRoom = rooms[roomIndex];\r\n\r\nlet main = () => { // main game loop\r\n    if (!running) return;\r\n    requestAnimationFrame(main);\r\n    let now = performance.now();\r\n    let delta = now - lastFrameTime;\r\n\r\n    if (delta < 1000 / targetFrames) return;\r\n\r\n    currentFrame++;\r\n    debugStatuses = [];\r\n\r\n    currentRoom.step();\r\n\r\n    currentRoom.draw();\r\n    currentRoom.drawGUI();\r\n\r\n    let currentKeys = whichKeyDown();\r\n    for (let i = 0; i < currentKeys.length; i++) {\r\n        if (debug) debugStatuses.push(currentKeys[i]);\r\n        currentRoom.keyDown(currentKeys[i]);\r\n    }\r\n    \r\n    pressedLastFrame = currentKeys;    \r\n\r\n    if (debug) {\r\n        text.render(\"FPS:\" + Math.round(1000 / delta), 0, 0);\r\n        text.render(currentRoom.name, canvas.width-8*(currentRoom.name.length), 0);\r\n\r\n        debugStatuses.push(\"Debug mode\");\r\n        if (currentFrame <= 60*5) {\r\n            debugStatuses.push(\"Dimensions:\"+canvas.width+\"x\"+canvas.height);\r\n            debugStatuses.push(\"Have fun!\");\r\n        }\r\n    }\r\n\r\n    for (let i = 0; i < debugStatuses.length; i++) {\r\n        text.render(debugStatuses[i], 0, canvas.height-7*(debugStatuses.length-i));\r\n    }\r\n\r\n    lastFrameTime = now;\r\n    }\r\n\r\nlet init = () => {\r\n    // begin loading all the assets.\r\n    currentRoom.updateStatus(\"Loading images...\");\r\n    for (let image in assets.images) {\r\n        currentRoom.updateStatus(\"Loading image \" + image);\r\n        let img = new Image();\r\n        img.src = assets.images[image];\r\n        img.onload = () => {\r\n            assets.images[image] = img;\r\n        }\r\n    }\r\n    console.log(\"Images loaded.\")\r\n    currentRoom.updateStatus(\"Loading complete!\");\r\n    setTimeout(() => {\r\n        \r\n        main();\r\n    }, 1000);\r\n}\r\n\r\nwindow.onload = () => {\r\n    document.title = GAME_TITLE;\r\n    init();\r\n}\r\n"],
+  "mappings": "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",
   "names": []
 }
diff --git a/src/img/selector.webp b/src/img/selector.webp
new file mode 100644
index 0000000000000000000000000000000000000000..8e9d19bc0d5eb33b558f06ff0e907e744ba41b11
Binary files /dev/null and b/src/img/selector.webp differ
diff --git a/src/img/t.webp b/src/img/t.webp
index 19001b624dc205d7218b15f4329add8f99d6a529..339d2d21457c3d7eb3f6d13ba8a9baaa83a56075 100644
Binary files a/src/img/t.webp and b/src/img/t.webp differ
diff --git a/src/index.html b/src/index.html
index a7869e76fce5cfa5d3881965177d3d707d6066d4..18edcf31ddb53f0c6172f2eb13bebdc0384c2fd4 100644
--- a/src/index.html
+++ b/src/index.html
@@ -19,5 +19,6 @@
 </style>
 <body>
   <canvas id=c></canvas>
+  <script src="lzs.js"></script>
   <script type="module" src=game.js></script>
 </body>
\ No newline at end of file
diff --git a/src/js/canvas.js b/src/js/canvas.js
index f5b571bff33932594db6e8489199b52cb4aec5ba..e688f02ce59e969addf7afcf1e5a43512846998a 100644
--- a/src/js/canvas.js
+++ b/src/js/canvas.js
@@ -33,7 +33,7 @@ class Canvas {
         // console.debug("sliceImage", img, x, y, w, h, cropX, cropY, cropW, cropH, direction);
         this.ctx.save();
         this.ctx.translate((x+w/2)-this.cX, (y+h/2)-this.cY);
-        this.ctx.rotate(direction * pi/180);
+        this.ctx.rotate(direction);
         this.ctx.drawImage(img, cropX, cropY, cropW, cropH, -w/2, -h/2, w, h);
         this.ctx.restore();
         // console.log(`${x}, ${y}, ${w}, ${h}, ${cropX}, ${cropY}, ${cropW}, ${cropH}`);
diff --git a/src/js/config.js b/src/js/config.js
index 2111b811d6f702d161b6ce33010975fba9dec0e2..ddaf8616da372a1d3cce76009c90dc8eb2a05c79 100644
--- a/src/js/config.js
+++ b/src/js/config.js
@@ -1,8 +1,9 @@
 // Holds all the config for your game.
 
-const GAME_TITLE = "Untitled JS13K23 Game."
+export const GAME_TITLE = "Untitled JS13K23 Game."
 
-const WIDTH = 160; // pixels
-const HEIGHT = 144; // pixels
+export const WIDTH = 256; // pixels
+export const HEIGHT = 240; // pixels
+
+export const SCALE = 2<<4;
 
-export { GAME_TITLE, WIDTH, HEIGHT };
diff --git a/src/js/game.js b/src/js/game.js
index 3a1c55bad2cff59e1dc33d6fe229e4d15d4c4308..7221e096d35fabdaf56644cf6bc0eda2ce17c7e0 100644
--- a/src/js/game.js
+++ b/src/js/game.js
@@ -1,18 +1,17 @@
 
-import { WIDTH, HEIGHT, GAME_TITLE } from "./config.js";
+import { WIDTH, HEIGHT, GAME_TITLE, SCALE } from "./config.js";
 import { Canvas } from "./canvas.js";
 import { TextRenderer } from "./text.js";
 import { Room, Object } from "./objects.js";
 import { whichKeyDown } from "./keyboard.js";
-import { convertTileToScreen } from "./utils.js";
-
-console.debug(convertTileToScreen(1, 1));
+import { convertTileToScreen, getParameter, hash } from "./utils.js";
 
 let assets = {
     images: {
         splash: "../img/splash1.webp",
         font: "../img/hampsterfont.webp",
-        tiles: "../img/t.webp"
+        tiles: "../img/t.webp",
+        selector: "../img/selector.webp",
     },
     spritesheets: {
         player: [
@@ -21,14 +20,12 @@ let assets = {
             {x: 32}, // looking down
             {x: 48} // looking left
         ]
-    },
-    tilesets: {
-        castle: [
-            {x: 0, y: 0}, // ???
-            {x: 16, y: 0}, // floor
-        ]
     }
+}
 
+const tileTypes = {
+    1: 1, // floor
+    2: 2, // wall
 }
 
 let running = 1;
@@ -38,6 +35,8 @@ let targetFrames = 60;
 
 let lastFrameTime = performance.now();
 
+let debug = getParameter("debug") || 0;
+
 let rooms = [];
 let debugStatuses = [];
 let canvas = new Canvas("c", WIDTH, HEIGHT);
@@ -85,7 +84,7 @@ let searchForRoom = (name) => {
     // returns the room's index in the rooms array
     for (let i = 0; i < rooms.length; i++) {
         if (rooms[i].name == name) return i;
-    } throw new Error("Room not found:"+name);
+    } throw new Error("Room not found:"+name+". Are you sure it's pushed?");
 }
 
 const changeRoom = (index) => {
@@ -113,65 +112,136 @@ debugRoom.keyDown = (key) => {
 }
 
 const menuRoom = new Room("menu");
-let menuOptions = [
+menuRoom.options = [
     {"label": "Start Game", "action": _ => {changeRoom(searchForRoom("game"))}},
-    {"label": "Debug Room", "action": _ => changeRoom(searchForRoom("debug"))},
-    {"label": "Reload", "action": _ => {running = 0; location.reload();}}
 ];
-let menuIndex = 0;
+menuRoom.index = 0;
 
 menuRoom.draw = () => {
     canvas.fill("black");
 }
 menuRoom.drawGUI = () => {
     text.render(GAME_TITLE, 8, 7*4);
-    for (let i = 0; i < menuOptions.length; i++) {
-        if (i == menuIndex) {
+    for (let i = 0; i < menuRoom.options.length; i++) {
+        if (i == menuRoom.index) {
             text.render(">", 8, 7*(i+5));
         }
-        text.render(menuOptions[i].label, 16, 7*(i+5));
+        text.render(menuRoom.options[i].label, 16, 7*(i+5));
     }
 }
 menuRoom.keyDown = (key) => {
     if (pressedLastFrame.includes(key)) return;
-    switch (key) {
-        case "ArrowUp":
-            menuIndex--;
-            break;
-        case "ArrowDown":
-            menuIndex++;
-            break;
-        case "Enter":
-            menuOptions[menuIndex].action();
-            break;
+
+    const keyActions = {
+        ArrowUp: () => menuRoom.index--,
+        ArrowDown: () => menuRoom.index++,
+        Enter: () => menuRoom.options[menuRoom.index].action(),
+    };
+
+    const action = keyActions[key];
+    if (action) action();
+    if (menuRoom.index >= menuRoom.options.length) menuRoom.index = 0;
+    if (menuRoom.index < 0) menuRoom.index = menuRoom.options.length-1;
+}
+
+const getTileType = (x, y, data) => {
+    for (let i = 0; i < data.tiles.length; i++) {
+        let tile = data.tiles[i];
+        if (tile.x == x && tile.y == y) return tileTypes[tile.id];
     }
-    if (menuIndex >= menuOptions.length) menuIndex = 0;
-    if (menuIndex < 0) menuIndex = menuOptions.length-1;
+    return 0;
 }
+    
+
+const renderTiles = (data) => {
+    for (let i = 0; i < data.tiles.length; i++) {
+        let tile = data.tiles[i];
+        let tileLocation = convertTileToScreen(tile.x, tile.y);
+        let tId = tile.id;
+
+        if (tile.id == 2) {
+            tId = 3;
+            // connect walls to each other.
+            // check the tile above, below, left, and right of this one. if it is also id 2, then set the corresponding variable to 1
+            getTileType(tile.x, tile.y-1, data) == 2 ? tId += 1 : tId += 0;
+            getTileType(tile.x, tile.y+1, data) == 2 ? tId += 2 : tId += 0;
+            getTileType(tile.x-1, tile.y, data) == 2 ? tId += 4 : tId += 0;
+            getTileType(tile.x+1, tile.y, data) == 2 ? tId += 8 : tId += 0;
+        }
 
-let currentLevelData = {
-    tiles: [
-        {id: 1, x: 1, y: 1}, // floor at tile coords 1, 1
-        {id:123, x: 2, y: 2},
-    ]
+        canvas.sliceImage(assets.images.tiles, tileLocation.x, tileLocation.y, SCALE, SCALE, tId*16, 0, 16, 16);
+    }
 }
 
 const gameRoom = new Room("game"); 
+gameRoom.data = { tiles: [ ] };
 gameRoom.draw = () => {
     canvas.fill("black");
-    for (let i = 0; i < currentLevelData.tiles.length; i++) {
-        let tile = currentLevelData.tiles[i];
-        if (tile.id > currentLevelData.length) tile.id = 0;
-        let tileLocation = convertTileToScreen(tile.x, tile.y);
-        canvas.sliceImage(assets.images.tiles, tileLocation.x, tileLocation.y, 16, 16, tile.id*16, 0, 16, 16);
-    }
 
-    
+    renderTiles(gameRoom.data);
+}
+
+const levelEditor = new Room("editor");
+levelEditor.currentTile = { x: 0, y: 0, id: 0 };
+levelEditor.data = { tiles : [] };
+levelEditor.step = _ => {
+
+    // place the camera at the center of the current tile if it is outside the screen
+    let tileLocation = convertTileToScreen(levelEditor.currentTile.x, levelEditor.currentTile.y);
+    if (tileLocation.x < canvas.cX) canvas.cX = tileLocation.x;
+    if (tileLocation.x >= canvas.cX+256) canvas.cX = tileLocation.x-canvas.width;
+    if (tileLocation.y < canvas.cY) canvas.cY = tileLocation.y;
+    if (tileLocation.y > canvas.cY+224) canvas.cY = tileLocation.y-canvas.height;
+
+
+    debugStatuses.push("Current tile:"+levelEditor.currentTile.x+","+levelEditor.currentTile.y);
+    debugStatuses.push("Current tile ID:"+levelEditor.currentTile.id);
+    debugStatuses.push("Camera:"+canvas.cX+","+canvas.cY);
+}
+levelEditor.keyDown = (key) => {
+    if (pressedLastFrame.includes(key)) return;
+
+    const { currentTile, data } = levelEditor;
+    const { x, y, id } = currentTile;
+
+    const keyActions = {
+        ArrowUp: () => currentTile.y--,
+        ArrowDown: () => currentTile.y++,
+        ArrowLeft: () => currentTile.x--,
+        ArrowRight: () => currentTile.x++,
+        PageUp: () => currentTile.id++,
+        PageDown: () => currentTile.id--,
+        KeyP: () => console.log(data),
+        Enter: () => {
+            data.tiles = data.tiles.filter(tile => tile.x !== x || tile.y !== y);
+            data.tiles.push({ id, x, y });
+        },
+    };
+
+    const action = keyActions[key];
+    if (action) action();
+};
+
+levelEditor.draw = () => {
+    canvas.fill("#010101");
+
+    renderTiles(levelEditor.data);
+
+    canvas.drawLine(-canvas.width*100, 0, canvas.width*100, 0, "white");
+    canvas.drawLine(0, -canvas.height*100, 0, canvas.height*100, "white");
+    text.render("(0,0)", 1-canvas.cX, 1-canvas.cY)
+
+    let tileLocation = convertTileToScreen(levelEditor.currentTile.x, levelEditor.currentTile.y);
+    canvas.ctx.globalAlpha = 0.5;
+    canvas.sliceImage(assets.images.tiles, tileLocation.x, tileLocation.y, SCALE, SCALE, levelEditor.currentTile.id*16, 0, 16, 16);
+    canvas.ctx.globalAlpha = 1;
+    canvas.drawImage(assets.images.selector, tileLocation.x, tileLocation.y, SCALE, SCALE);
 }
 
 rooms.push(loadingRoom);
 rooms.push(menuRoom);
 rooms.push(gameRoom);
+rooms.push(levelEditor);
 rooms.push(debugRoom);
 
 currentRoom = rooms[roomIndex];
@@ -187,24 +257,28 @@ let main = () => { // main game loop
     currentFrame++;
     debugStatuses = [];
 
+    currentRoom.step();
+
     currentRoom.draw();
     currentRoom.drawGUI();
 
     let currentKeys = whichKeyDown();
     for (let i = 0; i < currentKeys.length; i++) {
-        debugStatuses.push(currentKeys[i]);
+        if (debug) debugStatuses.push(currentKeys[i]);
         currentRoom.keyDown(currentKeys[i]);
     }
     
     pressedLastFrame = currentKeys;    
-    
-    text.render("FPS:" + Math.round(1000 / delta), 0, 0);
-    text.render(currentRoom.name, canvas.width-8*(currentRoom.name.length), 0);
 
-    if (currentFrame <= 60*5) {
-        debugStatuses.push("Debug mode.");
-        debugStatuses.push("Dimensions:"+canvas.width+"x"+canvas.height);
-        debugStatuses.push("Have fun!");
+    if (debug) {
+        text.render("FPS:" + Math.round(1000 / delta), 0, 0);
+        text.render(currentRoom.name, canvas.width-8*(currentRoom.name.length), 0);
+
+        debugStatuses.push("Debug mode");
+        if (currentFrame <= 60*5) {
+            debugStatuses.push("Dimensions:"+canvas.width+"x"+canvas.height);
+            debugStatuses.push("Have fun!");
+        }
     }
 
     for (let i = 0; i < debugStatuses.length; i++) {
@@ -225,14 +299,10 @@ let init = () => {
             assets.images[image] = img;
         }
     }
-    console.log(assets.images);
     console.log("Images loaded.")
     currentRoom.updateStatus("Loading complete!");
-
-    canvas.fill("#222034");
-    canvas.drawImage(splash, canvas.width / 2 - splash.width / 2, canvas.height / 2 - splash.height / 2);
     setTimeout(() => {
-        currentRoom = rooms[1];
+        
         main();
     }, 1000);
 }
diff --git a/src/js/text.js b/src/js/text.js
index ae8f2c9235bfb83e2c0187a8f46398e18d65b5c0..cbe816deed3bea64952879fce8baf42737e00fa6 100644
--- a/src/js/text.js
+++ b/src/js/text.js
@@ -9,13 +9,15 @@ class TextRenderer {
         this.canvas = canvas;
     }
 
-    drawLetter(letter, x, y, substituteOK=0) {
+    drawLetter(letter, x, y, substituteOK=1) {
+        let { canvas, fontWidth, fontHeight } = this;
+
         let index = this.fontChars.indexOf(letter.toLowerCase());
         if (index == -1) {
             if (!substituteOK) return;
-            this.canvas.drawText(letter, x, y, "#ffffff", 7, "monospace");
+            canvas.drawText(letter, x, y, "#ffffff", 7, "monospace");
         }
-        let sx = index * this.fontWidth;
+        let sx = index * fontWidth;
         let sy = 0;
         // draw image to context
         let yOffset = 0;
@@ -23,7 +25,9 @@ class TextRenderer {
         if (letter == ",") {
             yOffset = -1;
         }
-        this.canvas.sliceImage(this.fontimg, x, y + yOffset, this.fontWidth, this.fontHeight, sx, sy, this.fontWidth, this.fontHeight);
+        canvas.sliceImage(this.fontimg, x+canvas.cX, y + yOffset + canvas.cY, fontWidth, fontHeight, sx, sy, fontWidth, fontHeight); 
+            // canvas.cX and canvas.cY are the camera offsets. we dont want to have text flying off the screen.
+            // you can counteract this by specifying x-cX and x-cY when calling this.
     }
 
     render(text, x, y) {
diff --git a/src/js/utils.js b/src/js/utils.js
index 4d75fb04c9da82e5c642b881c9ea26091db79a1c..15051afa7be70d7ddc1768ec7c16284e8e5caad9 100644
--- a/src/js/utils.js
+++ b/src/js/utils.js
@@ -1,5 +1,36 @@
 // random shit
 
+import { SCALE } from "./config";
+
+const params = new URLSearchParams(location.search);
+
 export const pi = Math.PI;
+export const convertTileToScreen = (x, y) => ({x: x*SCALE, y: y*SCALE});
+export const getParameter = key => key ? params.get(key) : 0;
+export const hash = window.location.hash.split("?")[0].slice(1);
+
+// The following code is responsible for building levels.
+const lineToCoords=(x0,y0,x1,y1,id=2)=>{const c=[];let dx=Math.abs(x1-x0),dy=Math.abs(y1-y0),sx=(x0<x1)?1:-1,sy=(y0<y1)?1:-1,err=dx-dy;while(true){c.push({x:x0,y:y0,id:id});if(x0===x1&&y0===y1)break;const e2=2*err;if(e2>-dy){err-=dy;x0+=sx}if(e2<dx){err+=dx;y0+=sy}}return c};
+
+const generateBox = (x, y, width, height, fillTileId=1) => {
+    const box = [];
+    
+    for (let y = y; y < height; y++) {
+        for (let x = x; x < width; x++) {
+            box.push({ x, y, id: fillTileId });
+        }
+    }
+    
+    return box;
+}
+
+export const generateRoom = (x,y,width,height,lineId=2,fillId=1) => {
+    let top = lineToCoords(x, y, x+width, y, lineId);
+    let bottom = lineToCoords(x, y+height, x+width, y+height, lineId);
+    let left = lineToCoords(x, y, x, y+height, lineId);
+    let right = lineToCoords(x+width, y, x+width, y+height, lineId);
+
+    let fill = generateBox(x+1, y+1, x+width-1, y+height-1, fillId);
 
-export const convertTileToScreen = (x, y) => ({x: x<<4, y: y<<4});
+    return [...top, ...bottom, ...left, ...right, ...fill];
+}