diff --git a/.gitignore b/.gitignore index 40b878db5b1c97fc77049537a71bb2e249abe5dc..04c01ba7ba0830ccdc49fce1c7257114ed81e68b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules/ \ No newline at end of file +node_modules/ +dist/ \ No newline at end of file diff --git a/assets/tilemap.ase b/assets/tilemap.ase new file mode 100644 index 0000000000000000000000000000000000000000..c559a196e2b36890b647307e6d650a7a7e491979 Binary files /dev/null and b/assets/tilemap.ase differ diff --git a/dist/game.js b/dist/game.js index 635d3ef3f17e5bdc03b412d8afec59d01ba4648e..494c9c9c366570cf13418ad7e75126cff6914199 100644 --- a/dist/game.js +++ b/dist/game.js @@ -1,8 +1,12 @@ (() => { // src/js/config.js var GAME_TITLE = "Untitled JS13K23 Game."; - var WIDTH = 256; - var HEIGHT = 256; + var WIDTH = 160; + var HEIGHT = 144; + + // src/js/utils.js + var pi = Math.PI; + var convertTileToScreen = (x, y) => ({x: x << 4, y: y << 4}); // src/js/canvas.js var Canvas = class { @@ -10,35 +14,41 @@ this.canvas = document.getElementById(id); this.canvas.width = w; this.canvas.height = h; - this.context = this.canvas.getContext("2d"); - this.context.imageSmoothingEnabled = false; - this.context.textBaseline = "top"; + this.ctx = this.canvas.getContext("2d"); + this.ctx.imageSmoothingEnabled = false; + this.ctx.textBaseline = "top"; this.width = this.canvas.width; this.height = this.canvas.height; + this.cX = 0; + this.cY = 0; } fill(c = "black") { - this.context.fillStyle = c; - this.context.fillRect(0, 0, this.width, this.height); + this.ctx.fillStyle = c; + 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.context.drawImage(image, x, y, width, height); + this.ctx.drawImage(image, x - this.cX, y - this.cY, width, height); } - sliceImage(image, sx, sy, sw, sh, dx, dy, dw = sw, dh = sh) { - this.context.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh); + 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.drawImage(img, cropX, cropY, cropW, cropH, -w / 2, -h / 2, w, h); + this.ctx.restore(); } drawText(text2, x, y, c = "white", size = 16, font = "monospace") { - this.context.fillStyle = c; - this.context.font = `${size}px ${font}`; - this.context.fillText(text2, x, y); + this.ctx.fillStyle = c; + this.ctx.font = `${size}px ${font}`; + this.ctx.fillText(text2, x, y); } drawLine(x1, y1, x2, y2, c = "white", w = 1) { - this.context.strokeStyle = c; - this.context.lineWidth = w; - this.context.beginPath(); - this.context.moveTo(x1, y1); - this.context.lineTo(x2, y2); - this.context.stroke(); + this.ctx.strokeStyle = c; + this.ctx.lineWidth = w; + this.ctx.beginPath(); + this.ctx.moveTo(x1 - this.cX, y1 - this.cY); + this.ctx.lineTo(x2 - this.cX, y2 - this.cY); + this.ctx.stroke(); } }; @@ -48,13 +58,15 @@ this.fontimg = fontimg; this.fontWidth = 7; this.fontHeight = 7; - this.fontChars = "abcdefghijklmnopqrstuvwxyz1234567890.,!?:;)(~"; + this.fontChars = "abcdefghijklmnopqrstuvwxyz1234567890.,!?:;)(~>"; this.canvas = canvas2; } - drawLetter(letter, x, y) { + drawLetter(letter, x, y, substituteOK = 0) { let index = this.fontChars.indexOf(letter.toLowerCase()); if (index == -1) { - return; + if (!substituteOK) + return; + this.canvas.drawText(letter, x, y, "#ffffff", 7, "monospace"); } let sx = index * this.fontWidth; let sy = 0; @@ -62,7 +74,7 @@ if (letter == ",") { yOffset = -1; } - this.canvas.sliceImage(this.fontimg, sx, sy, this.fontWidth, this.fontHeight, x, y - yOffset, this.fontWidth, this.fontHeight); + this.canvas.sliceImage(this.fontimg, x, y + yOffset, this.fontWidth, this.fontHeight, sx, sy, this.fontWidth, this.fontHeight); } render(text2, x, y) { let heightOffset = 0; @@ -92,9 +104,10 @@ } }; var Room = class extends Object2 { - constructor() { + constructor(name = "") { super(); this.objects = []; + this.name = name; } draw() { for (let i = 0; i < this.objects.length; i++) { @@ -103,6 +116,10 @@ } drawGUI() { } + keyDown(key) { + } + keyUp(key) { + } step() { for (let i = 0; i < this.objects.length; i++) { this.objects[i].step(); @@ -112,28 +129,49 @@ // src/js/keyboard.js var KEYS = {}; + var _isKeyDown = (code) => KEYS[code] || 0; var _releaseKey = (code) => delete KEYS[code]; addEventListener("keydown", (e) => { + e.preventDefault(); if (!e.repeat) { KEYS[e.code] = performance.now(); } }); addEventListener("keyup", (e) => _releaseKey(e.code)); + 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", - splash2: "../img/splash2.webp", - font: "../img/hampsterfont.webp" + font: "../img/hampsterfont.webp", + tiles: "../img/t.webp" + }, + spritesheets: { + player: [ + {x: 0}, + {x: 16}, + {x: 32}, + {x: 48} + ] + }, + tilesets: { + castle: [ + {x: 0, y: 0}, + {x: 16, y: 0} + ] } }; + var running = 1; var currentFrame = 0; var targetFrames = 60; var lastFrameTime = performance.now(); var rooms = []; + var debugStatuses = []; var canvas = new Canvas("c", WIDTH, HEIGHT); var text; + var pressedLastFrame = []; canvas.fill("#222034"); var splash = new Image(); splash.src = assets.images.splash; @@ -145,20 +183,23 @@ console.log("font loaded"); text = new TextRenderer(canvas, font); window.onerror = (e) => { + running = 0; text.throwPanic(e); }; }; }; - var DebugEntity = class extends Object2 { - constructor() { - super(); - this.x = 0; - this.y = 0; - } - draw() { - canvas.context.fillStyle = "red"; - canvas.context.fillRect(this.x, this.y, 10, 10); + var roomIndex = 0; + var currentRoom = rooms[roomIndex]; + var searchForRoom = (name) => { + for (let i = 0; i < rooms.length; i++) { + if (rooms[i].name == name) + return i; } + throw new Error("Room not found:" + name); + }; + var changeRoom = (index) => { + currentRoom = rooms[index]; + roomIndex = index; }; var loadingRoom = new Room("loading"); loadingRoom.updateStatus = (status) => { @@ -169,33 +210,109 @@ }; var debugRoom = new Room("debug"); debugRoom.draw = () => { - canvas.fill("#222034"); - canvas; - for (let i = 0; i < debugRoom.objects.length; i++) { - debugRoom.objects[i].draw(); - } + canvas.fill("black"); }; debugRoom.drawGUI = () => { - text.render("Welcome to the Debug Room,\nwe've got fun and games", 0, canvas.height - 14); - text.render("Current Frame:" + currentFrame + `(~${Math.floor(currentFrame / targetFrames * 100) / 100}sec)`, 0, canvas.height - 21); + debugStatuses.push("Current Frame:" + currentFrame + `(~${Math.round(currentFrame / targetFrames * 100) / 100} sec)`); + }; + debugRoom.keyDown = (key) => { + if (key == "Escape") + changeRoom(searchForRoom("menu")); + }; + var menuRoom = new Room("menu"); + var menuOptions = [ + {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.draw = () => { + canvas.fill("black"); + }; + menuRoom.drawGUI = () => { + text.render(GAME_TITLE, 8, 7 * 4); + for (let i = 0; i < menuOptions.length; i++) { + if (i == menuIndex) { + text.render(">", 8, 7 * (i + 5)); + } + text.render(menuOptions[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; + } + if (menuIndex >= menuOptions.length) + menuIndex = 0; + if (menuIndex < 0) + menuIndex = menuOptions.length - 1; + }; + var currentLevelData = { + tiles: [ + {id: 1, x: 1, y: 1}, + {id: 123, x: 2, y: 2} + ] + }; + var gameRoom = new Room("game"); + 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); + } }; - var testObject = new DebugEntity(0, 0); - debugRoom.objects.push(testObject); rooms.push(loadingRoom); + rooms.push(menuRoom); + rooms.push(gameRoom); rooms.push(debugRoom); - var roomIndex = 0; - var currentRoom = rooms[roomIndex]; + currentRoom = rooms[roomIndex]; var main = () => { + if (!running) + return; requestAnimationFrame(main); let now = performance.now(); let delta = now - lastFrameTime; if (delta < 1e3 / targetFrames) return; currentFrame++; + debugStatuses = []; currentRoom.draw(); currentRoom.drawGUI(); - lastFrameTime = now; + let currentKeys = whichKeyDown(); + for (let i = 0; i < currentKeys.length; i++) { + 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!"); + } + for (let i = 0; i < debugStatuses.length; i++) { + text.render(debugStatuses[i], 0, canvas.height - 7 * (debugStatuses.length - i)); + } + lastFrameTime = now; }; var init = () => { currentRoom.updateStatus("Loading images..."); @@ -207,6 +324,8 @@ 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); @@ -216,12 +335,8 @@ }, 1e3); }; window.onload = () => { - try { - document.title = GAME_TITLE; - init(); - } catch (e) { - text.throwPanic(e); - } + document.title = GAME_TITLE; + init(); }; })(); //# sourceMappingURL=game.js.map diff --git a/dist/game.js.map b/dist/game.js.map index 0fb77d1487b8e559afd409c45661dd3e3c46b266..831429a02de40f78951b4c5bc29b243057397ba7 100644 --- a/dist/game.js.map +++ b/dist/game.js.map @@ -1,7 +1,7 @@ { "version": 3, - "sources": ["../src/js/config.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 = 256; // pixels\r\nconst HEIGHT = 256; // pixels\r\n\r\nexport { GAME_TITLE, WIDTH, HEIGHT };\r\n", "\r\n// Holds canvas, context and adds endpoints for graphics\r\n\r\nconst floor = function (...args) {\r\n return Math.floor(...args);\r\n}\r\n\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.context = this.canvas.getContext(\"2d\");\r\n this.context.imageSmoothingEnabled = false;\r\n this.context.textBaseline = \"top\";\r\n\r\n this.width = this.canvas.width;\r\n this.height = this.canvas.height;\r\n }\r\n\r\n fill(c=\"black\") {\r\n this.context.fillStyle = c;\r\n this.context.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.context.drawImage(image, x, y, width, height);\r\n }\r\n\r\n sliceImage(image, sx, sy, sw, sh, dx, dy, dw = sw, dh = sh) {\r\n this.context.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);\r\n }\r\n\r\n drawText(text, x, y, c=\"white\", size=16, font=\"monospace\") {\r\n this.context.fillStyle = c;\r\n this.context.font = `${size}px ${font}`;\r\n this.context.fillText(text, x, y);\r\n }\r\n\r\n drawLine(x1, y1, x2, y2, c=\"white\", w=1) {\r\n this.context.strokeStyle = c;\r\n this.context.lineWidth = w;\r\n this.context.beginPath();\r\n this.context.moveTo(x1, y1);\r\n this.context.lineTo(x2, y2);\r\n this.context.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) {\r\n let index = this.fontChars.indexOf(letter.toLowerCase());\r\n if (index == -1) {\r\n return;\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, sx, sy, this.fontWidth, this.fontHeight, x, y-yOffset, 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 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\nclass Room extends Object {\r\n constructor() {\r\n super();\r\n this.objects = [];\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 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 { isKeyDown } from \"./keyboard.js\";\r\n\r\nlet assets = {\r\n images: {\r\n splash: \"../img/splash1.webp\",\r\n splash2: \"../img/splash2.webp\",\r\n font: \"../img/hampsterfont.webp\"\r\n }\r\n}\r\n\r\nlet currentFrame = 0;\r\nlet targetFrames = 60;\r\n\r\nlet lastFrameTime = performance.now();\r\nlet startFrameTime = lastFrameTime;\r\n\r\nlet rooms = [];\r\nlet canvas = new Canvas(\"c\", WIDTH, HEIGHT);\r\nlet text;\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 text.throwPanic(e);\r\n }\r\n }\r\n}\r\n\r\nclass DebugEntity extends Object {\r\n constructor() {\r\n super();\r\n this.x = 0;\r\n this.y = 0;\r\n }\r\n\r\n draw() {\r\n canvas.context.fillStyle = \"red\";\r\n canvas.context.fillRect(this.x, this.y, 10, 10);\r\n }\r\n}\r\n\r\n// Create all the game rooms\r\n\r\nlet 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\nlet debugRoom = new Room(\"debug\");\r\ndebugRoom.draw = () => {\r\n canvas.fill(\"#222034\");\r\n canvas\r\n for (let i = 0; i < debugRoom.objects.length; i++) {\r\n debugRoom.objects[i].draw();\r\n }\r\n}\r\ndebugRoom.drawGUI = () => {\r\n text.render(\"Welcome to the Debug Room,\\nwe've got fun and games\", 0, canvas.height-14);\r\n text.render(\"Current Frame:\" + currentFrame + `(~${Math.floor((currentFrame/targetFrames)*100)/100}sec)`, 0, canvas.height-21);\r\n}\r\nlet testObject = new DebugEntity(0, 0);\r\ndebugRoom.objects.push(testObject);\r\n\r\nrooms.push(loadingRoom);\r\nrooms.push(debugRoom);\r\n\r\nlet roomIndex = 0;\r\nlet currentRoom = rooms[roomIndex];\r\n\r\nlet main = () => { // main game loop\r\n requestAnimationFrame(main);\r\n\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 \r\n currentRoom.draw();\r\n currentRoom.drawGUI();\r\n\r\n lastFrameTime = now;\r\n\r\n text.render(\"FPS:\" + Math.round(1000 / delta), 0, 0);\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 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 try {\r\n document.title = GAME_TITLE;\r\n init();\r\n } catch (e) {\r\n text.throwPanic(e);\r\n }\r\n}\r\n"], - "mappings": "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", + "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;", "names": [] } diff --git a/oldgame.js b/oldgame.js deleted file mode 100644 index 895f09e8a27b13db8fc4d33d2af66b491f263329..0000000000000000000000000000000000000000 --- a/oldgame.js +++ /dev/null @@ -1,1395 +0,0 @@ -/* - For js13k 2022 - Theme: death -*/ - -// CONFIG -let id = 0; -const pi = Math.PI; -let pause = 0; -let lP = "bye_dbh_" // this is for JS13K's shared localStorage -const gPar = (key) => { - - // Address of the current window - let address = window.location.search - - // Returns a URLSearchParams object instance - let parameterList = new URLSearchParams(address) - - // Returning the respected value associated - // with the provided key - return parameterList.get(key) -}; - -let lS = localStorage - -let gS = (o) => { - return lS.getItem(o); -} -let sS = (o, v, ops={}) => { - if (ops.c) { // compression - v = lzs.compress(v); - } - lS.setItem(`${lP}${o}`, `${v}`); -} -let o = { -} - -for (let i of Object.keys(lS)) { - console.log(i) - if (i.startsWith(`${lP}o_`)) { - switch(gS(i)) { - case "true": - o[i.slice(10)] = 1 - break; - case "false": - o[i.slice(10)] = 0 - break; - } - break; - } -} - -const cLV = gPar("lv"); - -class Canvas { - constructor(id) { - this.c = document.getElementById(id); - this.ctx = this.c.getContext('2d'); - this.w = this.c.width; - this.h = this.c.height; - // get the width and height of the canvas from CSS - this.tW = this.c.offsetWidth; - this.tH = this.c.offsetHeight; - this.cam = {x: 0, y: 0}; - - this.mPos = {x: 0, y: 0}; - - } - - fill(color) { - this.ctx.fillStyle = color; - this.ctx.fillRect(0, 0, this.w, this.h); - } - - // Mouse position crap - gMP(evt) { - const rect = this.c.getBoundingClientRect(), // abs. size of element - scaleX = this.c.width / rect.width, // relationship bitmap vs. element for x - scaleY = this.c.height / rect.height; // relationship bitmap vs. element for y - - this.mPos.x = ((evt.clientX - rect.left) * scaleX) + this.cam.x; - this.mPos.y = ((evt.clientY - rect.top) * scaleY) + this.cam.y; - - return { - x: (evt.clientX - rect.left) * scaleX, // scale mouse coordinates after they have - y: (evt.clientY - rect.top) * scaleY // been adjusted to be relative to element - } - } - - translate(x, y) { - this.ctx.translate(x, y); - } - - rotate(angle) { - this.ctx.rotate(angle); - } - - // Drawing - dImg(img, x, y, w, h, direction=0, originx=x+w/2, originy=y+h/2) { - this.ctx.save(); - this.ctx.translate(originx-this.cam.x, originy-this.cam.y); - this.ctx.rotate(direction * pi/180); - this.ctx.drawImage(img, (-w/2), -h/2, w, h); - this.ctx.restore(); - } - sImg(img, x, y, w, h, cropX, cropY, cropW, cropH, direction=0) { - this.ctx.save(); - this.ctx.translate((x+w/2)-this.cam.x, (y+h/2)-this.cam.y); - this.ctx.rotate(direction * pi/180); - this.ctx.drawImage(img, cropX, cropY, cropW, cropH, -w/2, -h/2, w, h); - 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}`); - } - - dI(img, x, y, w, h, direction=0) { - // alias for drawImg - this.dImg(img, x, y, w, h, direction); - } - - dR(x, y, w, h, color="white") { - this.ctx.fillStyle = color; - this.ctx.fillRect(x-this.cam.x, y-this.cam.y, w, h); - } - - sR(x, y, w, h, color) { - this.ctx.strokeStyle = color; - this.ctx.strokeRect(x-this.cam.x, y-this.cam.y, w, h); - } - - dT(string, x, y, scaley, scalex, color, align="", vAliign="top") { - string = string.toUpperCase(); - let chars = string.split(""); - // console.log(chars); - - let charWidth = 7 - let charOff = 0; - // check if there's an odd number of chars - if (chars.length % 2 == 1) { - charOff = 1 - } - let strLength = (chars.length * charWidth - charOff) * scalex; - - let charHeight = 7 - let strHeight = (charHeight * scaley); - if (strHeight % 2 == 1) { - strHeight += 1; - } - - if (align === "middle") { - x = x - strLength / 2; - } else if (align === "end") { - x = x - strLength; - } - - switch(vAliign) { - case "middle": - case "center": - y = y - strHeight/2; - break; - case "bottom": - y = y - strHeight; - break; - } - - - let charI = 0; - let nextOffset = (7 * scalex); - for (let char of chars) { - - this.ctx.fillStyle = color; - let row = 0; - let col = 0; - // offset is the amount of pixels to offset the character by. you can calculate this by multiplying the current character (they're all the same size) by the scalex and scaley - let offset = nextOffset; - - char = fI[char]; - if (char == undefined) { - // leave a blank space - } else { - for (let cRow in char) { - col = 0; - // for each pixel in the row - for (let c of char[cRow]) { - if (c == 1) { - this.ctx.fillRect((x + (col * scalex) + (charI * offset)) - this.cam.x,( y + (row * scaley)) - this.cam.y, scalex, scaley); - - } - col++; - } - row++; - } - } - - charI++; - // console.log(charI) - - nextOffset = (7 * scalex); - - } - - return { - "w": strLength, - "h": strHeight - } - - } - - sC(x, y) { - this.cam.x = x; - this.cam.y = y; - } - -} - -// Entity classes - -class Entity { - constructor(name, x, y, sprite=undefined) { - this.name = name, - this.x = x, - this.y = y, - this.sprite = sprite - } - step() { - console.log(`${this.name} is stepping`); - console.log(`${this.name} is at ${this.x}, ${this.y}`); - } - draw () { - - } - intersects(other) { - return (this.x < other.x + other.w && this.x + this.w > other.x && this.y < other.y + other.h && this.y + this.h > other.y) - } -} - -class Room { - constructor(name) { - this.id = id; - id += 1; - this.name = name; - this.objects = []; - this.background = "#111"; - this.w = c.w; - this.h = c.h; - } - - spawn(entity) { - this.objects.push(entity) - } - - step() { - // step all objects in the room - for (let obj of this.objects) { - obj.step(); - } - } - - draw() { - // draw all objects in the room - for (let obj of this.objects) { - obj.draw(); - } - } - - dGUI() { - - } - - kD(key) { - // console.log(key); - } - kH(key) { - // console.log(key); - } - click(x, y) { - // console.log(x, y); - } - - start() { - c.cam = {x:0,y:0} - } - -} - -// INIT CANVAS -let c = new Canvas('gameCanvas'); -gameCtx = c.ctx; -c.fill("#151f1f"); -gameCtx.imageSmoothingEnabled = false; - -let dPM = _=>{ - c.ctx.globalAlpha = .7; - c.fill("#000"); - c.ctx.globalAlpha = .85; - c.dR((c.w/2-150)+c.cam.x,120+c.cam.y, 300,120, "#000") - c.ctx.globalAlpha = 1; - c.dT("paused", c.w/2+c.cam.x,c.h/2-30+c.cam.y, 3,3,"#fff", "middle","middle"); - for (let o in gameRoom.pseo) { - let t = c.dT(gameRoom.pseo[o].t, c.w/2+c.cam.x, c.h/2+(o*9)+c.cam.y, 1,1,"#fff", "middle"); - if (o == gameRoom.pses) { - let a = img.a; - c.dImg(a, ((c.w/2+c.cam.x)-t.w/2)-8, c.h/2+(o*9)+c.cam.y, 6,7) - } - } -} - -c.dT("Death By Hamster", c.w / 2, c.h / 2 - 40, 2, 2, "white", "middle"); - -// Load images -let img = { - "ingame": "./aimerthing.png", - "cursor": "./cursor.png", - "tileset": "./t.png", - "human": "./human.png", - "car": "./hamster.png", - "gun": "./gun.png", - "a": "./arw.png" // arrow -}; - -let snd = { - select: "./blip.mp3" -} - -const beep = _=> { - snd.select.pause() - snd.select.currentTime = 0; - snd.select.play() -} - -let loader = new Room("loader"); -let lText = "Loading..."; -let lErrT; -let lErr; -loader.dGUI = () => { - c.dT(lText, c.w / 2, c.h / 2, 2, 2, "white", "middle"); - if (lErr) { - c.dT(lErrT, c.w / 2, c.h / 2 + 20, 1, 1, "red", "middle"); - } -} -let rooms = []; - -let limg = 0; -let timg = 7; - -lText = `Loading...`; - -// after all images are loaded, and no errors occured, start the game -for (let key in img) { - // attempt to load the image - let IMG = new Image(); - IMG.addEventListener('load', () => { - limg++; - lText = `Loading images (${limg} / ${timg})` - }); - IMG.addEventListener('error', (e) => { - lErr = 1; - lErrT = `Error loading image ${e.target.src}`; - } ); - IMG.src = img[key]; - - // add the image to the images object - img[key] = IMG; -} - -console.log("SAAA") - -let lsnd = 0; -let tsnd = 0; - -for (let key in snd) { - let SND = new Audio(snd[key]); - // SND.addEventListener('load', () => { - // lsnd++; - // lText = `Loading sounds (${lsnd} / ${tsnd})` - // if (lsnd == tsnd) { - // console.log("Finish") - // } - // }); - // SND.addEventListener('error', (e) => { - // lErr = 1; - // lErrT = `Error loading image ${e.target.src}`; - // } ); - - // add the image to the images object - snd[key] = SND; -} - -loader.step = _=> { - setRoom(1) -} - -let levels = [ - /*tutorial*/ "NrCMBoIJgXXNLgMx2AVkQFlRXqrgAMy+R4s8BxoqmZhqGED8GVjyRHdL64AbF1bgMvDAHYhwQQA4p0EvAhJFCOiiUDVESRoSSK+yDi01NbE4MMQMem8c10za8qgCc5V5pXXOzhc6SxNjCBCEIcnbgcuE64LHgHglyaCYeqZqSGQgeUXK+ggmgwSbFIqXE-GkCJnJV8IKC2YKS9dIi8ahZ5fCtNb3RDu0egYle7WWGkmV6tp3CdOEEkuIcoMprBHqCoCp6BLvzwAcq2Sf9x5Aqq5RX0aSHbqSSMs+JXeDTvHKHxZuQYkguEBZT+whGgJ+vAgclemjkT3hn2qNwQZVRuB0pQgbUxRzx2VwYVKBFxkAIhPJyM0oAIcLRBERaIgTLx9NBxHZYy5HlZAPeMCAA", - /* lv 2 */ "NrCsBoGZwBgXXMALLWCzgIxsRATDsAJzgF7qYEry7gQ0YBshEA7C+ABwckMSYwOmbAwKDMFKFknRyibAQnys05QvRVVwTXO2pdzSoQFZBuxYo1YyVo5EnCoD9WuxLgb8ZJLvsJXX5OaoL2ytyhHuDsEdgQMeDM8fiS7AFcpBQozGn8koYpWtjcvlLxBPHYEWJBeigR7ChI6BAQTbTMbRgNzemdECR9puCDjp2ORp2G0J2K0KC20IxWgjOo84jVSxtY0Kw9Rpz7RfuCh7QkZxjcl2xcPRA3Ko8EjyiP0I-Ml8Uf6OynyyiMmGkioDhBajooKhyjmkhQ60ii3hCRRe1hQOUBHRkQIW0i2HxbkxkRaeRhkXYiKKFMC1KG9McjIIzNRamxzhJni5QyJ4BIfJIOJpwvSfPY4p5zGFgmYfIg8q5KEcSzgQA", - /* lv 3 */ "NrCMBoCZwFgXXGcBmcBWBTX0dCPg91MiA2E8CAdkwlRsTRXAA5Mno3ErLXapiiCNHJCBDMDGaiwwlBWiQFlTFOgAGds02NY4HcCZMDTUvq0tzunifABOK4crrHTUBFugNW0Kk9SDM1BjTB4pAiNYLTMI8DD+HgxEenAZdzi+XSkuJyYc00zCDIl8OP4pNJdTcsEkI34zJKQzNNSysSYS5lB+VCVdVGRMGPkxNRqejpUhKqh+JiGxM0XmuaXppB5JzbWdlYhLfdZdg42IB23zk-tRpFAXI-vrp8vKCFf0x6r+L3a7itoLlAUiaeSiugcsSeUPwPmgBCC2AovlqKJRMjcqHyb0Kbk4Phc2Ic2MsOVOTVOXUsrQcFJuNL+7xcJSBLla6XZ1B+Im5qJxdJRXWBfzc2UwlmChTwnWR1VwlHq8slBGlu1VK1V2wuwl5H0UvMegx+fWNZ0oUg+Uke4zEwoI70s-SKoB4TrwWx+Ep+F2RPEeh29tyCkORtNClAcGKgbK0kAgUcgPLEib5KftUGgRxTTo4eH4kCB4bjZuLFItDidwhclejtyr4KQBdR1dhybo+Z1iEdqBkZkgqG2ihN8v7tyHDfHoMolgIW3iiAtNmGUA8PksngctibBgt8jgQA" -] - -hamsterRef = { - "file": img.car, - "nl": { - "x": 0, - "y": 0, - "w": 32, - "h": 16, - } -} - -let lRef = { - "file": img.tileset, - "default": { - "x": 0, - "y": 0, - "w": 32, - "h": 32, - "type": "blank" - }, - "tiles": [ - { - }, - { - "x": 32, - "type": "floor" - }, - { - "x": 64, - "type": "wall" - }, - { - "x": 96, - "type": "wall" - - }, - { - "x": 128, - "type": "wall" - }, - { - "x": 160, - "type": "wall" - }, - { - "x": 192, - "type": "wall" - }, - { - "x": 224, - "type": "wall" - }, - { - "x": 256, - "type": "wall" - }, - { // player - "x": 32, - "type": "floor" - }, - { // human - "x": 32, - "type": "floor" - }, - { - "x": 352, - "type": "wall" - }, - { - "x": 384, - "type": "wall" - }, - { - "x": 416, - "type": "wall" - }, - { - "x": 448, - "type": "vent" - }, - ] -} - - -for (let tile of lRef.tiles) { - // if the tile is missing properties from the default, add them - for (let key in lRef.default) { - if (!tile[key]) { - tile[key] = lRef.default[key]; - } - } -} - -console.debug(img) -let targFPS = 60; -let frame = 0; - - -let menu = new Room("menu"); - -menu.s = 0 -menu.o = [ - { - "t": "Play", - "a": _=>{ setRoom(4) } // go to game room - }, - { - "t": "Editor", - "a": _=>{ setRoom(3) } // go to level editor - }, - { - "t": "Settings", - "a": _=>{ setRoom(5) } - } -] - - -menu.dGUI = () => { - c.dT("Death by Hamster", c.w/2, c.h/2-25, 4, 4, "white", "middle", "middle"); - c.dT("W/Up or S/Down to select", c.w/2, c.h/2, 1,1,"gray","middle","middle"); - c.dT("Space or ENTER to activate", c.w/2, c.h/2+8, 1,1,"gray","middle","middle") - for (let o in menu.o) { - let txt = c.dT(`${menu.o[o].t}`, c.w/2, (c.h/2+50)+(o*20), 2,2,"#fff","middle","top"); - if (menu.s == o) { - let a = img.a; - let ap = ((c.w/2)-(txt.w/2))-a.width-4; - let ap2 = ((c.w/2)+(txt.w/2))+a.width-4; - c.dImg(a, ap, (c.h/2+50)+(o*20), a.width*2, a.height*2) - c.dImg(a, ap2, (c.h/2+50)+(o*20), a.width*2, a.height*2, 180) - } - } -} -const nextRoom = () => { - // move to the next room - roomI++; - if (roomI >= rooms.length) { - roomI = 0; - } - cRoom = rooms[roomI]; - cRoom.start(); -} - -const prevRoom = () => { - // move to the previous room - roomI--; - if (roomI < 0) { - roomI = rooms.length - 1; - } - cRoom = rooms[roomI]; - cRoom.start(); -} - -const setRoom = (roomI) => { - // set the current room to the given room - cRoom = rooms[roomI]; - cRoom.start(); -} -menu.kD = (key) => { - switch (key) { - case "ArrowUp": - case "KeyW": - menu.s -= 1 - if (menu.s < 0) { - menu.s = menu.o.length - 1 - } - beep() - break; - case "ArrowDown": - case "KeyS": - menu.s += 1 - if (menu.s > menu.o.length - 1) { - menu.s = 0 - } - beep() - break; - case "Space": - case "Enter": - menu.o[menu.s].a(); - break; - } -} - -let gameRoom = new Room("Game"); -let lvlS = new Room("Level Select") -lvlS.s = 0 -lvlS.o = levels -gameRoom.humans = 0 -gameRoom.li = 0 -let player = new Entity("Player", 0,0); -player.speed = 0; -player.maxSpeed = 20; -player.direction = 0; -player.sprite = img.car; -player.crop = hamsterRef.nl; -player.x = 0; -player.y = 0; -player.w = player.crop.w*2; -player.h = player.crop.h*2; -gameRoom.o = [{t:"Next Level",a:_=>{lvlS.s += 1; lvlS.kD("Space"); gameRoom.tutorial=0}}, {t:"Level Select",a: _=>{setRoom(4)}}, {t:"Menu", a: _=>{setRoom(1)}}] - -gameRoom.s = 0 -gameRoom.pseo = [{t:"Back to Menu", a:_=>{setRoom(1)}},{t:"Level Select",a:_=>{setRoom(4)}}] -gameRoom.pses = 0 -player.oldDir = 0; - -player.step = _=> { - // move in this.direction, which is an angle in degrees - player.tooltip = "" - player.x += player.speed * Math.cos(player.direction * pi / 180); - player.y += player.speed * Math.sin(player.direction * pi / 180); - // check that the player won't go into a wall on the next step, and if so, stop. - player.checkpoints = []; - for (let i = 0; i < 9; i++) { - - let carCx = player.x + player.w/2; - let carCy = player.y + player.h/2; - - let pointOx = 0; - let pointOy = 0; - switch (i) { - case 0: - pointOx = -32; - break; - case 1: - pointOx = 32; - break; - case 2: - pointOx = -30; - pointOy = -15; - break; - case 3: - pointOx = -30; - pointOy = 15; - break; - case 4: - pointOx = 30; - pointOy = -15; - break; - case 5: - pointOx = 30; - pointOy = 15; - break; - case 6: - pointOx = 20; - break; - case 7: - pointOx = -20; - } - - let pointX = carCx - pointOx * Math.cos(player.direction * pi / 180) - pointOy * Math.sin(player.direction * pi / 180); - let pointY = carCy - pointOx * Math.sin(player.direction * pi / 180) + pointOy * Math.cos(player.direction * pi / 180); - - player.checkpoints.push({x: pointX, y: pointY}); - - } - - for (let checkpoint of player.checkpoints) { - let x = checkpoint.x / 64; - let y = checkpoint.y / 64; - if (gameRoom.checktile(x, y, "wall")) { - player.direction = player.oldDir; - player.x = player.xy[0]; - player.y = player.xy[1]; - player.speed *= 0.001; - } else if (gameRoom.checktile(x, y, "vent")) { - player.tooltip = "Press SHIFT to vent" - } - } - - // keep the camera centered on the player - c.sC(player.x - c.w/2, player.y - c.h/2); - - player.oldDir = player.direction; - player.xy = [player.x, player.y] - - player.speed *= .99 - if (player.speed < 0) { - player.speed *= .009 - } - -} - -console.log(player); - -player.draw = _=> { - // draw this.sprite at this.x, this.y - c.sImg(player.sprite, player.x, player.y, player.w, player.h, player.crop.x, player.crop.y, player.crop.w, player.crop.h, player.direction); - // c.dT(`${player.x/64} ${player.y/64}`, player.x, player.y, 1,1,"white","middle","middle"); - // canvas.strokeRect(player.x, player.y, player.w, player.h, "white"); - - let gun = img.gun; - let gunOx = 13; - let gunOy = 0; - - let carCx = player.x + player.w/2; - let carCy = player.y + player.h/2; - - // get gunx and guny by moving backwards (gunOx and gunOy) from the center of the car in this.direction - let gunx = carCx - gunOx * Math.cos(player.direction * pi / 180) - gunOy * Math.sin(player.direction * pi / 180); - let guny = carCy - gunOx * Math.sin(player.direction * pi / 180) + gunOy * Math.cos(player.direction * pi / 180); - player.gx = gunx - player.gy = guny - - if (!pause&&!gameRoom.finish) { - - // get the angle between the gun and the mouse - player.aim = Math.atan2(c.mPos.y - guny, c.mPos.x - gunx) * 180 / pi; - - } - - // canvas.drawText(`Width${gun.width} Height${gun.height}`, gunx, guny-15, 1, 1, "green", "middle", "middle"); - c.dImg(gun, gunx, guny, gun.width*2, gun.height*2, player.aim, gunx, guny); // these two vars at the end are where the gun's center is placed - // canvas.drawRect(gunx, guny, 1,1, "red"); - - if (player.tooltip) { - c.dT(player.tooltip, player.x+32, player.y-16, 1, 1, "#64bee3", "middle") - } - -} - -player.shoot = () => { - // shoot a bullet - let bullet = new Entity("Bullet", player.gx, player.gy); - bullet.speed = 20; - bullet.direction = player.aim; - bullet.w = 2; - bullet.h = 2; - - bullet.step = () => { - // for each step, check if it's path intersects with any other entity - for (let i = 0; i < cRoom.objects.length; i++) { - let ent = cRoom.objects[i]; - if (ent != bullet && ent.intersects(bullet)) { - // if it does, remove the bullet and the entity unless it's the player - console.log(ent); - if (ent != player) { - cRoom.objects.splice(i, 1); - cRoom.objects.splice(cRoom.objects.indexOf(bullet), 1); - gameRoom.humans -= 1; - return; - } - } - - } - if (gameRoom.checktile(bullet.x/64,bullet.y/64)) { - cRoom.objects.splice(cRoom.objects.indexOf(bullet), 1); - } - // if it doesn't, move the bullet - bullet.x += bullet.speed * Math.cos(bullet.direction * pi / 180); - bullet.y += bullet.speed * Math.sin(bullet.direction * pi / 180); - } - bullet.draw = () => { - c.dR(bullet.x, bullet.y, bullet.w,bullet.h, "#2f2f2f"); - } - cRoom.spawn(bullet); -} - - - -gameRoom.kD = (key) => { - if (!pause&&!gameRoom.finish){ - switch (key) { - case "ArrowUp": - case "KeyW": - player.speed += player.accel; - if (player.speed > player.maxSpeed) { - player.speed = player.maxSpeed; - } - break; - case "ArrowDown": - case "KeyS": - player.speed -= player.accel * .8 - if (player.speed < -player.maxSpeed) { - player.speed = -player.maxSpeed; - } - break; - case "ArrowLeft": - case "KeyA": - player.direction -= 2.5; - if (player.direction < 0) { - player.direction = 360; - } - break; - case "ArrowRight": - case "KeyD": - player.direction += 2.5; - if (player.direction > 360) { - player.direction = 0; - } - break; - case "Space": - player.shoot() - break; - case "ShiftLeft": - console.log("Triggered!") - for (let i = 0; i < 9; i++) { - let x = Math.floor(player.checkpoints[i].x / 64); - let y = Math.floor(player.checkpoints[i].y / 64); - - for (let tile of gameRoom.level) { - if (lRef.tiles[tile[0]].type == "vent" && tile[1] == x && tile[2] == y) { - for (let tile of gameRoom.level) if (lRef.tiles[tile[0]].type == "vent" && !(tile[1] == x) && !(tile[2] == y)) { - console.log(x, y) - console.log(tile[1], tile[2]) - player.x = tile[1] * 64; - player.y = tile[2] * 64 + 16; - } - } - } - - } - break; - } - - } - if (!gameRoom.finish ) { - if (key == "KeyP" || key == "Escape") { - pause = !pause - } - if (pause) { - switch (key) { - case "ArrowUp": - case "KeyW": - gameRoom.pses -= 1 - if (gameRoom.pses < 0) { - gameRoom.pses = gameRoom.pseo.length - 1 - } - beep(); - break; - case "ArrowDown": - case "KeyS": - gameRoom.pses += 1 - if (gameRoom.pses > gameRoom.pseo.length - 1) { - gameRoom.pses = 0 - } - beep(); - break; - case "Space": - case "Enter": - pause = 0; - gameRoom.tutorial = 0; - gameRoom.pseo[gameRoom.pses].a() - break; - } - } - } - if (gameRoom.finish) { - switch (key) { - case "ArrowUp": - case "KeyW": - gameRoom.s -= 1 - if (gameRoom.s < 0) { - gameRoom.s = gameRoom.o.length - 1 - } - beep(); - break; - case "ArrowDown": - case "KeyS": - gameRoom.s += 1 - if (gameRoom.s > gameRoom.o.length - 1) { - gameRoom.s = 0 - } - beep(); - break; - - case "Space": - case "Enter": - gameRoom.finish = 0; - gameRoom.tutorial = 0; - gameRoom.o[gameRoom.s].a(); - break; - } - } -} -gameRoom.kH = (key) => { - if (!pause&&!gameRoom.finish){ - if (key == "ArrowUp" || key == "KeyW") { - player.speed += player.accel; - if (player.speed > player.maxSpeed) { - player.speed = player.maxSpeed; - } - } - if (key == "ArrowDown" || key == "KeyS") { - player.speed -= player.accel*1.1; - if (player.speed < -player.maxSpeed) { - player.speed = -player.maxSpeed; - } - } - if (key == "ArrowLeft" || key == "KeyA") { - player.direction -= 2.5; - if (player.direction < 0) { - player.direction = 360; - } - } - if (key == "ArrowRight" || key == "KeyD") { - player.direction += 2.5; - if (player.direction > 360) { - player.direction = 0; - } - } - } -} -gameRoom.click = (e) => { - if (!pause&&!gameRoom.finish){ - player.shoot(); - } -} -gameRoom.checktile = (tx, ty, tp) => { - tx = Math.floor(tx); - ty = Math.floor(ty); - for (let tile of gameRoom.level) { - if (lRef.tiles[tile[0]].type == tp && tile[1] == tx && tile[2] == ty) { - return true; - } - } - return false; -} - -gameRoom.start = () =>{ - - if (cLV) { - gameRoom.level = cLV - } - - if (gameRoom.li) { - gameRoom.level = JSON.parse(lzs.decompressFromEncodedURIComponent(levels[gameRoom.li - 1])) - } - - gameRoom.finish = 0; - - gameRoom.objects = []; - - - gameRoom.humans = 0; - gameRoom.spawn(player); - - - if (gameRoom.tutorial) { - player.accel = .05 - } else { - player.accel = .1 - - } - - - for (let tile of gameRoom.level) { - if (tile[0] === 9) { - player.x = (tile[1]*64)+32 - player.y = (tile[2]*64)+32 - } - if(tile[0]===10){ - - let pooman = new Entity("Human", (tile[1]*64),(tile[2]*64), img.human) - pooman.w = 26*2 - pooman.h = 16*2 - pooman.bh = Math.floor(Math.random()*3); - pooman.bb = Math.floor(Math.random()*3) - pooman.getT=_=>{ - pooman.tX = Math.floor((pooman.x + pooman.w) / 64) - pooman.tY = Math.floor((pooman.y + pooman.h) / 64) - } - pooman.step = _=>{ - let xy = [pooman.x, pooman.y] - if (pooman.timer<=0){ - let director = Math.floor(Math.random()*4) - pooman.direction = director*90; - if (director === 0){ - pooman.y -= pooman.h; - } - if (director === 1){ - pooman.x += pooman.w; - } - if (director === 2){ - pooman.y += pooman.w; - } - if (director === 3){ - pooman.x -= pooman.h; - } - pooman.getT(); - if (gameRoom.checktile(pooman.tX, pooman.tY)){ - pooman.x = xy[0] - pooman.y = xy[1] - pooman.step() - return - } - - pooman.timer = Math.floor(Math.random() * (120 - 60) ) + 60; - } - pooman.timer--; - } - pooman.draw = _=>{ - c.sImg(pooman.sprite, pooman.x, pooman.y, pooman.w, pooman.h, pooman.bb*pooman.w/2, 0, pooman.w/2, pooman.h/2, pooman.direction); - c.sImg(pooman.sprite, pooman.x, pooman.y, pooman.w, pooman.h, pooman.bh*pooman.w/2, pooman.h/2, pooman.w/2, pooman.h/2, pooman.direction); - // c.dT(`${pooman.timer} :: ${pooman.direction}`, pooman.x, pooman.y, 1, 1, "white", "middle", "middle"); - } - pooman.timer = 90; - gameRoom.spawn(pooman); - gameRoom.humans += 1; - } - } -} - -gameRoom.step = _=> { - if (lvlS.s+1 >= lvlS.o.length) { - gameRoom.o[0] = {t:"you killed them all!", a:_=>{alert("well done!!!")}} - } - if (!pause&&!gameRoom.finish) { - if (gameRoom.humans <= 0){ - gameRoom.tutorial = 0; - gameRoom.finish = true; - } - // step all objects in the room - for (let obj of gameRoom.objects) { - obj.step(); - } - } -} - -gameRoom.draw = _=> { - - for (let tile of gameRoom.level) { - // [index, x, y] - c.sImg(lRef.file, (tile[1]*32)*2, (tile[2]*32)*2, 32*2,32*2, lRef.tiles[tile[0]].x, 0, 32, 32); - } - - if (gameRoom.tutorial) { - c.dT("Welcome to", 3*64, 64+15, 1, 1, "black"); - c.dT("Death by Hamster", (3*64)+32, 64+25, 2,2, "black", "middle"); - - c.dT("Use WASD/arrows to move", 128, 2*64, 1,1, "black"); - c.dT("Aim with the mouse and click to shoot!", 128, 2*64+10, 1,1, "black"); - - c.dT("As a member of the hamster uprising,", 6*64, 3*64+25, 1,1, "black"); - c.dT("you might want to kill any humans", 6*64, 3*64+35, 1,1, "black"); - c.dT("you find!", 10*64-16, 3*64+45, 1,1, "black", "end"); - - - } - for (let i = 0; i < cRoom.objects.length; i++) { - cRoom.objects[i].draw(); - } -} - -gameRoom.dGUI = _=>{ - c.dT(`Humans:${gameRoom.humans}`, (c.w-10)+c.cam.x, 10+c.cam.y, 2,2,"#fff", "end") - - if (pause) { - dPM(gameRoom); - } - if (gameRoom.finish) { - c.ctx.globalAlpha = .7; - c.fill("#000"); - c.ctx.globalAlpha = .85; - c.dR((c.w/2-150)+c.cam.x,120+c.cam.y, 300,150, "#000") - c.ctx.globalAlpha = 1; - - - c.dT("You Won!", c.w/2+c.cam.x,c.h/2-30+c.cam.y, 3,3,"#fff", "middle","middle"); - for (let o in gameRoom.o) { - let t = c.dT(gameRoom.o[o].t, c.w/2+c.cam.x, c.h/2+(o*9)+c.cam.y, 1,1,"#fff", "middle"); - if (o == gameRoom.s) { - let a = img.a; - c.dImg(a, ((c.w/2+c.cam.x)-t.w/2)-8, c.h/2+(o*9)+c.cam.y, 6,7) - } - } - } -} - -let editor = new Room("Editor"); -editor.i = 0; -editor.t = lRef; -editor.l = [] -editor.saving = false -editor.sa = 0 - -editor.start = _=>{ - editor.dPos = [15,65] -} -editor.draw = _=>{ - for (let tile of editor.l) { - // [index, x, y] - c.sImg(lRef.file, (tile[1]*32)+editor.dPos[0], tile[2]*32+editor.dPos[1], 32,32, tile[0]*32, 0, 32, 32); - c.dR(editor.dPos[0], editor.dPos[1], 1,1, "red") - } -} -editor.step = _=>{ - if (editor.i < 0) { - editor.i = lRef.tiles.length-1; - } - if (editor.i > lRef.tiles.length-1) { - editor.i = 0; - } -} -editor.generate = _=>{ - editor.saving=1 - for (let tile of editor.l) { - if (tile[0] == 0){ - editor.l.splice(editor.l.indexOf(tile)) - } - } - let encodedLevel = lzs.compressToEncodedURIComponent(JSON.stringify(editor.l)) - console.log(encodedLevel); - if (encodedLevel != editor.data){ - document.getElementById("leveltext").innerText = encodedLevel; - document.getElementById("levelLink").innerHTML = `<a href="/?lv=${encodedLevel}&goto=2">Play</a>` - } - editor.data = encodedLevel; - editor.saving=0; - editor.sa = 1; -} -editor.click = (x,y)=>{ - if (y < 50) { - if (x> 516 && y < 50) { - if (!editor.saving){ - editor.generate() - } - editor.saveclick = true - } - } - else { - x = Math.floor((x-editor.dPos[0])/32) - y = Math.floor((y-editor.dPos[1])/32) - // console.debug(x,y) - for (let t in editor.l) { - if (editor.l[t][1] == x && editor.l[t][2] == y) { - editor.l[t] = [editor.i, x, y]; - return; - } - } - editor.l.push([editor.i,x,y]) - editor.sa = 0 - } -} -editor.kH = (key) => { - switch (key) { - case "KeyW": - case "ArrowUp": - editor.dPos[1] += 4 - break; - case "KeyS": - case "ArrowDown": - editor.dPos[1] -= 4 - break; - case "KeyA": - case "ArrowLeft": - editor.dPos[0] += 4 - break; - case "KeyD": - case "ArrowRight": - editor.dPos[0] -= 4 - break; - } -} - -editor.dGUI = _=>{ - c.dR(0,0,c.w,50,"gray") - c.dT(`DBH Editor::${editor.n}`, 15,25,2,2,"#fff","start","middle"); - let s = c.dT(`Save`, c.w-15,25,2,2,"#fff","end","middle"); - if (c.mPos.x > (c.w-30)-s.w && c.mPos.y < 50) { - // console.debug((c.w-30)-s.w) - c.dT(`Save`, c.w-15,25,2,2,"#e5e5e5","end","middle"); - } - if (editor.sa) { - c.dT(`Save`, c.w-15,25,2,2,"#1fdc2f","end","middle"); - } if (editor.saving) { - c.dT(`Save`, c.w-15,25,2,2,"#1fccdc","end","middle"); - } - - c.sImg(editor.t.file, c.mPos.x+16,c.mPos.y+16,32,32,32*editor.i,0,32,32); -} - - - -lvlS.dGUI = () => { - c.dT("Death by Hamster", c.w/2, 25, 2, 2, "white", "middle", "top"); - c.dT("Level Select", c.w/2, 44, 1,1,"gray","middle","middle"); - for (let o in lvlS.o) { - let n = parseInt(o)+1 - c.dT(`${n}`, (20)+(32*n), 70, 2, 2, "#fff", "middle", "middle") - if (o == lvlS.s) { - c.sR((20-14)+(32*n), 70-16, 32, 32, "#fff") - } - } -} - -lvlS.kD = (key) => { - switch (key) { - case "ArrowUp": - case "ArrowLeft": - case "KeyW": - case "KeyA": - lvlS.s -= 1 - if (lvlS.s < 0) { - lvlS.s = lvlS.o.length - 1 - } - beep(); - break; - case "ArrowDown": - case "ArrowRight": - case "KeyS": - case "KeyD": - lvlS.s += 1 - if (lvlS.s > lvlS.o.length - 1) { - lvlS.s = 0 - } - beep(); - break; - case "Space": - case "Enter": - gameRoom.li = lvlS.s + 1; - if (lvlS.s === 0) { - gameRoom.tutorial = 1; - } - setRoom(2) - // beep(); - break; - case "KeyE": - editor.l = JSON.parse(lzs.decompressFromEncodedURIComponent(lvlS.o[lvlS.s].data)); - setRoom(3) - break; - } -} - -let options = new Room("Settings") -options.s = 0 -options.ops = o; -options.o = [{ - "t": "Show FPS", - "a": _=>{ o.showFPS = !o.showFPS; sS("o_showFPS", o.showFPS) }, - "v": "showFPS" -}, { - "t": "Menu", - "a": _=>{ setRoom(0) } -}] - -options.dGUI = () => { - c.dT("Settings", c.w/2, 25, 2, 2, "#fff", "middle", "top"); - for (let o in options.o) { - let s = options.o[o] - let txt = c.dT(`${options.o[o].t}`, 150, 50+(o*20), 2,2,"#fff","left","top"); - if (options.s == o) { - let a = img.a; - c.dImg(a, 136, 50 + (o * 20), a.width * 2, a.height * 2) - } - let v = options.ops[s.v] - if (!(v==undefined)){ - c.dT(`${v}`, 450, 50+(o*20), 2,2,"#fff", "end"); - - } - } -} - -options.kD = (key) => { - switch (key) { - case "ArrowUp": - case "ArrowRight": - case "KeyW": - options.s -= 1 - if (options.s < 0) { - options.s = options.o.length - 1 - } - beep(); - break; - case "ArrowDown": - case "ArrowLeft": - case "KeyS": - options.s += 1 - if (options.s > options.o.length - 1) { - options.s = 0 - } - beep(); - break; - case "Space": - case "Enter": - if (options.o[options.s].a) { - options.o[options.s].a() - } - break; - } -} - - -rooms.push(loader); -rooms.push(menu); -rooms.push(gameRoom); -rooms.push(editor); -rooms.push(lvlS); -rooms.push(options) -let roomI = !gPar("goto") ? 0 : gPar("goto"); - -let cRoom = rooms[roomI]; - - -let keysPressed = {}; -let keysLastPressed = {}; - -document.addEventListener('keydown', (e) => { - keysPressed[e.code] = true; -}); -document.addEventListener('keyup', (e) => { - keysPressed[e.code] = false; - keysLastPressed[e.code] = false; -} ); - -let lastTime = 0; - -let mse = {x: 0, y: 0}; -let lastClick = {x: 0, y: 0}; -let leftclicked = false; -let rightclicked = false - -c.c.addEventListener('mousemove', (e) => { - mse = c.gMP(e); -} ); - -c.c.addEventListener("mousedown", (e) => { - // console.log(e); - e.preventDefault() - lastClick = c.gMP(e); - mse = c.gMP(e); - switch (e.button) { - case 0: // left - leftclicked=1; - break; - case 1: - rightclicked=1; - break; - } -}); -c.c.addEventListener("mouseup", (e)=>{ - // console.log(e); - lastClick = c.gMP(e); - mse = c.gMP(e); -}) -c.c.oncontextmenu = _=>{return 0;} - -window.onwheel = (e)=>{ - if (e.deltaY > 0) { - editor.i += 1; - } - if (e.deltaY < 0) { - editor.i -= 1; - } -} - -try { - cRoom.start(); - - setInterval(() => { - c.tW = c.c.offsetWidth; - c.tH = c.c.offsetHeight; - c.scale = c.tW / c.w; - frame++; - c.fill(cRoom.background); - - for (let key in keysPressed) { - if (keysPressed[key]) { - if (!keysLastPressed[key]) { - cRoom.kD(key); - keysLastPressed[key] = true; - } else if (keysLastPressed[key]) { - cRoom.kH(key); - } - } - } - if (leftclicked) { - cRoom.click(lastClick.x, lastClick.y); - leftclicked = 0; - } - - cRoom.step(); - cRoom.draw(); - cRoom.dGUI(); - - if (o.showFPS){ - c.dT(`FPS:${Math.round(1000 / (Date.now() - lastTime))}`, 0+c.cam.x, 0+c.cam.y, 1, 1, "#fafafa", "left", "top"); - } - - switch (cRoom.name) { - case "menu": - case "Editor": - c.ctx.drawImage(img.cursor, Math.round(mse.x), Math.round(mse.y), img.cursor.width*2, img.cursor.height*2); - break; - case "Game": - c.ctx.drawImage(img.ingame, Math.round(mse.x)-16, Math.round(mse.y)-16, 32, 32); - break; - } - lastTime = Date.now(); - - } , 1000/targFPS); // 60 fps - -} catch (error) { - c.fill("#1c1c1c"); - c.dT("Death By Hamster", c.w / 2, c.h / 2 - 40, 2, 2, "white", "middle"); - c.dT(`${error}`, c.w/2, c.h / 2, 1, 1, "red", "middle"); - c.dT(`pls let Bye know by emailing him via`, c.w /2, c.h / 2 + 40, 1, 1, "white", "middle"); - c.dT('bye at byecorps.com', c.w / 2, c.h / 2 + 60, 2, 2, "white", "middle"); -} diff --git a/src/img/hampsterfont.webp b/src/img/hampsterfont.webp index 1fc8acb9d553748de667895d9daf2221aac6d515..a8eddc36fba2ac0e66216ed0d11b68c0bdb77315 100644 Binary files a/src/img/hampsterfont.webp and b/src/img/hampsterfont.webp differ diff --git a/src/img/splash2.webp b/src/img/splash2.webp deleted file mode 100644 index 1d6f0d16def6ae932103ea032bb38c508d2c4648..0000000000000000000000000000000000000000 Binary files a/src/img/splash2.webp and /dev/null differ diff --git a/src/img/t.webp b/src/img/t.webp new file mode 100644 index 0000000000000000000000000000000000000000..19001b624dc205d7218b15f4329add8f99d6a529 Binary files /dev/null and b/src/img/t.webp differ diff --git a/src/js/canvas.js b/src/js/canvas.js index d797244e669b026174fd7050f342f3551a39dfb6..050541a96fe3baf21d403085b845212eac7cf7f2 100644 --- a/src/js/canvas.js +++ b/src/js/canvas.js @@ -1,51 +1,58 @@ // Holds canvas, context and adds endpoints for graphics -const floor = function (...args) { - return Math.floor(...args); -} - +import { pi } from "./utils.js" class Canvas { constructor(id="c", w=128, h=128) { this.canvas = document.getElementById(id); this.canvas.width = w; this.canvas.height = h; - this.context = this.canvas.getContext("2d"); - this.context.imageSmoothingEnabled = false; - this.context.textBaseline = "top"; + this.ctx = this.canvas.getContext("2d"); + this.ctx.imageSmoothingEnabled = false; + this.ctx.textBaseline = "top"; this.width = this.canvas.width; this.height = this.canvas.height; + + // camera + this.cX = 0; + this.cY = 0; } fill(c="black") { - this.context.fillStyle = c; - this.context.fillRect(0, 0, this.width, this.height); + this.ctx.fillStyle = c; + 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.context.drawImage(image, x, y, width, height); + this.ctx.drawImage(image, x-this.cX, y-this.cY, width, height); } - sliceImage(image, sx, sy, sw, sh, dx, dy, dw = sw, dh = sh) { - this.context.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh); + sliceImage(img, x, y, w, h, cropX, cropY, cropW, cropH, direction=0) { + // 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.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}`); } drawText(text, x, y, c="white", size=16, font="monospace") { - this.context.fillStyle = c; - this.context.font = `${size}px ${font}`; - this.context.fillText(text, x, y); + this.ctx.fillStyle = c; + this.ctx.font = `${size}px ${font}`; + this.ctx.fillText(text, x, y); } drawLine(x1, y1, x2, y2, c="white", w=1) { - this.context.strokeStyle = c; - this.context.lineWidth = w; - this.context.beginPath(); - this.context.moveTo(x1, y1); - this.context.lineTo(x2, y2); - this.context.stroke(); + this.ctx.strokeStyle = c; + this.ctx.lineWidth = w; + this.ctx.beginPath(); + this.ctx.moveTo(x1-this.cX, y1-this.cY); + this.ctx.lineTo(x2-this.cX, y2-this.cY); + this.ctx.stroke(); } } diff --git a/src/js/config.js b/src/js/config.js index 35cfb3dd3e4e14781eada74da917949295307a7f..2111b811d6f702d161b6ce33010975fba9dec0e2 100644 --- a/src/js/config.js +++ b/src/js/config.js @@ -2,7 +2,7 @@ const GAME_TITLE = "Untitled JS13K23 Game." -const WIDTH = 256; // pixels -const HEIGHT = 256; // pixels +const WIDTH = 160; // pixels +const HEIGHT = 144; // pixels export { GAME_TITLE, WIDTH, HEIGHT }; diff --git a/src/js/game.js b/src/js/game.js index feaaaafc7c6dc24b8e2835535ddacd08a4297570..3a1c55bad2cff59e1dc33d6fe229e4d15d4c4308 100644 --- a/src/js/game.js +++ b/src/js/game.js @@ -3,25 +3,47 @@ import { WIDTH, HEIGHT, GAME_TITLE } from "./config.js"; import { Canvas } from "./canvas.js"; import { TextRenderer } from "./text.js"; import { Room, Object } from "./objects.js"; -import { isKeyDown } from "./keyboard.js"; +import { whichKeyDown } from "./keyboard.js"; +import { convertTileToScreen } from "./utils.js"; + +console.debug(convertTileToScreen(1, 1)); let assets = { images: { splash: "../img/splash1.webp", - splash2: "../img/splash2.webp", - font: "../img/hampsterfont.webp" + font: "../img/hampsterfont.webp", + tiles: "../img/t.webp" + }, + spritesheets: { + player: [ + {x: 0}, // looking up + {x: 16}, // looking right + {x: 32}, // looking down + {x: 48} // looking left + ] + }, + tilesets: { + castle: [ + {x: 0, y: 0}, // ??? + {x: 16, y: 0}, // floor + ] } + } +let running = 1; + let currentFrame = 0; let targetFrames = 60; let lastFrameTime = performance.now(); -let startFrameTime = lastFrameTime; let rooms = []; +let debugStatuses = []; let canvas = new Canvas("c", WIDTH, HEIGHT); let text; + +let pressedLastFrame = []; canvas.fill("#222034"); let splash = new Image(); @@ -34,27 +56,44 @@ splash.onload = () => { console.log("font loaded") text = new TextRenderer(canvas, font); window.onerror = (e) => { + running = 0; text.throwPanic(e); } } } -class DebugEntity extends Object { - constructor() { +// Entity class is here becuase otherwise every entity would need the canvas passed into it +class Entity extends Object { + constructor(x=0, y=0, spritesheet=null, sprite=null) { super(); - this.x = 0; - this.y = 0; + this.x = x; + this.y = y; + this.sprite = sprite; + this.spritesheet = spritesheet; } draw() { - canvas.context.fillStyle = "red"; - canvas.context.fillRect(this.x, this.y, 10, 10); + canvas.drawImage(this.sprite, this.x, this.y); } } // Create all the game rooms +let roomIndex = 0; +let currentRoom = rooms[roomIndex]; -let loadingRoom = new Room("loading"); +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); +} + +const changeRoom = (index) => { + currentRoom = rooms[index]; + roomIndex = index; +} + +const loadingRoom = new Room("loading"); loadingRoom.updateStatus = (status) => { console.log(status); canvas.fill("#222034"); @@ -62,44 +101,118 @@ loadingRoom.updateStatus = (status) => { text.render(status, 0, 0); } -let debugRoom = new Room("debug"); +const debugRoom = new Room("debug"); debugRoom.draw = () => { - canvas.fill("#222034"); - canvas - for (let i = 0; i < debugRoom.objects.length; i++) { - debugRoom.objects[i].draw(); - } + canvas.fill("black"); } debugRoom.drawGUI = () => { - text.render("Welcome to the Debug Room,\nwe've got fun and games", 0, canvas.height-14); - text.render("Current Frame:" + currentFrame + `(~${Math.floor((currentFrame/targetFrames)*100)/100}sec)`, 0, canvas.height-21); + debugStatuses.push("Current Frame:"+currentFrame+`(~${Math.round((currentFrame/targetFrames)*100)/100} sec)`); +} +debugRoom.keyDown = (key) => { + if (key == "Escape") changeRoom(searchForRoom("menu")); +} + +const menuRoom = new Room("menu"); +let menuOptions = [ + {"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.draw = () => { + canvas.fill("black"); +} +menuRoom.drawGUI = () => { + text.render(GAME_TITLE, 8, 7*4); + for (let i = 0; i < menuOptions.length; i++) { + if (i == menuIndex) { + text.render(">", 8, 7*(i+5)); + } + text.render(menuOptions[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; + } + if (menuIndex >= menuOptions.length) menuIndex = 0; + if (menuIndex < 0) menuIndex = menuOptions.length-1; +} + +let currentLevelData = { + tiles: [ + {id: 1, x: 1, y: 1}, // floor at tile coords 1, 1 + {id:123, x: 2, y: 2}, + ] +} + +const gameRoom = new Room("game"); +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); + } + + } -let testObject = new DebugEntity(0, 0); -debugRoom.objects.push(testObject); rooms.push(loadingRoom); +rooms.push(menuRoom); +rooms.push(gameRoom); rooms.push(debugRoom); -let roomIndex = 0; -let currentRoom = rooms[roomIndex]; +currentRoom = rooms[roomIndex]; let main = () => { // main game loop + if (!running) return; requestAnimationFrame(main); - let now = performance.now(); let delta = now - lastFrameTime; if (delta < 1000 / targetFrames) return; currentFrame++; - + debugStatuses = []; + currentRoom.draw(); currentRoom.drawGUI(); - lastFrameTime = now; - + let currentKeys = whichKeyDown(); + for (let i = 0; i < currentKeys.length; i++) { + 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!"); + } + + for (let i = 0; i < debugStatuses.length; i++) { + text.render(debugStatuses[i], 0, canvas.height-7*(debugStatuses.length-i)); + } + + lastFrameTime = now; + } let init = () => { // begin loading all the assets. @@ -112,6 +225,8 @@ let init = () => { assets.images[image] = img; } } + console.log(assets.images); + console.log("Images loaded.") currentRoom.updateStatus("Loading complete!"); canvas.fill("#222034"); @@ -123,10 +238,6 @@ let init = () => { } window.onload = () => { - try { - document.title = GAME_TITLE; - init(); - } catch (e) { - text.throwPanic(e); - } + document.title = GAME_TITLE; + init(); } diff --git a/src/js/keyboard.js b/src/js/keyboard.js index 28acdadd02b173dde131fab92fcfd60d721ed310..e2402e93c84230562cb8f357e19f9a4b252b6c9d 100644 --- a/src/js/keyboard.js +++ b/src/js/keyboard.js @@ -21,7 +21,7 @@ const _releaseKey = code => delete KEYS[code]; addEventListener('keydown', e => { // prevent itch.io from scrolling the page up/down - // e.preventDefault(); + e.preventDefault(); if (!e.repeat) { KEYS[e.code] = performance.now(); diff --git a/src/js/loader.js b/src/js/loader.js deleted file mode 100644 index d0d3ba5e1b64df42fa385784131265e3389dad91..0000000000000000000000000000000000000000 --- a/src/js/loader.js +++ /dev/null @@ -1,17 +0,0 @@ -// Loads a list of assets and calls a callback when done -// Shows a loading screen while loading - -// Depends on canvas.js and text.js - -class Loader { - constructor(canvas, splashImg, fontRenderer) { - this.canvas = canvas; - this.splashImg = splashImg; - this.fontRenderer = fontRenderer; - this.assets = []; - this.loaded = 0; - this.total = 0; - this.loading = false; - this.callback = null; - } -} \ No newline at end of file diff --git a/src/js/objects.js b/src/js/objects.js index a3b8fafa4b7f5ba3a060295053e33b173bed22cd..9113a0b8b884f786697217ecfa32682b76ff9531 100644 --- a/src/js/objects.js +++ b/src/js/objects.js @@ -3,10 +3,12 @@ class Object { step() {} } + class Room extends Object { - constructor() { + constructor(name="") { super(); this.objects = []; + this.name = name; // needs to be unique, otherwise the searching code will just use the first one it finds. } draw() { @@ -19,6 +21,14 @@ class Room extends Object { } + keyDown(key) { + } + + keyUp(key) { + } + + + step() { for (let i = 0; i < this.objects.length; i++) { this.objects[i].step(); diff --git a/src/js/text.js b/src/js/text.js index 9494b099b6ffe5235cc282ef04052a4d91013fc8..ae8f2c9235bfb83e2c0187a8f46398e18d65b5c0 100644 --- a/src/js/text.js +++ b/src/js/text.js @@ -5,14 +5,15 @@ class TextRenderer { this.fontimg = fontimg; // MUST BE AN IMAGE OBJECT this.fontWidth = 7; this.fontHeight = 7; - this.fontChars = "abcdefghijklmnopqrstuvwxyz1234567890.,!?:;)(~"; + this.fontChars = "abcdefghijklmnopqrstuvwxyz1234567890.,!?:;)(~>"; this.canvas = canvas; } - drawLetter(letter, x, y) { + drawLetter(letter, x, y, substituteOK=0) { let index = this.fontChars.indexOf(letter.toLowerCase()); if (index == -1) { - return; + if (!substituteOK) return; + this.canvas.drawText(letter, x, y, "#ffffff", 7, "monospace"); } let sx = index * this.fontWidth; let sy = 0; @@ -22,7 +23,7 @@ class TextRenderer { if (letter == ",") { yOffset = -1; } - this.canvas.sliceImage(this.fontimg, sx, sy, this.fontWidth, this.fontHeight, x, y-yOffset, this.fontWidth, this.fontHeight); + this.canvas.sliceImage(this.fontimg, x, y + yOffset, this.fontWidth, this.fontHeight, sx, sy, this.fontWidth, this.fontHeight); } render(text, x, y) { @@ -44,6 +45,7 @@ class TextRenderer { // It'll show the error on-screen. this.canvas.fill("#00000080") // 50% black + this.render(err, 0, 0); throw err; } diff --git a/src/js/utils.js b/src/js/utils.js new file mode 100644 index 0000000000000000000000000000000000000000..4d75fb04c9da82e5c642b881c9ea26091db79a1c --- /dev/null +++ b/src/js/utils.js @@ -0,0 +1,5 @@ +// random shit + +export const pi = Math.PI; + +export const convertTileToScreen = (x, y) => ({x: x<<4, y: y<<4});