diff --git a/README.md b/README.md index d93c89c..cc5cf9b 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,4 @@ - Tetris +- Maze diff --git a/src/game-maze-context2d.html b/src/game-maze-context2d.html new file mode 100644 index 0000000..3b17aa1 --- /dev/null +++ b/src/game-maze-context2d.html @@ -0,0 +1,46 @@ + + + + + Game: Maze + + + + + + + + + +
+ + Willem Games +
+ +
+

Game: Maze

+ +
Loading ...
+ +

How to:

+

Use the arrow keys up, down, left and right to move, or [W], [S], [A] and [D].
+ Press spacebar or enter, to select.

+ + + + + +
+ + + + + + + + diff --git a/src/games-launcher.html b/src/games-launcher.html index d0b2b46..cec7dcb 100644 --- a/src/games-launcher.html +++ b/src/games-launcher.html @@ -40,6 +40,7 @@ + diff --git a/src/index.html b/src/index.html index b7cf6d5..ec8d2af 100644 --- a/src/index.html +++ b/src/index.html @@ -25,6 +25,7 @@ diff --git a/src/js/game-maze-context2d.js b/src/js/game-maze-context2d.js new file mode 100644 index 0000000..c5194b2 --- /dev/null +++ b/src/js/game-maze-context2d.js @@ -0,0 +1,692 @@ +// this uses the Backtracking Algorithm to generate a maze +class Maze_CTX2D { + static get DEBUG_PARENT_OBJECT() { return true; }; + static get AUTO_CONTINUE_ON_FOCUS() { return false; }; + static get SHOW_FPS_INTERVAL() { return 1000 / 4; }; // four times per second + + static get KEY_ARROW_UP() { return 'ArrowUp'; }; + static get KEY_ARROW_DOWN() { return 'ArrowDown'; }; + static get KEY_ARROW_LEFT() { return 'ArrowLeft'; }; + static get KEY_ARROW_RIGHT() { return 'ArrowRight'; }; + static get KEY_W() { return 'w'; }; + static get KEY_S() { return 's'; }; + static get KEY_A() { return 'a'; }; + static get KEY_D() { return 'd'; }; + static get KEY_ENTER() { return 'Enter'; }; + static get KEY_SPACEBAR() { return ' '; }; + static get KEY_ESCAPE() { return 'Escape'; }; + + static get STATE_MENU() { return 0; }; + static get STATE_MAP_GEN_ANIM() { return 1; }; + static get STATE_PLAYING() { return 2; }; + static get STATE_ENDED() { return 3; }; + static get ANIMATION_DELAY() { return 10; }; + + animationRequestId = 0; + running = false; + + constructor(divId, width, height, difficulty = 1, showFps = false) { + this.createCanvas(divId, width, height); + this.canvasEl.title = "Playing: Maze"; + this.ctx = this.canvasEl.getContext("2d"); + this.ctx.textAlign = "center"; + + this.audioCtx = new AudioContext(); + this.sounds = []; + this.sounds[0] = new Audio('./snd/short-success.mp3'); + this.audioCtx.createMediaElementSource(this.sounds[0]).connect(this.audioCtx.destination); + this.sounds[1] = new Audio('./snd/footstep.mp3'); + this.audioCtx.createMediaElementSource(this.sounds[1]).connect(this.audioCtx.destination); + this.sounds[2] = new Audio('./snd/glass-knock.mp3'); + this.audioCtx.createMediaElementSource(this.sounds[2]).connect(this.audioCtx.destination); + this.sounds[3] = new Audio('./snd/flash.mp3'); + this.audioCtx.createMediaElementSource(this.sounds[3]).connect(this.audioCtx.destination); + this.sounds[4] = new Audio('./snd/metronome.mp3'); + this.audioCtx.createMediaElementSource(this.sounds[4]).connect(this.audioCtx.destination); + + this.showFps = showFps; + if (showFps) { + // add framecounter and fps variables and html + this.frameCounter = 0; + this.initTime = performance.now(); + const el = document.createElement('p'); + this.frameLabel = document.createElement('span'); + const floatRight = document.createElement('span'); + floatRight.style = "float: right;"; + this.fpsCounter = 0; + this.fpsLabel = document.createElement('span'); + el.appendChild(document.createTextNode('fps: ')); + el.appendChild(this.fpsLabel); + floatRight.appendChild(document.createTextNode('frame: ')); + floatRight.appendChild(this.frameLabel); + el.appendChild(floatRight); + this.divEl.appendChild(el); + } + + this.setDifficulty(difficulty); + this.gameState = Maze_CTX2D.STATE_MENU; + this.canvasEl.addEventListener("keydown", (e) => this.onKeyDown(e)); + this.canvasEl.addEventListener("keyup", (e) => this.onKeyUp(e)); + this.canvasEl.addEventListener("blur", (e) => this.onBlur(e)); + this.canvasEl.addEventListener("focus", (e) => this.onFocus(e)); + + if (Maze_CTX2D.DEBUG_PARENT_OBJECT) window.game = this; + if (typeof WTerminal === "function") { + WTerminal.terminalAddCommand("restartgame", (t) => this.terminalRestartGame(t)); + WTerminal.terminalAddCommand("printgame", (t) => t.printVar(this, "maze")); + WTerminal.printLn("new Maze: @", divId, ' ', width, 'x', height, ' difficulty=', difficulty, ' showFps=', showFps); + } + + this.drawCanvas(); + } + + newGame(gridWidth, gridHeight) { + gridWidth -= gridWidth % 2; gridWidth++; + gridHeight -= gridHeight % 2; gridHeight++; + + this.gridWidth = gridWidth; + this.gridHeight = gridHeight; + this.gridCellWidth = this.width / gridWidth; + this.gridCellHeight = this.height / gridHeight; + this.gridCellSize = Math.floor(Math.min(this.gridCellWidth, this.gridCellHeight)); + this.mapWidth = gridWidth * this.gridCellSize; + this.mapHeight = gridHeight * this.gridCellSize; + this.mapOffsetX = (this.width - this.mapWidth) / 2; + this.mapOffsetY = (this.height - this.mapHeight) / 2; + + var result = this.generateBacktrackingMaze(gridWidth, gridHeight); + this.maze = result.maze; + this.animation = result.animation; + this.animationPos = 0; + this.animationStepTime = 0; + this.animationStepDelay = Maze_CTX2D.ANIMATION_DELAY / this.animation.length; + this.player = { x: 1, y: 0 }; + // console.log("animation", this.animation); + } + + playSound(index) { + try { + const snd = this.sounds[index]; + snd.currentTime = 0; + snd.play(); + } catch (e) { + console.log(`Failed to play sound '${index}': ${e}}`) + } + } + + createCanvas(divId, width = 0, height = 0, zoom = 1) { + this.divEl = document.getElementById(divId); + if (this.divEl === null) throw new Error("elementId not found: " + divId); + while (this.divEl.firstChild) { + this.divEl.removeChild(this.divEl.lastChild); + } + this.canvasEl = this.divEl.appendChild(document.createElement("canvas")); + const c = this.canvasEl; + this.width = width; + this.height = height; + if (width > 0 && height > 0) { + c.style.width = width * zoom + 'px'; + c.style.height = height * zoom + 'px'; + c.width = width; + c.height = height; + } + c.tabIndex = 0; // improtant for keyboard focus! + // c.style.imageRendering = 'pixelated'; + } + + drawCanvas() { + if (this.showFps) { + this.frameCounter++; + this.fpsCounter++; + + const now = performance.now(); + const diff = now - this.initTime; + if (Maze_CTX2D.SHOW_FPS_INTERVAL < diff || !this.running) { + this.initTime = now; + const seconds = diff / 1000; + const fps = this.fpsCounter / seconds; + this.fpsCounter = 0; + if (this.frameLabel) this.frameLabel.innerHTML = this.frameCounter; + if (this.fpsLabel) { + this.fpsLabel.innerHTML = Math.round((fps + Number.EPSILON) * 100) / 100; + if (!this.running) this.fpsLabel.innerHTML += " (not running)"; + } + } + } + const ctx = this.ctx; + ctx.clearRect(0, 0, this.width, this.height); + + if (this.gameState == Maze_CTX2D.STATE_MENU) { + ctx.strokeStyle = 'black'; + ctx.fillStyle = "#4080ff"; + const FONT_SIZE = this.width > 440 ? 24 : 16; + const x = this.width / 2; + const y = this.height / 2; + let text = "Menu"; + ctx.font = "bold " + (2 * FONT_SIZE) + "px serif"; + ctx.fillText(text, x, y - 3 * FONT_SIZE); + ctx.strokeText(text, x, y - 3 * FONT_SIZE); + + ctx.font = "bold " + FONT_SIZE + "px mono"; + ctx.fillStyle = "#b0b0b0"; + text = `Difficulty: ${this.difficulty}`; + ctx.fillText(text, x, y + 0 * FONT_SIZE); + ctx.strokeText(text, x, y + 0 * FONT_SIZE); + text = `Size: ${this.gridWidth} x ${this.gridHeight}`; + ctx.fillText(text, x, y + 2 * FONT_SIZE); + ctx.strokeText(text, x, y + 2 * FONT_SIZE); + if (this.isFocused) return; + this.drawBanner("Click here to continue. (unfocused)"); + } else if (this.gameState == Maze_CTX2D.STATE_MAP_GEN_ANIM) { + ctx.fillStyle = "#404040"; + ctx.fillRect(this.mapOffsetX, this.mapOffsetY, this.mapWidth, this.mapHeight); + ctx.fillStyle = "blue"; + for (let i = 0; i < this.animationPos; i++) { + const animationStep = this.animation[i]; + if (animationStep.state == 0) { + //clearRect + ctx.clearRect(this.mapOffsetX + animationStep.x * this.gridCellSize, + this.mapOffsetY + animationStep.y * this.gridCellSize, this.gridCellSize, this.gridCellSize); + } else { + //fillRect + // ctx.fillRect(this.mapOffsetX + animationStep.x * this.gridCellSize, + // this.mapOffsetY + animationStep.y * this.gridCellSize, this.gridCellSize, this.gridCellSize); + ctx.beginPath(); + ctx.arc(this.mapOffsetX + (0.5 + animationStep.x) * this.gridCellSize, + this.mapOffsetY + (0.5 + animationStep.y) * this.gridCellSize, + this.gridCellSize / 2, 0, Math.PI * 2); + ctx.fill(); + } + } + if (!this.running) { + if (this.isFocused) + this.drawBanner("press space to continue. (paused)"); + else + this.drawBanner("Click here to continue. (unfocused)"); + } + } else {// Maze_CTX2D.STATE_ENDED & Maze_CTX2D.STATE_PLAYING + ctx.fillStyle = "#505050"; + for (let i = 0; i < this.maze.length; i++) { + for (let j = 0; j < this.maze[i].length; j++) { + if (this.maze[i][j] != 0) { + ctx.fillRect(this.mapOffsetX + j * this.gridCellSize, + this.mapOffsetY + i * this.gridCellSize, this.gridCellSize, this.gridCellSize); + } + } + } + ctx.fillStyle = "red"; + ctx.beginPath(); + ctx.arc(this.mapOffsetX + (0.5 + this.player.x) * this.gridCellSize, + this.mapOffsetY + (0.5 + this.player.y) * this.gridCellSize, + this.gridCellSize / 2, 0, Math.PI * 2); + ctx.fill(); + if (this.gameState == Maze_CTX2D.STATE_ENDED) { + if (this.isFocused) + this.drawBanner("Press space or enter to continue", "Victory!"); + ctx.lineWidth = 3; + for (let p of this.fireworks) { + ctx.strokeStyle = p.color; + if (p.type >= 1) { + ctx.beginPath(); + ctx.arc(p.x, p.y, p.size * 2, 0, Math.PI * 2); + ctx.stroke(); + } else { + ctx.beginPath(); + ctx.moveTo(p.x, p.y); + ctx.lineTo(p.x + p.vx * 0.1, p.y + p.vy * 0.1); + ctx.stroke(); + } + } + ctx.lineWidth = 1; + if (!this.isFocused) + this.drawBanner("Click here to continue. (unfocused)", "Paused"); + } + if (this.gameState == Maze_CTX2D.STATE_PLAYING) { + if (!this.isFocused) + this.drawBanner("Click here to continue. (unfocused)", "Paused"); + } + } + } + + drawBanner(innerText, titleText) { + const ctx = this.ctx; + const FONT_SIZE = this.width > 440 ? 24 : 16; + const x = this.width / 2; + const y = this.height / 2; + const y2 = y - FONT_SIZE / 2; + ctx.strokeStyle = 'black'; + const g = ctx.createLinearGradient(0, 0, this.width, 0); + g.addColorStop(0, '#404040a0'); + g.addColorStop(0.2, '#404040df'); + g.addColorStop(0.8, '#404040df'); + g.addColorStop(1, '#404040a0'); + ctx.fillStyle = g; + ctx.fillRect(0, y2 - 2, this.width + 1, FONT_SIZE + 5 * 2); + ctx.strokeRect(0, y2 - 2, this.width + 1, FONT_SIZE + 5 * 2); + if (typeof innerText === "string") { + ctx.fillStyle = 'goldenrod';//this.isFocused ? 'goldenrod' : 'lightgray'; + ctx.font = FONT_SIZE + "px serif"; + ctx.strokeText(innerText, x, y2 + FONT_SIZE); + ctx.fillText(innerText, x, y2 + FONT_SIZE); + } + if (typeof titleText === "string") { + ctx.fillStyle = this.isFocused ? 'red' : 'gray'; + ctx.font = 4 * FONT_SIZE + "px serif"; + ctx.fillText(titleText, x, y - 2 * FONT_SIZE); + ctx.strokeText(titleText, x, y - 2 * FONT_SIZE); + } + + } + + updateCanvas() { + const now = performance.now(); + const timeDelta = (now - this.prevNow) / 1000; //timeDelta = (milli - milli) / toSeconds + this.prevNow = now; + //#state switch + if (this.gameState == Maze_CTX2D.STATE_MAP_GEN_ANIM) { + this.animationStepTime += timeDelta; + while (this.animationStepTime >= this.animationStepDelay) { + this.animationStepTime -= this.animationStepDelay; + if (this.animationPos < this.animation.length) { + this.animationPos++; + } else { + this.gameState = Maze_CTX2D.STATE_PLAYING; + this.running = false; + this.playSound(3); + } + } + } else if (this.gameState == Maze_CTX2D.STATE_ENDED) { + this.fireworksStepTime += timeDelta; + if (this.fireworksStepTime > 3) { + this.boom(); + this.fireworksStepTime = 0 + Math.random() * .5; + } + for (let p of this.fireworks) { + p.time += timeDelta; + if (p.time > 2) { + p.time = 0; + p.type += 1; + } + if (p.type == 0) { + p.x += p.vx * timeDelta; + p.y += p.vy * timeDelta; + } else if (p.type == 1) { + p.size += 10 * timeDelta; + } else { + p.size += timeDelta; + } + } + while (this.fireworks.length > 0 && this.fireworks[0].type == 3) this.fireworks.shift(); + } + this.drawCanvas(); + if (this.running) { // loop + this.animationRequestId = requestAnimationFrame(() => this.updateCanvas()); + } else { // not looping + this.animationRequestId = 0; + } + } + + startRunning(fn) { + if (this.animationRequestId != 0) cancelAnimationFrame(this.animationRequestId); + this.running = true; + this.prevNow = performance.now(); + if (this.showFps) { + this.initTime = performance.now(); + } + this.animationRequestId = requestAnimationFrame(fn); + } + + close() { + if (this.animationRequestId != 0) { + cancelAnimationFrame(this.animationRequestId); + this.animationRequestId = 0; + } + this.running = false; + } + + pausePlayGame() { + this.running = !this.running; + if (this.running) { + this.startRunning(() => this.updateCanvas()); + } + } + + terminalPrintGame(term) { + term.printVar(this, "pong"); + } + + terminalRestartGame(term) { + term.terminalClose(); + this.restartGame(); + // this.canvasEl.focus(); + setTimeout(() => { this.canvasEl.focus(); this.pausePlayGame(); }, 200); + } + + setDifficulty(difficulty) { + if (difficulty <= 0) difficulty = 0; + this.difficulty = difficulty; + this.gridWidth = 11 + 4 * this.difficulty; + this.gridHeight = 11 + 4 * this.difficulty; + } + + onKeyDown(e) { + if (e.key == Maze_CTX2D.KEY_ESCAPE) { + console.log('esc'); + console.log('running:', this.running); + if (this.running) { + this.pausePlayGame(); + } else { + this.gameState = Maze_CTX2D.STATE_MENU; + this.drawCanvas(); + } + e.preventDefault(); + return false; + } + switch (this.gameState) { + case Maze_CTX2D.STATE_MENU: + if (e.key == Maze_CTX2D.KEY_ENTER || e.key == Maze_CTX2D.KEY_SPACEBAR) { + // console.log("ENTER!"); + this.newGame(this.gridWidth, this.gridHeight); + this.gameState = Maze_CTX2D.STATE_MAP_GEN_ANIM; + this.startRunning(() => this.updateCanvas()); + this.playSound(4); + e.preventDefault(); + return false; + } + if (e.key == Maze_CTX2D.KEY_S || e.key == Maze_CTX2D.KEY_ARROW_DOWN || + e.key == Maze_CTX2D.KEY_A || e.key == Maze_CTX2D.KEY_ARROW_LEFT) { + this.setDifficulty(this.difficulty - 1); + this.playSound(1); + this.drawCanvas(); + e.preventDefault(); + return false; + } + if (e.key == Maze_CTX2D.KEY_W || e.key == Maze_CTX2D.KEY_ARROW_UP || + e.key == Maze_CTX2D.KEY_D || e.key == Maze_CTX2D.KEY_ARROW_RIGHT) { + this.setDifficulty(this.difficulty + 1); + this.playSound(1); + this.drawCanvas(); + e.preventDefault(); + return false; + } + break; + case Maze_CTX2D.STATE_MAP_GEN_ANIM: + if (e.key == Maze_CTX2D.KEY_ENTER || e.key == Maze_CTX2D.KEY_SPACEBAR) { + if (this.running) { + this.animationPos = this.animation.length; + } else { + this.startRunning(() => this.updateCanvas()); + } + e.preventDefault(); + return false; + } + break; + case Maze_CTX2D.STATE_PLAYING: + if (e.key == 'r') { + this.gameState = Maze_CTX2D.STATE_MAP_GEN_ANIM; + this.animationPos = 0; + this.animationStepTime = 0; + this.startRunning(() => this.updateCanvas()); + e.preventDefault(); + return false; + } + if (e.key == Maze_CTX2D.KEY_W || e.key == Maze_CTX2D.KEY_ARROW_UP) { + if (this.player.y > 0) { + if (this.maze[this.player.y - 1][this.player.x] == 0) { + this.player.y -= 1; + this.drawCanvas(); + this.playSound(1); + } else { + this.playSound(2); + } + } + e.preventDefault(); + return false; + } + if (e.key == Maze_CTX2D.KEY_S || e.key == Maze_CTX2D.KEY_ARROW_DOWN) { + if (this.player.y < this.gridHeight - 1) { + if (this.maze[this.player.y + 1][this.player.x] == 0) { + this.player.y += 1; + if (this.player.y == this.gridHeight - 1) { + this.gameState = Maze_CTX2D.STATE_ENDED; + this.fireworks = []; + this.fireworksStepTime = 0; + this.boom(); + this.playSound(0); + // this.drawCanvas(); + this.startRunning(() => this.updateCanvas()) + } else { + this.drawCanvas(); + } + this.playSound(1); + } else { + this.playSound(2); + } + } + e.preventDefault(); + return false; + } + if (e.key == Maze_CTX2D.KEY_A || e.key == Maze_CTX2D.KEY_ARROW_LEFT) { + if (this.player.x > 0) { + if (this.maze[this.player.y][this.player.x - 1] == 0) { + this.player.x -= 1; + this.drawCanvas(); + this.playSound(1); + } else { + this.playSound(2); + } + } + e.preventDefault(); + return false; + } + if (e.key == Maze_CTX2D.KEY_D || e.key == Maze_CTX2D.KEY_ARROW_RIGHT) { + if (this.player.x < this.gridWidth) { + if (this.maze[this.player.y][this.player.x + 1] == 0) { + this.player.x += 1; + this.drawCanvas(); + this.playSound(1); + } else { + this.playSound(2); + } + } + e.preventDefault(); + return false; + } + break; + case Maze_CTX2D.STATE_ENDED: + if (e.key == Maze_CTX2D.KEY_ENTER || e.key == Maze_CTX2D.KEY_SPACEBAR) { + this.gameState = Maze_CTX2D.STATE_MENU; + e.preventDefault(); + return false; + } + break; + } + // if (e.key == Maze_CTX2D.KEY_ARROW_UP || e.key == Maze_CTX2D.KEY_W) { + // this.human.vy = -this.speedHuman; + // e.preventDefault(); + // return false; + // } else if (e.key == Maze_CTX2D.KEY_ARROW_DOWN || e.key == Maze_CTX2D.KEY_S) { + // this.human.vy = this.speedHuman; + // e.preventDefault(); + // return false; + // } else if (e.key == Maze_CTX2D.KEY_ENTER || e.key == Maze_CTX2D.KEY_SPACEBAR) { + // //# next round/pause/play + // if (this.gameState == Maze_CTX2D.STATE_ENDED) { + // this.newRound(); + // this.startRunning(() => this.updateCanvas()); + // } else { + // this.pausePlayGame(); + // } + // e.preventDefault(); + // return false; + // } + return true; + } + + onKeyUp(e) { + // if (e.key == Maze_CTX2D.KEY_ARROW_UP || e.key == Maze_CTX2D.KEY_W) { + // this.human.vy = 0; + // e.preventDefault(); + // return false; + // } else if (e.key == Maze_CTX2D.KEY_ARROW_DOWN || e.key == Maze_CTX2D.KEY_S) { + // this.human.vy = 0; + // e.preventDefault(); + // return false; + // } + return true; + } + + onBlur() { + this.isFocused = false; + this.canvasEl.style.borderColor = null; + if (this.running) { + this.pausePlayGame(); + } else { + this.drawCanvas(); + } + } + + onFocus() { + this.isFocused = true; + this.canvasEl.style.borderColor = "red"; + if (!this.running && (Maze_CTX2D.AUTO_CONTINUE_ON_FOCUS + || this.gameState == Maze_CTX2D.STATE_MAP_GEN_ANIM + || this.gameState == Maze_CTX2D.STATE_ENDED)) { + this.pausePlayGame(); + } else { + this.drawCanvas(); + } + } + + boom() { + const colors = ['red', 'green', 'blue', 'violet', 'orange', 'goldenrod']; + const color = colors[Math.floor(Math.random() * colors.length)]; + + const cx = this.width / 2; + const cy = this.height / 2; + const speed = 100; + const size = 10; + const count = Math.random() < 0.75 ? 2 : 3; + for (let i = 0; i < count; i++) { + let p = {} + p.x = cx; + p.y = cy; + let rot = Math.random() * Math.PI * .5 - 0.75 * Math.PI; + p.vx = Math.cos(rot) * speed; + p.vy = Math.sin(rot) * speed; + p.type = 0; + p.time = 0 + Math.random() * 0.5; + p.size = size + Math.random() * 10; + p.color = color; + this.fireworks.push(p) + } + } + + generateBacktrackingMaze(width, height) { + + // Make them odd + width -= width % 2; width++; + height -= height % 2; height++; + + // Fill maze with 1's (walls) + let maze = []; + for (let i = 0; i < height; i++) { + maze.push([]); + for (let j = 0; j < width; j++) { + maze[i].push(1); + } + } + + let anim = []; + + // Opening at top - start of maze + maze[0][1] = 0; + + let start = []; + do { + start[0] = Math.floor(Math.random() * height) + } while (start[0] % 2 == 0); + do { + start[1] = Math.floor(Math.random() * width) + } while (start[1] % 2 == 0); + + maze[start[0]][start[1]] = 0; + anim.push({ state: 1, x: start[1], y: start[0] }); + + // First open cell + let openCells = [start]; + + while (openCells.length) { + + let cell, n; + + // Add unnecessary element for elegance of code + // Allows openCells.pop() at beginning of do while loop + openCells.push([-1, -1]); + + // Define current cell as last element in openCells + // and get neighbors, discarding "locked" cells + do { + let step = openCells.pop(); + if (step[0] > 0 && step[1] > 0) anim.push({ state: 0, x: step[1], y: step[0] }); + if (openCells.length == 0) + break; + cell = openCells[openCells.length - 1]; + n = this.getMazeNeighbors(maze, cell[0], cell[1]); + } while (n.length == 0 && openCells.length > 0); + + // If we're done, don't bother continuing + if (openCells.length == 0) + break; + + // Choose random neighbor and add it to openCells + let choice = n[Math.floor(Math.random() * n.length)]; + openCells.push(choice); + + // Set neighbor to 0 (path, not wall) + // Set connecting node between cell and choice to 0 + let connectY = (choice[0] + cell[0]) / 2; + let connectX = (choice[1] + cell[1]) / 2; + maze[choice[0]][choice[1]] = 0; + maze[connectY][connectX] = 0; + anim.push({ state: 0, x: connectX, y: connectY }); + anim.push({ state: 1, x: choice[1], y: choice[0] }); + } + + // Opening at bottom - end of maze + maze[maze.length - 1][maze[0].length - 2] = 0; + // maze[maze.length - 2][maze[0].length - 2] = 0; + anim.push({ state: 0, x: maze[0].length - 2, y: maze.length - 1 }); + // anim.push({ state: 0, x: maze[0].length - 1, y: maze.length - 1 }); + + return { maze: maze, animation: anim }; + } + + getMazeNeighbors(maze, ic, jc) { + let final = []; + for (let i = 0; i < 4; i++) { + let n = [ic, jc]; + + // Iterates through four neighbors + // [i][j - 2] + // [i][j + 2] + // [i - 2][j] + // [i + 2][j] + n[i % 2] += ((Math.floor(i / 2) * 2) || -2); + if (n[0] < maze.length && + n[1] < maze[0].length && + n[0] > 0 && + n[1] > 0) { + + if (maze[n[0]][n[1]] == 1) { + final.push(n); + } + } + } + return final; + } +}//-> class Maze_CTX2D + +function startMaze(divId, width = 480, height = 320, difficulty = 1, showFps = true) { + return new Maze_CTX2D(divId, width, height, difficulty, showFps); +} diff --git a/src/js/game-pong-context2d.js b/src/js/game-pong-context2d.js index 1c43f0a..7151907 100644 --- a/src/js/game-pong-context2d.js +++ b/src/js/game-pong-context2d.js @@ -41,6 +41,8 @@ class PingPong_CTX2D { this.audioCtx.createMediaElementSource(this.sounds[0]).connect(this.audioCtx.destination); this.sounds[1] = new Audio('./snd/short-success.mp3'); this.audioCtx.createMediaElementSource(this.sounds[1]).connect(this.audioCtx.destination); + this.sounds[2] = new Audio('./snd/flash.mp3'); + this.audioCtx.createMediaElementSource(this.sounds[2]).connect(this.audioCtx.destination); this.showFps = showFps; if (showFps) { @@ -255,6 +257,7 @@ class PingPong_CTX2D { this.countDown -= timeDelta;//PONG_UPDATE_INTERVAL; if (this.countDown < 0) { this.gameState = PingPong_CTX2D.STATE_PLAYING; + this.playSound(2); } } else if (this.gameState == PingPong_CTX2D.STATE_PLAYING) { //cpu actions diff --git a/src/js/games-launcher.js b/src/js/games-launcher.js index 6ce1a67..402964a 100644 --- a/src/js/games-launcher.js +++ b/src/js/games-launcher.js @@ -40,6 +40,7 @@ class GamesLauncher { this.selectGameEl = createElement('select', { id: 'games', style: 'float: right;' }); this.selectGameEl.appendChild(createElement('option', { value: 'pong-context2d' }, 'Pingpong (Context2D)')); this.selectGameEl.appendChild(createElement('option', { value: 'tetris-context2d' }, 'Tetris (Context2D)')); + this.selectGameEl.appendChild(createElement('option', { value: 'maze-context2d' }, 'Maze (Context2D)')); this.selectGameEl.onchange = () => this.onSelectGameChange(); this.gameEl.appendChild(this.selectGameEl); this.gameEl.appendChild(createElement('br')); @@ -93,6 +94,11 @@ class GamesLauncher { this.heightEl.value = 600; // this.difficultyEl.value = 1; break; + case 'maze-context2d': + this.widthEl.value = 800; + this.heightEl.value = 640; + // this.difficultyEl.value = 1; + break; } } @@ -113,6 +119,11 @@ class GamesLauncher { this.currentGame = new Tetris_CTX2D(this.gameId, width, height, difficulty, showFps); this.currentGame.canvasEl.focus(); break; + case 'maze-context2d': + this.createCloseExitGameButton(); + this.currentGame = new Maze_CTX2D(this.gameId, width, height, difficulty, showFps); + this.currentGame.canvasEl.focus(); + break; default: alert("Select a game first?"); } diff --git a/src/js/theme.js b/src/js/theme.js index a93ed70..22aa963 100644 --- a/src/js/theme.js +++ b/src/js/theme.js @@ -10,7 +10,7 @@ function toggleTheme() { } function setTheme(themeName) { - console.log("setting them: " + themeName); + console.log("setting theme: " + themeName); const root = document.querySelector(":root"); root.classList.add(themeName); localStorage.setItem('theme', themeName); diff --git a/src/snd/click-button.mp3 b/src/snd/click-button.mp3 index 4d5fbc3..86ba9cd 100644 Binary files a/src/snd/click-button.mp3 and b/src/snd/click-button.mp3 differ diff --git a/src/snd/flash.mp3 b/src/snd/flash.mp3 new file mode 100644 index 0000000..e099c9d Binary files /dev/null and b/src/snd/flash.mp3 differ diff --git a/src/snd/footstep.mp3 b/src/snd/footstep.mp3 new file mode 100644 index 0000000..34b5bd3 Binary files /dev/null and b/src/snd/footstep.mp3 differ diff --git a/src/snd/glass-knock.mp3 b/src/snd/glass-knock.mp3 index 52b49e5..f236579 100644 Binary files a/src/snd/glass-knock.mp3 and b/src/snd/glass-knock.mp3 differ diff --git a/src/snd/metronome.mp3 b/src/snd/metronome.mp3 new file mode 100644 index 0000000..9c6cad4 Binary files /dev/null and b/src/snd/metronome.mp3 differ diff --git a/src/snd/snap.mp3 b/src/snd/snap.mp3 new file mode 100644 index 0000000..5af9609 Binary files /dev/null and b/src/snd/snap.mp3 differ diff --git a/src/snd/surprise.mp3 b/src/snd/surprise.mp3 new file mode 100644 index 0000000..caa4c98 Binary files /dev/null and b/src/snd/surprise.mp3 differ