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": ";;AAEA,MAAM,aAAa;AAEnB,MAAM,QAAQ;AACd,MAAM,SAAS;;;ACHR,MAAM,KAAK,KAAK;AAEhB,MAAM,sBAAsB,CAAC,GAAG,MAAO,EAAC,GAAG,KAAG,GAAG,GAAG,KAAG;;;ACA9D,qBAAa;AAAA,IACT,YAAY,KAAG,KAAK,IAAE,KAAK,IAAE,KAAK;AAC9B,WAAK,SAAS,SAAS,eAAe;AACtC,WAAK,OAAO,QAAQ;AACpB,WAAK,OAAO,SAAS;AAErB,WAAK,MAAM,KAAK,OAAO,WAAW;AAClC,WAAK,IAAI,wBAAwB;AACjC,WAAK,IAAI,eAAe;AAExB,WAAK,QAAQ,KAAK,OAAO;AACzB,WAAK,SAAS,KAAK,OAAO;AAG1B,WAAK,KAAK;AACV,WAAK,KAAK;AAAA;AAAA,IAGd,KAAK,IAAE,SAAS;AACZ,WAAK,IAAI,YAAY;AACrB,WAAK,IAAI,SAAS,GAAG,GAAG,KAAK,OAAO,KAAK;AAAA;AAAA,IAG7C,UAAU,OAAO,GAAG,GAAG,QAAQ,MAAM,OAAO,SAAS,MAAM,QAAQ;AAC/D,cAAQ,MAAM,aAAa,OAAO,GAAG,GAAG,OAAO;AAC/C,WAAK,IAAI,UAAU,OAAO,IAAE,KAAK,IAAI,IAAE,KAAK,IAAI,OAAO;AAAA;AAAA,IAG3D,WAAW,KAAK,GAAG,GAAG,GAAG,GAAG,OAAO,OAAO,OAAO,OAAO,YAAU,GAAG;AAEjE,WAAK,IAAI;AACT,WAAK,IAAI,UAAW,IAAE,IAAE,IAAG,KAAK,IAAK,IAAE,IAAE,IAAG,KAAK;AACjD,WAAK,IAAI,OAAO,YAAY,KAAG;AAC/B,WAAK,IAAI,UAAU,KAAK,OAAO,OAAO,OAAO,OAAO,CAAC,IAAE,GAAG,CAAC,IAAE,GAAG,GAAG;AACnE,WAAK,IAAI;AAAA;AAAA,IAIb,SAAS,OAAM,GAAG,GAAG,IAAE,SAAS,OAAK,IAAI,OAAK,aAAa;AACvD,WAAK,IAAI,YAAY;AACrB,WAAK,IAAI,OAAO,GAAG,UAAU;AAC7B,WAAK,IAAI,SAAS,OAAM,GAAG;AAAA;AAAA,IAG/B,SAAS,IAAI,IAAI,IAAI,IAAI,IAAE,SAAS,IAAE,GAAG;AACrC,WAAK,IAAI,cAAc;AACvB,WAAK,IAAI,YAAY;AACrB,WAAK,IAAI;AACT,WAAK,IAAI,OAAO,KAAG,KAAK,IAAI,KAAG,KAAK;AACpC,WAAK,IAAI,OAAO,KAAG,KAAK,IAAI,KAAG,KAAK;AACpC,WAAK,IAAI;AAAA;AAAA;;;ACpDjB,2BAAmB;AAAA,IACf,YAAY,SAAQ,SAAS;AACzB,WAAK,UAAU;AACf,WAAK,YAAY;AACjB,WAAK,aAAa;AAClB,WAAK,YAAY;AACjB,WAAK,SAAS;AAAA;AAAA,IAGlB,WAAW,QAAQ,GAAG,GAAG,eAAa,GAAG;AACrC,UAAI,QAAQ,KAAK,UAAU,QAAQ,OAAO;AAC1C,UAAI,SAAS,IAAI;AACb,YAAI,CAAC;AAAc;AACnB,aAAK,OAAO,SAAS,QAAQ,GAAG,GAAG,WAAW,GAAG;AAAA;AAErD,UAAI,KAAK,QAAQ,KAAK;AACtB,UAAI,KAAK;AAET,UAAI,UAAU;AAEd,UAAI,UAAU,KAAK;AACf,kBAAU;AAAA;AAEd,WAAK,OAAO,WAAW,KAAK,SAAS,GAAG,IAAI,SAAS,KAAK,WAAW,KAAK,YAAY,IAAI,IAAI,KAAK,WAAW,KAAK;AAAA;AAAA,IAGvH,OAAO,OAAM,GAAG,GAAG;AACf,UAAI,eAAe;AACnB,UAAI,UAAU;AACd,eAAS,IAAI,GAAG,IAAI,MAAK,QAAQ,KAAK;AAClC,YAAI,MAAK,MAAM,MAAM;AACjB;AACA,oBAAU;AACV;AAAA;AAEJ,aAAK,WAAW,MAAK,IAAI,IAAK,UAAU,KAAK,WAAY,IAAK,eAAe,KAAK;AAClF;AAAA;AAAA;AAAA,IAIR,aAAa,CAAC,QAAQ;AAIlB,WAAK,OAAO,KAAK;AAEjB,WAAK,OAAO,KAAK,GAAG;AACpB,YAAM;AAAA;AAAA;;;ACjDd,sBAAa;AAAA,IACT,OAAO;AAAA;AAAA,IACP,OAAO;AAAA;AAAA;AAIX,2BAAmB,QAAO;AAAA,IACtB,YAAY,OAAK,IAAI;AACjB;AACA,WAAK,UAAU;AACf,WAAK,OAAO;AAAA;AAAA,IAGhB,OAAO;AACH,eAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,QAAQ,KAAK;AAC1C,aAAK,QAAQ,GAAG;AAAA;AAAA;AAAA,IAIxB,UAAU;AAAA;AAAA,IAIV,QAAQ,KAAK;AAAA;AAAA,IAGb,MAAM,KAAK;AAAA;AAAA,IAKX,OAAO;AACH,eAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,QAAQ,KAAK;AAC1C,aAAK,QAAQ,GAAG;AAAA;AAAA;AAAA;;;AClB5B,MAAM,OAAO;AAEb,MAAM,aAAa,UAAQ,KAAK,SAAS;AAEzC,MAAM,cAAc,UAAQ,OAAO,KAAK;AAExC,mBAAiB,WAAW,OAAK;AAE/B,MAAE;AAEF,QAAI,CAAC,EAAE,QAAQ;AACb,WAAK,EAAE,QAAQ,YAAY;AAAA;AAAA;AAI/B,mBAAiB,SAAS,OAAK,YAAY,EAAE;AAWtC,MAAM,eAAe,MAAM,OAAO,KAAK,MAAM,OAAO,UAAQ,WAAW;;;ACjC9E,UAAQ,MAAM,oBAAoB,GAAG;AAErC,MAAI,SAAS;AAAA,IACT,QAAQ;AAAA,MACJ,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,OAAO;AAAA;AAAA,IAEX,cAAc;AAAA,MACV,QAAQ;AAAA,QACJ,CAAC,GAAG;AAAA,QACJ,CAAC,GAAG;AAAA,QACJ,CAAC,GAAG;AAAA,QACJ,CAAC,GAAG;AAAA;AAAA;AAAA,IAGZ,UAAU;AAAA,MACN,QAAQ;AAAA,QACJ,CAAC,GAAG,GAAG,GAAG;AAAA,QACV,CAAC,GAAG,IAAI,GAAG;AAAA;AAAA;AAAA;AAMvB,MAAI,UAAU;AAEd,MAAI,eAAe;AACnB,MAAI,eAAe;AAEnB,MAAI,gBAAgB,YAAY;AAEhC,MAAI,QAAQ;AACZ,MAAI,gBAAgB;AACpB,MAAI,SAAS,IAAI,OAAO,KAAK,OAAO;AACpC,MAAI;AAEJ,MAAI,mBAAmB;AACvB,SAAO,KAAK;AAEZ,MAAI,SAAS,IAAI;AACjB,SAAO,MAAM,OAAO,OAAO;AAC3B,SAAO,SAAS,MAAM;AAClB,WAAO,UAAU,QAAQ,OAAO,QAAQ,IAAI,OAAO,QAAQ,GAAG,OAAO,SAAS,IAAI,OAAO,SAAS;AAClG,QAAI,OAAO,IAAI;AACf,SAAK,MAAM,OAAO,OAAO;AACzB,SAAK,SAAS,MAAM;AAChB,cAAQ,IAAI;AACZ,aAAO,IAAI,aAAa,QAAQ;AAChC,aAAO,UAAU,CAAC,MAAM;AACpB,kBAAU;AACV,aAAK,WAAW;AAAA;AAAA;AAAA;AAqB5B,MAAI,YAAY;AAChB,MAAI,cAAc,MAAM;AAExB,MAAI,gBAAgB,CAAC,SAAS;AAE1B,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACnC,UAAI,MAAM,GAAG,QAAQ;AAAM,eAAO;AAAA;AACpC,UAAM,IAAI,MAAM,oBAAkB;AAAA;AAGxC,MAAM,aAAa,CAAC,UAAU;AAC1B,kBAAc,MAAM;AACpB,gBAAY;AAAA;AAGhB,MAAM,cAAc,IAAI,KAAK;AAC7B,cAAY,eAAe,CAAC,WAAW;AACnC,YAAQ,IAAI;AACZ,WAAO,KAAK;AACZ,WAAO,UAAU,QAAQ,OAAO,QAAQ,IAAI,OAAO,QAAQ,GAAG,OAAO,SAAS,IAAI,OAAO,SAAS;AAClG,SAAK,OAAO,QAAQ,GAAG;AAAA;AAG3B,MAAM,YAAY,IAAI,KAAK;AAC3B,YAAU,OAAO,MAAM;AACnB,WAAO,KAAK;AAAA;AAEhB,YAAU,UAAU,MAAM;AACtB,kBAAc,KAAK,mBAAiB,eAAa,KAAK,KAAK,MAAO,eAAa,eAAc,OAAK;AAAA;AAEtG,YAAU,UAAU,CAAC,QAAQ;AACzB,QAAI,OAAO;AAAU,iBAAW,cAAc;AAAA;AAGlD,MAAM,WAAW,IAAI,KAAK;AAC1B,MAAI,cAAc;AAAA,IACd,CAAC,OAAS,cAAc,QAAU,OAAK;AAAC,iBAAW,cAAc;AAAA;AAAA,IACjE,CAAC,OAAS,cAAc,QAAU,OAAK,WAAW,cAAc;AAAA,IAChE,CAAC,OAAS,UAAU,QAAU,OAAK;AAAC,gBAAU;AAAG,eAAS;AAAA;AAAA;AAE9D,MAAI,YAAY;AAEhB,WAAS,OAAO,MAAM;AAClB,WAAO,KAAK;AAAA;AAEhB,WAAS,UAAU,MAAM;AACrB,SAAK,OAAO,YAAY,GAAG,IAAE;AAC7B,aAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AACzC,UAAI,KAAK,WAAW;AAChB,aAAK,OAAO,KAAK,GAAG,IAAG,KAAE;AAAA;AAE7B,WAAK,OAAO,YAAY,GAAG,OAAO,IAAI,IAAG,KAAE;AAAA;AAAA;AAGnD,WAAS,UAAU,CAAC,QAAQ;AACxB,QAAI,iBAAiB,SAAS;AAAM;AACpC,YAAQ;AAAA,WACC;AACD;AACA;AAAA,WACC;AACD;AACA;AAAA,WACC;AACD,oBAAY,WAAW;AACvB;AAAA;AAER,QAAI,aAAa,YAAY;AAAQ,kBAAY;AACjD,QAAI,YAAY;AAAG,kBAAY,YAAY,SAAO;AAAA;AAGtD,MAAI,mBAAmB;AAAA,IACnB,OAAO;AAAA,MACH,CAAC,IAAI,GAAG,GAAG,GAAG,GAAG;AAAA,MACjB,CAAC,IAAG,KAAK,GAAG,GAAG,GAAG;AAAA;AAAA;AAI1B,MAAM,WAAW,IAAI,KAAK;AAC1B,WAAS,OAAO,MAAM;AAClB,WAAO,KAAK;AACZ,aAAS,IAAI,GAAG,IAAI,iBAAiB,MAAM,QAAQ,KAAK;AACpD,UAAI,OAAO,iBAAiB,MAAM;AAClC,UAAI,KAAK,KAAK,iBAAiB;AAAQ,aAAK,KAAK;AACjD,UAAI,eAAe,oBAAoB,KAAK,GAAG,KAAK;AACpD,aAAO,WAAW,OAAO,OAAO,OAAO,aAAa,GAAG,aAAa,GAAG,IAAI,IAAI,KAAK,KAAG,IAAI,GAAG,IAAI;AAAA;AAAA;AAM1G,QAAM,KAAK;AACX,QAAM,KAAK;AACX,QAAM,KAAK;AACX,QAAM,KAAK;AAEX,gBAAc,MAAM;AAEpB,MAAI,OAAO,MAAM;AACb,QAAI,CAAC;AAAS;AACd,0BAAsB;AACtB,QAAI,MAAM,YAAY;AACtB,QAAI,QAAQ,MAAM;AAElB,QAAI,QAAQ,MAAO;AAAc;AAEjC;AACA,oBAAgB;AAEhB,gBAAY;AACZ,gBAAY;AAEZ,QAAI,cAAc;AAClB,aAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AACzC,oBAAc,KAAK,YAAY;AAC/B,kBAAY,QAAQ,YAAY;AAAA;AAGpC,uBAAmB;AAEnB,SAAK,OAAO,SAAS,KAAK,MAAM,MAAO,QAAQ,GAAG;AAClD,SAAK,OAAO,YAAY,MAAM,OAAO,QAAM,IAAG,YAAY,KAAK,QAAS;AAExE,QAAI,gBAAgB,KAAG,GAAG;AACtB,oBAAc,KAAK;AACnB,oBAAc,KAAK,gBAAc,OAAO,QAAM,MAAI,OAAO;AACzD,oBAAc,KAAK;AAAA;AAGvB,aAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC3C,WAAK,OAAO,cAAc,IAAI,GAAG,OAAO,SAAO,IAAG,eAAc,SAAO;AAAA;AAG3E,oBAAgB;AAAA;AAGpB,MAAI,OAAO,MAAM;AAEb,gBAAY,aAAa;AACzB,aAAS,SAAS,OAAO,QAAQ;AAC7B,kBAAY,aAAa,mBAAmB;AAC5C,UAAI,MAAM,IAAI;AACd,UAAI,MAAM,OAAO,OAAO;AACxB,UAAI,SAAS,MAAM;AACf,eAAO,OAAO,SAAS;AAAA;AAAA;AAG/B,YAAQ,IAAI,OAAO;AACnB,YAAQ,IAAI;AACZ,gBAAY,aAAa;AAEzB,WAAO,KAAK;AACZ,WAAO,UAAU,QAAQ,OAAO,QAAQ,IAAI,OAAO,QAAQ,GAAG,OAAO,SAAS,IAAI,OAAO,SAAS;AAClG,eAAW,MAAM;AACb,oBAAc,MAAM;AACpB;AAAA,OACD;AAAA;AAGP,SAAO,SAAS,MAAM;AAClB,aAAS,QAAQ;AACjB;AAAA;", + "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]; +}