Bar Brawl Side Scroller class NPC extends Entity { constructor(x, y) { super(x, y, 40, 60); this.direction = Math.random() > 0.5 ? 1 : -1; this.health = 3; this.idleFrame = 0; this.isMoving = false; } draw() { // Head ctx.fillStyle = '#f1c27d'; ctx.fillRect(this.x + 10, this.y, 20, 20); // Body (red shirt) ctx.fillStyle = 'red'; ctx.fillRect(this.x + 10, this.y + 20, 20, 20); // Legs (black jeans) let legOffset = 0; if (!this.isMoving) { // Idle leg swing oscillation between -2 and 2 px every 30 frames legOffset = 2 * Math.sin(this.idleFrame * 0.2); } ctx.fillStyle = 'black'; ctx.fillRect(this.x + 10 + legOffset, this.y + 40, 10, 20); ctx.fillRect(this.x + 20 - legOffset, this.y + 40, 10, 20); } update() { // Decide if moving or idle this.isMoving = this.vx !== 0; if (this.isMoving) { this.x += this.direction; // move horizontally } else { // Slight horizontal idle sway this.x += 0.3 * Math.sin(this.idleFrame * 0.1); } super.update(); // Update idle animation frame this.idleFrame++; // Randomly change walking direction sometimes if (Math.random() < 0.01) this.direction *= -1; } }
const canvas = document.getElementById('game'); const ctx = canvas.getContext('2d'); const assetsOnline = 'https://raw.githubusercontent.com/riiamri23/gamelist/3fa07387e471f823aa4bb7fc49525e0ece2f35bb/released/snake/'; const headX = 10; const headY = 10; const parts = []; const appleX = 5; const appleY = 5; const tileCount = 20; const tileSize = canvas.width / tileCount - 2; let velocityX = 0; let velocityY = 0; const speed = 7; const gulpSound = new Audio(`${assetsOnline}assets/gulp.mp3`); const gameOverSound = new Audio(`${assetsOnline}assets/game-over.mp3`) class SnakeParts{ constructor(X,Y){ this.X = X; this.Y = Y; } } class Snake{ constructor(headX, headY, tileCount, tileSize, color, parts){ this.headX = headX; this.headY = headY; this.tileCount = tileCount; this.tileSize = tileSize; this.color = color; this.parts = parts; this.tailLength = 0; } draw = function(){ ctx.beginPath(); ctx.fillStyle = this.color; ctx.fillRect(this.headX * this.tileCount , this.headY * this.tileCount , this.tileSize , this.tileSize ); this.drawTail(); ctx.closePath(); } addTail = function(){ this.tailLength++; } drawTail = function(){ for(let i=0;i < this.parts.length;i++){ const snakePart = this.parts[i]; ctx.fillStyle = "grey"; ctx.fillRect( snakePart.X * this.tileCount , snakePart.Y * this.tileCount , this.tileSize , this.tileSize ); } this.parts.push(new SnakeParts(this.headX, this.headY)); while(this.parts.length > this.tailLength) this.parts.shift(); } changePosition = function(velocityX, velocityY){ this.headX = this.headX + velocityX; this.headY = this.headY + velocityY; } } class Apple{ constructor(appleX, appleY, tileCount, tileSize){ this.appleX = appleX; this.appleY = appleY; this.tileCount = tileCount; this.tileSize = tileSize; } draw = function(){ ctx.beginPath(); ctx.fillStyle = "red"; ctx.fillRect(this.appleX * this.tileCount, this.appleY * this.tileCount, this.tileSize, this.tileSize); ctx.closePath(); } randomAppear = function(isCollision){ if(isCollision){ this.appleX = Math.floor(Math.random() * this.tileCount); this.appleY = Math.floor(Math.random() * this.tileCount); } this.draw(); } } class Score{ constructor(totalScore){ this.totalScore = totalScore; } draw = function(){ ctx.fillStyle = "white"; ctx.font = "10px Verdana"; ctx.fillText("Score " + this.totalScore, 0, 10); } drawHighestScore = function(){ ctx.fillStyle = "white"; ctx.font = "10px Verdana"; const highestScore = localStorage.getItem("highestScore") !== null ? localStorage.getItem("highestScore") : 0; ctx.fillText("Highest Score " + highestScore, canvas.width-100, 10); } incScore = function(){ this.totalScore++; } setHighestScore = function(){ const highestScore = localStorage.getItem("highestScore") !== null ? localStorage.getItem("highestScore") : 0; if(this.totalScore > highestScore) localStorage.setItem("highestScore", this.totalScore); } } let snake = new Snake( headX, headY, tileCount, tileSize, "white", parts, ); let apple = new Apple( Math.floor(Math.random() * tileCount), Math.floor(Math.random() * tileCount), tileCount, tileSize ); let score = new Score(0); function checkGameOver(snake){ let isOver = false; // check collidate with wall; if(snake.headX < 0 || snake.headX === tileCount || snake.headY < 0 || snake.headY === tileCount ) isOver = true; // check collidate with tail; for(let i = 0; i < snake.parts.length; i++) if(snake.headX === snake.parts[i].X && snake.headY === snake.parts[i].Y ) isOver = true; if(isOver){ // create label on screen ctx.beginPath(); ctx.fillStyle = "white"; ctx.font = "50px Verdana"; ctx.fillText("Game Over", canvas.width / 6.5, canvas.height / 2); ctx.font = "25px Verdana"; ctx.fillText("Enter to play again", canvas.width / 6.5, canvas.height / 1.8); ctx.closePath(); } return isOver; } checkOnCollision = function(snake, apple){ if(snake.headX === apple.appleX && snake.headY === apple.appleY ) { gulpSound.play(); return true; } return false; } function clearScreen(){ // Start a new Path ctx.beginPath(); ctx.fillStyle = 'black'; ctx.fillRect(0,0, canvas.width, canvas.height); ctx.closePath(); } document.body.addEventListener('keydown', keyDown); function keyDown(event){ if(event.keyCode === 38){ // up if(velocityY === 1) return; velocityX = 0; velocityY = -1; } if(event.keyCode === 40){ // down if(velocityY === -1) return; velocityX = 0; velocityY = 1; } if(event.keyCode === 37){ // left if(velocityX === 1) return; velocityX = -1; velocityY = 0; } if(event.keyCode === 39){ // right if(velocityX === -1) return; velocityX = 1; velocityY = 0; } if(event.keyCode === 13){ velocityX = 0; velocityY = 0; snake = new Snake( headX, headY, tileCount, tileSize, "white", parts, ); apple = new Apple( Math.floor(Math.random() * tileCount), Math.floor(Math.random() * tileCount), tileCount, tileSize ); score = new Score(0); drawGame(); } } function drawGame(){ snake.changePosition(velocityX, velocityY); // check game over let isOver = checkGameOver(snake); if(isOver) { gameOverSound.play(); score.setHighestScore(); score.drawHighestScore(); return; } clearScreen(); snake.draw(); score.draw(); score.drawHighestScore(); let isCollision = checkOnCollision(snake, apple); apple.randomAppear(isCollision); if(isCollision){ snake.addTail(); score.incScore(); } setTimeout(drawGame, 1000/speed); } drawGame();