├── CNAME ├── README.md ├── pacman ├── data │ └── map.png ├── images │ ├── 32_dot.png │ ├── 32_player_close.png │ ├── 32_player_open.png │ ├── bg_center.png │ ├── bg_down.png │ ├── bg_left.png │ ├── bg_right.png │ ├── bg_up.png │ └── enemy_red.png ├── index.html └── js │ ├── ai.js │ ├── assets.js │ ├── collision.js │ ├── controls.js │ ├── draw.js │ ├── game.js │ ├── objects.js │ ├── objectsLoad.js │ ├── update.js │ └── utils.js ├── snake ├── data │ └── map.png ├── index.html └── js │ ├── controls.js │ ├── draw.js │ ├── framework.js │ ├── logic.js │ └── utils.js └── space-invaders ├── enemies.json ├── images ├── 32_enemy.png ├── 32_laser_blue.png ├── 32_laser_red.png ├── 32_player.png ├── 64_1.png ├── 64_2.png ├── 64_laser_blue.png ├── 64_laser_red.png └── background.png ├── index.html └── js ├── ai.js ├── assets.js ├── controls.js ├── draw.js ├── game.js ├── objects.js ├── update.js └── utils.js /CNAME: -------------------------------------------------------------------------------- 1 | 404.coconauts.net -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A collection of simple 404-themed games made with 2 | [canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial). 3 | Ideal to embed in your 404 page! 4 | As seen on [coconauts.net](http://coconauts.net/404). 5 | 6 | ## How to embed them in a page 7 | 8 | Copy the folder for your desired game, and then use an `iframe` 9 | pointing to the relative path of that folder. 10 | 11 | 13 | 14 | ## I can't decide, can't I have a random one every time? 15 | 16 | Then you'll need a little js snippet: 17 | 18 | ``` 19 | var randomBetween = function(f, to){ 20 | return Math.floor(Math.random() * to) + f; 21 | } 22 | var randomGame = function(){ 23 | var games = ["pacman", "space-invaders", "snake"]; 24 | var randomGame = randomBetween(0,games.length); 25 | return games[randomGame]; 26 | } 27 | var src = "path/to/games/folder/" + randomGame(); 28 | document.getElementById('404-game').src = src; 29 | ``` 30 | 31 | Have fun! 32 | -------------------------------------------------------------------------------- /pacman/data/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coconauts/404-games/c8096c8ab65e66312bc64496deaae90de82037df/pacman/data/map.png -------------------------------------------------------------------------------- /pacman/images/32_dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coconauts/404-games/c8096c8ab65e66312bc64496deaae90de82037df/pacman/images/32_dot.png -------------------------------------------------------------------------------- /pacman/images/32_player_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coconauts/404-games/c8096c8ab65e66312bc64496deaae90de82037df/pacman/images/32_player_close.png -------------------------------------------------------------------------------- /pacman/images/32_player_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coconauts/404-games/c8096c8ab65e66312bc64496deaae90de82037df/pacman/images/32_player_open.png -------------------------------------------------------------------------------- /pacman/images/bg_center.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coconauts/404-games/c8096c8ab65e66312bc64496deaae90de82037df/pacman/images/bg_center.png -------------------------------------------------------------------------------- /pacman/images/bg_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coconauts/404-games/c8096c8ab65e66312bc64496deaae90de82037df/pacman/images/bg_down.png -------------------------------------------------------------------------------- /pacman/images/bg_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coconauts/404-games/c8096c8ab65e66312bc64496deaae90de82037df/pacman/images/bg_left.png -------------------------------------------------------------------------------- /pacman/images/bg_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coconauts/404-games/c8096c8ab65e66312bc64496deaae90de82037df/pacman/images/bg_right.png -------------------------------------------------------------------------------- /pacman/images/bg_up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coconauts/404-games/c8096c8ab65e66312bc64496deaae90de82037df/pacman/images/bg_up.png -------------------------------------------------------------------------------- /pacman/images/enemy_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coconauts/404-games/c8096c8ab65e66312bc64496deaae90de82037df/pacman/images/enemy_red.png -------------------------------------------------------------------------------- /pacman/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 404 Pacman 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /pacman/js/ai.js: -------------------------------------------------------------------------------- 1 | var updateEnemies = function(modifier){ 2 | 3 | for (var i= 0; i < enemies.length; i++){ 4 | var enemy = enemies[i]; 5 | if (isCollidingBlocks(enemy,enemy.direction)){ 6 | changeDirection(enemy); 7 | 8 | } else { 9 | move(enemy, modifier); 10 | } 11 | } 12 | } 13 | 14 | var changeDirection = function(enemy){ 15 | var tol = p(-5,0); 16 | var randomDirection = randomBetween(0, 3); 17 | switch(randomDirection){ 18 | case 0: if (!isCollidingBlocks(enemy, LEFT, tol)) enemy.direction = LEFT;break; 19 | case 1: if (!isCollidingBlocks(enemy, RIGHT, tol)) enemy.direction = RIGHT;break; 20 | case 2: if (!isCollidingBlocks(enemy, UP, tol)) enemy.direction = UP;break; 21 | default: if (!isCollidingBlocks(enemy, DOWN, tol)) enemy.direction = DOWN;break; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /pacman/js/assets.js: -------------------------------------------------------------------------------- 1 | var setImage = function(img){ 2 | var i = new Image(); 3 | i.src = img; 4 | return i; 5 | }; 6 | 7 | // Hero image 8 | var playerImageClose = setImage("images/32_player_close.png"); 9 | var playerImageOpen = setImage("images/32_player_open.png"); 10 | 11 | var monsterImage = setImage("images/enemy_red.png"); 12 | 13 | var dotImage = setImage("images/32_dot.png"); 14 | 15 | var backgroundImage = {}; 16 | backgroundImage.center = setImage("images/bg_center.png"); 17 | backgroundImage.left = setImage("images/bg_left.png"); 18 | backgroundImage.right = setImage("images/bg_right.png"); 19 | backgroundImage.down = setImage("images/bg_down.png"); 20 | backgroundImage.up = setImage("images/bg_up.png"); 21 | -------------------------------------------------------------------------------- /pacman/js/collision.js: -------------------------------------------------------------------------------- 1 | //http://gamedev.stackexchange.com/questions/13774/how-do-i-detect-the-direction-of-2d-rectangular-object-collisions 2 | var collision = new Collision(); 3 | 4 | var RIGHT = { x: 1, y: 0}; 5 | var LEFT = { x: -1, y: 0}; 6 | var UP = { x: 0, y: -1}; 7 | var DOWN = { x: 0, y: 1}; 8 | 9 | function Collision(){ 10 | 11 | this.onCollisionChange = function(ojb1, obj2, size, callback) { 12 | var isColliding = false; 13 | setInterval( 14 | function(){ 15 | var beforeStatus = isColliding; 16 | isColliding = collision(obj1, obj2, size); 17 | if (beforeStatus != isColliding) callback(); 18 | }, 100); 19 | } 20 | 21 | this.isCollidingDirection = function(obj1, obj2, direction, offset){ 22 | switch(direction){ 23 | case LEFT: return collision.containsLeft(obj1, obj2, SIZE, offset); break; 24 | case RIGHT: return collision.containsRight(obj1, obj2, SIZE, offset); break; 25 | case UP: return collision.containsUp(obj1, obj2, SIZE, offset); break; 26 | case DOWN: return collision.containsDown(obj1, obj2, SIZE, offset); break; 27 | default: console.log("Undefined direction"); 28 | } 29 | } 30 | 31 | this.contains = function(obj, p, size){ 32 | var contains = p.x > obj.x && p.x < (obj.x + size) && 33 | p.y > obj.y && p.y < (obj.y + size); 34 | return contains; 35 | } 36 | 37 | this.collision = function(obj1, obj2, size){ 38 | return obj1.x <= (obj2.x + size) 39 | && obj2.x <= (obj1.x + size) 40 | && obj1.y <= (obj2.y + size) 41 | && obj2.y <= (obj1.y + size) 42 | } 43 | 44 | this.containsLeft = function(obj1, obj2, size, offset){ 45 | return this.contains(obj1, addCoords(middlePoints(obj2, size).left, offset) , size); 46 | } 47 | this.containsRight = function(obj1, obj2, size, offset){ 48 | return this.contains(obj1, addCoords(middlePoints(obj2, size).right, offset), size); 49 | } 50 | this.containsUp = function(obj1, obj2, size, offset){ 51 | return this.contains(obj1, addCoords(middlePoints(obj2, size).top, offset), size); 52 | } 53 | this.containsDown = function(obj1, obj2, size, offset){ 54 | return this.contains(obj1, addCoords( middlePoints(obj2, size).bottom, offset), size); 55 | } 56 | 57 | var addCoords = function(point, offset) { 58 | return p(point.x + offset.x, point.y + offset.y); 59 | } 60 | 61 | var middlePoints = function(obj, size){ 62 | var middleX = (obj.x + obj.x + size) / 2; //(obj.x *2 + size) / 2; ? 63 | var middleY = (obj.y + obj.y + size) / 2; 64 | return { 65 | left: p(obj.x, middleY), 66 | right: p(obj.x + size , middleY), 67 | top: p(middleX, obj.y), 68 | bottom: p(middleX, obj.y + size), 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /pacman/js/controls.js: -------------------------------------------------------------------------------- 1 | // Handle keyboard controls 2 | var keysDown = {}; 3 | 4 | var KEY_UP =38, KEY_DOWN =40, KEY_LEFT =37, KEY_RIGHT =39; 5 | 6 | addEventListener("keydown", function (e) { 7 | keysDown[e.keyCode] = true; 8 | }, false); 9 | 10 | addEventListener("keyup", function (e) { 11 | delete keysDown[e.keyCode]; 12 | }, false); 13 | 14 | 15 | //Called from update.js 16 | var controls = function(modifier){ 17 | if (KEY_LEFT in keysDown && !isCollidingBlocks(player, LEFT, p(-5,0))) player.direction = LEFT; 18 | if (KEY_RIGHT in keysDown && !isCollidingBlocks(player,RIGHT, p(5,0))) player.direction = RIGHT; 19 | if (KEY_UP in keysDown && !isCollidingBlocks(player,UP, p(0,-5))) player.direction = UP; 20 | if (KEY_DOWN in keysDown && !isCollidingBlocks(player,DOWN, p(0,5))) player.direction = DOWN; 21 | } 22 | -------------------------------------------------------------------------------- /pacman/js/draw.js: -------------------------------------------------------------------------------- 1 | // Create the canvas 2 | var canvas = document.getElementById("canvas"); 3 | var ctx = canvas.getContext("2d"); 4 | 5 | var SIZE = 32; 6 | 7 | var directionToRotation = function(direction){ 8 | switch(direction){ 9 | case RIGHT : return 0; 10 | case LEFT: return 180; 11 | case DOWN: return 90; 12 | case UP: return 270; 13 | 14 | } 15 | } 16 | // Draw everything 17 | var render = function () { 18 | // ctx.clearRect(0,0,canvas.width,canvas.height); 19 | /* 20 | if (bgReady) { 21 | ctx.drawImage(bgImage,0 , 0); 22 | }*/ 23 | ctx.fillStyle="black"; 24 | ctx.fillRect(0,0,canvas.width,canvas.height); 25 | 26 | drawBackground(); 27 | 28 | if (!player.dead) { 29 | var image; 30 | if(player.sprite == 0) image = playerImageClose; 31 | else image = playerImageOpen; 32 | //TODO rotate with direction 33 | // ctx.drawImage(image, player.x, player.y, SIZE, SIZE); 34 | var rotation = directionToRotation(player.direction); 35 | drawRotated(image, player.x, player.y, rotation); 36 | } 37 | 38 | for (var i=0; i < dots.length; i++){ 39 | var dot = dots[i]; 40 | ctx.drawImage(dotImage, dot.x, dot.y ,SIZE, SIZE); 41 | } 42 | 43 | for (var i=0; i < enemies.length; i++){ 44 | var enemy = enemies[i]; 45 | ctx.drawImage(monsterImage, enemy.x, enemy.y,SIZE, SIZE); 46 | 47 | } 48 | if (false) //DEBUG 49 | for (var i=0; i < blocks.length; i++){ 50 | var block = blocks[i]; 51 | ctx.drawImage(monsterImage, block.x, block.y ,SIZE, SIZE); 52 | } 53 | 54 | 55 | 56 | if (player.dead){ 57 | ctx.fillStyle = "red"; 58 | ctx.font = "48px Helvetica"; 59 | ctx.textAlign = "center"; 60 | ctx.textBaseline = "top"; 61 | ctx.fillText("Game Over", canvas.width / 2, canvas.height/ 2); 62 | } 63 | }; 64 | 65 | function drawBackground(){ 66 | for (var i= 0; i < background.length; i++){ 67 | var b = background[i]; 68 | 69 | //ctx.drawImage(backgroundImage.center, b.x, b.y ,SIZE, SIZE); 70 | for (var j= 0; j < b.directions.length; j++){ 71 | var d = b.directions[j]; 72 | ctx.drawImage(backgroundImage[d], b.x, b.y ,SIZE, SIZE); 73 | } 74 | } 75 | } 76 | var TO_RADIANS = Math.PI/180; 77 | 78 | //http://creativejs.com/2012/01/day-10-drawing-rotated-images-into-canvas/ 79 | function drawRotated(image, x, y, angle) { 80 | var halfWidth = image.width/2; 81 | var halfHeight = image.height/2; 82 | ctx.save(); 83 | ctx.translate(x +halfWidth, y + halfHeight); 84 | ctx.rotate(angle * TO_RADIANS); 85 | ctx.drawImage(image, -halfWidth, -halfHeight); 86 | ctx.restore(); 87 | } 88 | -------------------------------------------------------------------------------- /pacman/js/game.js: -------------------------------------------------------------------------------- 1 | // The main game loop 2 | var main = function () { 3 | var now = Date.now(); 4 | var delta = now - then; 5 | 6 | update(delta / 1000); 7 | render(); 8 | 9 | then = now; 10 | 11 | // Request to do this again ASAP 12 | requestAnimationFrame(main); 13 | }; 14 | 15 | // Cross-browser support for requestAnimationFrame 16 | var w = window; 17 | requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationFrame; 18 | 19 | // Let's play this game! 20 | var then = Date.now(); 21 | main(); 22 | -------------------------------------------------------------------------------- /pacman/js/objects.js: -------------------------------------------------------------------------------- 1 | // Game objects 2 | var player = { 3 | direction: LEFT, 4 | speed: 100, 5 | sprite: 0 6 | } 7 | 8 | var enemies = []; 9 | var dots = []; 10 | var blocks = []; 11 | 12 | var background = []; 13 | -------------------------------------------------------------------------------- /pacman/js/objectsLoad.js: -------------------------------------------------------------------------------- 1 | var BLACK = {r: 0, g: 0, b: 0}; 2 | var WHITE = {r: 255, g: 255, b: 255}; 3 | var YELLOW = {r: 255, g: 255, b: 0}; 4 | var GREEN = {r: 0, g: 255, b: 0}; 5 | var ORANGE = {r: 255, g: 127, b: 0}; 6 | var CYAN = {r: 0, g: 255, b: 255}; 7 | var RED = {r: 255, g: 0, b: 0}; 8 | var PINK = {r: 255, g: 0, b: 255}; 9 | 10 | var addEnemy = function(x, y){ 11 | var enemy = { 12 | direction: DOWN, 13 | speed: 100, 14 | x: x, 15 | y: y 16 | }; 17 | enemies.push(enemy); 18 | } 19 | 20 | var bg = function(x, y , dirs) { 21 | return { 22 | x: x * SIZE, 23 | y: y * SIZE, 24 | directions: dirs 25 | } 26 | } 27 | 28 | loadImagePixels("data/map.png", function(pixels){ 29 | 30 | pixelBackground(pixels); 31 | 32 | for(i=0;i canvas.height ) obj.y = 0 ; 39 | } 40 | 41 | var isCollidingBlocks = function(obj, direction, offset){ 42 | if (!offset) offset = p(0,0); 43 | for (var i=0; i < blocks.length; i++){ 44 | var block = blocks[i]; 45 | 46 | var isColliding = collision.isCollidingDirection(block, obj, direction, offset); 47 | if (isColliding) return true; 48 | } 49 | return false; 50 | } 51 | -------------------------------------------------------------------------------- /pacman/js/utils.js: -------------------------------------------------------------------------------- 1 | var loadJSON = function (path, success, error) 2 | { 3 | var oReq = new XMLHttpRequest(); 4 | oReq.onload = function reqListener () { 5 | jsonData = JSON.parse(this.responseText); 6 | //console.log("Loading JSON data", jsonData); 7 | success(jsonData); 8 | };; 9 | oReq.open("get", path, true); 10 | oReq.send(); 11 | } 12 | 13 | var p = function(x,y){ return {x: x, y: y}; } 14 | 15 | var loadImagePixels = function(image, callback){ 16 | 17 | var img = new Image(); 18 | img.src = image; 19 | img.onload = function() { 20 | console.log("Image " + image + " loaded " + img.width + "x" +img.height); 21 | var canvas = document.createElement('canvas'); 22 | document.body.appendChild(canvas); 23 | 24 | var ctx = canvas.getContext('2d'); 25 | canvas.width = img.width; 26 | canvas.height = img.height; 27 | ctx.drawImage(img, 0, 0); 28 | img.style.display = 'none'; 29 | var imgData = ctx.getImageData(0,0,canvas.width, canvas.height); 30 | 31 | var pixels = []; 32 | for(i=0;i 2 | 3 | 4 | 5 | 404 snake 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /snake/js/controls.js: -------------------------------------------------------------------------------- 1 | // Handle keyboard controls 2 | var keysDown = {}; 3 | 4 | var KEY_UP =38, KEY_DOWN =40, KEY_LEFT =37, KEY_RIGHT =39; 5 | 6 | addEventListener("keydown", function (e) { 7 | keysDown[e.keyCode] = true; 8 | }, false); 9 | 10 | addEventListener("keyup", function (e) { 11 | delete keysDown[e.keyCode]; 12 | }, false); 13 | 14 | //Called from update.js 15 | var controls = function(){ 16 | if (KEY_LEFT in keysDown) GAME_STATE.snake.move('l'); 17 | if (KEY_RIGHT in keysDown) GAME_STATE.snake.move('r'); 18 | if (KEY_UP in keysDown) GAME_STATE.snake.move('u'); 19 | if (KEY_DOWN in keysDown) GAME_STATE.snake.move('d'); 20 | } 21 | -------------------------------------------------------------------------------- /snake/js/draw.js: -------------------------------------------------------------------------------- 1 | // Create the canvas 2 | var canvas = document.getElementById("canvas"); 3 | var ctx = canvas.getContext("2d"); 4 | 5 | var SIZE = 32; 6 | 7 | Cell.prototype.draw = function(){ 8 | switch(this.content){ 9 | case 'SNAKE': 10 | ctx.fillStyle="gray"; 11 | break; 12 | case 'BLOCK': 13 | ctx.fillStyle="black"; 14 | break; 15 | case 'FOOD': 16 | ctx.fillStyle="silver"; 17 | break; 18 | } 19 | if (this.content && this.content != 'INVALID'){ 20 | ctx.fillRect(this.x*BLOCK_SIZE, this.y*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE) 21 | } 22 | } 23 | 24 | // Draw everything 25 | var render = function () { 26 | if (GAME_STATE.game_over) { 27 | ctx.fillStyle = "rgb(250, 0, 0)"; 28 | ctx.font = "48px Helvetica"; 29 | ctx.textAlign = "center"; 30 | ctx.textBaseline = "top"; 31 | ctx.fillText("Game Over", canvas.width / 2, canvas.height * 2 / 3); 32 | } 33 | else { 34 | // draw the background 35 | ctx.fillStyle="white"; 36 | ctx.fillRect(0,0,canvas.width,canvas.height); 37 | 38 | // draw game state 39 | Object.keys(GAME_STATE.grid).forEach(function(key){ 40 | GAME_STATE.grid[key].draw(); 41 | }); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /snake/js/framework.js: -------------------------------------------------------------------------------- 1 | // The main game loop 2 | var main = function () { 3 | var now = Date.now(); 4 | var delta = now - then; 5 | 6 | controls(); 7 | render(); 8 | 9 | then = now; 10 | 11 | // Request to do this again ASAP 12 | requestAnimationFrame(main); 13 | }; 14 | 15 | //var canvas = document.createElement('canvas'); 16 | //document.body.appendChild(canvas); 17 | 18 | 19 | 20 | setInterval(function(){update()}, 300); 21 | 22 | // Cross-browser support for requestAnimationFrame 23 | var w = window; 24 | requestAnimationFrame = 25 | ( 26 | w.requestAnimationFrame || 27 | w.webkitRequestAnimationFrame || 28 | w.msRequestAnimationFrame || 29 | w.mozRequestAnimationFrame 30 | ); 31 | 32 | // Let's play this game! 33 | var then = Date.now(); 34 | initGameState(main); 35 | -------------------------------------------------------------------------------- /snake/js/logic.js: -------------------------------------------------------------------------------- 1 | var BLOCK_SIZE = 16; 2 | 3 | function update(){ 4 | if (GAME_STATE.complete) GAME_STATE.snake.update(); 5 | } 6 | 7 | function initGameState(callback){ 8 | GAME_STATE.grid = {}; 9 | loadImagePixels("data/map.png", function(rows){ 10 | rows.forEach(function(row, i){ 11 | row.forEach(function(pixel, j){ 12 | var cell = new Cell(i,j); 13 | if (isColor(pixel, BLACK)) 14 | cell.content = 'BLOCK'; 15 | else if (isColor(pixel, GREEN)) 16 | cell.content = 'INVALID'; 17 | else if (isColor(pixel, RED)){ 18 | cell.content = 'SNAKE'; 19 | GAME_STATE.snake = new Snake(cell); 20 | } 21 | GAME_STATE.grid[[i,j]] = cell; 22 | GAME_STATE.gameHeight = i; 23 | GAME_STATE.gameWidth = j; 24 | }); 25 | }); 26 | Grid.newFood(); 27 | GAME_STATE.complete = true; 28 | callback(); 29 | }); 30 | } 31 | 32 | var GAME_STATE = { 33 | snake: null, 34 | grid: null, 35 | gameOver: false, 36 | gameHeight: 0, 37 | gameWidth: 0, 38 | } 39 | 40 | function Grid(){} 41 | 42 | Grid.get = function(x,y){ 43 | return GAME_STATE.grid[[x,y]]; 44 | } 45 | 46 | Grid.set = function(x, y, content){ 47 | var cell = GAME_STATE.grid[[x,y]]; 48 | cell.content = content; 49 | return cell; 50 | } 51 | 52 | Grid.unset = function(x, y){ 53 | var cell = GAME_STATE.grid[[x,y]]; 54 | cell.content = null; 55 | return cell; 56 | } 57 | 58 | Grid.randomFree = function(){ 59 | do{ 60 | var row = Math.floor(Math.random() * GAME_STATE.gameHeight); 61 | var col = Math.floor(Math.random() * GAME_STATE.gameWidth); 62 | var cell = this.get(row,col); 63 | } while (cell.content); 64 | return cell; 65 | } 66 | 67 | Grid.newFood = function (){ 68 | var freeCell = this.randomFree(); 69 | freeCell.content = 'FOOD'; 70 | } 71 | 72 | function Cell(x,y){ 73 | this.x = x; 74 | this.y = y; 75 | this.content = null; 76 | } 77 | 78 | 79 | function Snake(initCell){ 80 | this.body = [initCell]; 81 | this.dir = {x: -1, y: 0}; // start moving to the left 82 | } 83 | 84 | Snake.prototype.head = function(){ 85 | return this.body[0]; 86 | } 87 | 88 | Snake.prototype.tail = function(){ 89 | return this.body[-1]; 90 | } 91 | 92 | Snake.prototype.move = function(dir){ 93 | switch(dir){ 94 | case 'l': 95 | this.dir = {x: -1, y: 0}; 96 | break; 97 | case 'r': 98 | this.dir = {x: 1, y: 0}; 99 | break; 100 | case 'u': 101 | this.dir = {x: 0, y: -1}; 102 | break; 103 | case 'd': 104 | this.dir = {x: 0, y: 1}; 105 | break; 106 | } 107 | } 108 | 109 | Snake.prototype.update = function(dir){ 110 | // compute new position of the head 111 | var head = this.head(); 112 | var dir = this.dir; 113 | var newX = head.x + dir.x; 114 | var newY = head.y + dir.y; 115 | var newHead = Grid.get(newX, newY); 116 | 117 | // check collisions 118 | 119 | // if food, grow (on the head) 120 | if (newHead.content == 'FOOD') { 121 | newHead.content = 'SNAKE'; 122 | this.body.unshift(newHead); 123 | Grid.newFood(); 124 | } 125 | 126 | // if block, die 127 | else if (newHead.content != null){ 128 | GAME_STATE.game_over = true; 129 | } 130 | 131 | // else, move 132 | else { 133 | oldTail = this.body.pop(); 134 | oldTail.content = null; 135 | 136 | newHead.content = 'SNAKE'; 137 | this.body.unshift(newHead); 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /snake/js/utils.js: -------------------------------------------------------------------------------- 1 | var loadImagePixels = function(image, callback){ 2 | 3 | var img = new Image(); 4 | img.src = image; 5 | img.onload = function() { 6 | console.log("Image " + image + " loaded " + img.width + "x" +img.height); 7 | var canvas = document.createElement('canvas'); 8 | document.body.appendChild(canvas); 9 | 10 | var ctx = canvas.getContext('2d'); 11 | canvas.width = img.width; 12 | canvas.height = img.height; 13 | ctx.drawImage(img, 0, 0); 14 | img.style.display = 'none'; 15 | var imgData = ctx.getImageData(0,0,canvas.width, canvas.height); 16 | 17 | var pixels = []; 18 | for(i=0;i 2 | 3 | 4 | 5 | 404 Space Invaders 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /space-invaders/js/ai.js: -------------------------------------------------------------------------------- 1 | var updateEnemies = function(){ 2 | 3 | if (hasReachedEnd()) { 4 | changeEnemyDirection(); 5 | return; 6 | } 7 | var enemyMovement; 8 | if (enemyDirection == LEFT) enemyMovement = -10; 9 | else enemyMovement = 10; 10 | 11 | for (var i = 0 ; i < enemies.length; i++){ 12 | var enemy = enemies[i]; 13 | enemy.x = enemy.x + enemyMovement ; 14 | } 15 | } 16 | 17 | var hasReachedEnd = function(){ 18 | var threshold = 32; 19 | 20 | for (var i = 0 ; i < enemies.length; i++){ 21 | var enemy = enemies[i]; 22 | if (enemyDirection == RIGHT && enemy.x > canvas.width - threshold - 32) return true; 23 | else if (enemyDirection == LEFT && enemy.x < threshold) return true; 24 | } 25 | return false; 26 | } 27 | 28 | var changeEnemyDirection = function(){ 29 | if (enemyDirection == LEFT) enemyDirection = RIGHT; 30 | else enemyDirection = LEFT; 31 | 32 | for (var i = 0 ; i < enemies.length; i++){ 33 | var enemy = enemies[i]; 34 | enemy.y = enemy.y + 30 ; 35 | } 36 | } 37 | 38 | var enemyShot = function(){ 39 | var enemy = enemies[Math.floor(Math.random()*enemies.length)]; 40 | enemyShots.push({x:enemy.x, y: enemy.y}); 41 | } 42 | -------------------------------------------------------------------------------- /space-invaders/js/assets.js: -------------------------------------------------------------------------------- 1 | /** 2 | Ship and laser assets by Cpt_Flash 3 | Available at: http://opengameart.org/content/space-ships-2-units 4 | License: GPL 3.0 5 | **/ 6 | 7 | // Background image 8 | var bgReady = false; 9 | var bgImage = new Image(); 10 | 11 | bgImage.onload = function () { 12 | bgReady = true; 13 | }; 14 | bgImage.src = "images/background.png"; 15 | 16 | // Hero image 17 | var heroReady = false; 18 | var heroImage = new Image(); 19 | heroImage.onload = function () { 20 | heroReady = true; 21 | }; 22 | heroImage.src = "images/32_player.png"; 23 | 24 | // Monster image 25 | var monsterReady = false; 26 | var monsterImage = new Image(); 27 | monsterImage.onload = function () { 28 | monsterReady = true; 29 | }; 30 | monsterImage.src = "images/32_enemy.png"; 31 | 32 | // Hero image 33 | var shotReady = false; 34 | var shotImage = new Image(); 35 | shotImage.onload = function () { 36 | shotReady = true; 37 | }; 38 | shotImage.src = "images/32_laser_blue.png"; 39 | 40 | var shotRedReady = false; 41 | var shotRedImage = new Image(); 42 | shotRedImage.onload = function () { 43 | shotRedReady = true; 44 | }; 45 | shotRedImage.src = "images/32_laser_red.png"; 46 | -------------------------------------------------------------------------------- /space-invaders/js/controls.js: -------------------------------------------------------------------------------- 1 | // Handle keyboard controls 2 | var keysDown = {}; 3 | 4 | var KEY_UP =38, KEY_DOWN =40, KEY_LEFT =37, KEY_RIGHT =39; 5 | 6 | addEventListener("keydown", function (e) { 7 | keysDown[e.keyCode] = true; 8 | }, false); 9 | 10 | addEventListener("keyup", function (e) { 11 | delete keysDown[e.keyCode]; 12 | }, false); 13 | 14 | addEventListener("mousemove", function(evt) { 15 | hero.x = evt.pageX - 24 ; 16 | }, false); 17 | 18 | var lastShot = 0; 19 | var SHOT_DELAY = 1000; 20 | 21 | addEventListener("click", function(evt) { 22 | if (!hero.dead) { 23 | var now = new Date().getTime(); 24 | if (lastShot +SHOT_DELAY < now ) { 25 | lastShot = now; 26 | playerShots.push({x:evt.pageX, y: hero.y}); 27 | } 28 | } 29 | }, false); 30 | 31 | 32 | //Called from update.js 33 | var controls = function(modifier){ 34 | if (KEY_LEFT in keysDown) { // Player holding left 35 | hero.x -= hero.speed * modifier; 36 | } 37 | if (KEY_RIGHT in keysDown) { // Player holding right 38 | hero.x += hero.speed * modifier; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /space-invaders/js/draw.js: -------------------------------------------------------------------------------- 1 | // Create the canvas 2 | var canvas = document.getElementById("canvas"); 3 | var ctx = canvas.getContext("2d"); 4 | 5 | var SIZE = 32; 6 | 7 | var vy = 0; 8 | // Draw everything 9 | var render = function () { 10 | if (bgReady) { 11 | 12 | ctx.drawImage(bgImage, 0, vy); 13 | ctx.drawImage(bgImage, 0, bgImage.height-Math.abs(vy)); 14 | 15 | if (Math.abs(vy) > bgImage.height) vy = 0; 16 | vy -= 2; 17 | } 18 | 19 | if (heroReady && !hero.dead) { 20 | ctx.drawImage(heroImage, hero.x, hero.y,SIZE, SIZE); 21 | } 22 | 23 | for (var i=0; i < enemies.length; i++){ 24 | var enemy = enemies[i]; 25 | ctx.drawImage(monsterImage, enemy.x, enemy.y,SIZE, SIZE); 26 | 27 | } 28 | for (var i=0; i < enemyShots.length; i++){ 29 | var shot = enemyShots[i]; 30 | ctx.drawImage(shotRedImage, shot.x, shot.y ,SIZE, SIZE); 31 | } 32 | 33 | for (var i=0; i < playerShots.length; i++){ 34 | var shot = playerShots[i]; 35 | ctx.drawImage(shotImage, shot.x, shot.y ,SIZE, SIZE); 36 | } 37 | 38 | if (hero.dead){ 39 | ctx.fillStyle = "rgb(250, 0, 0)"; 40 | ctx.font = "48px Helvetica"; 41 | ctx.textAlign = "center"; 42 | ctx.textBaseline = "top"; 43 | ctx.fillText("Game Over", canvas.width / 2, canvas.height/ 2); 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /space-invaders/js/game.js: -------------------------------------------------------------------------------- 1 | // Reset the game when the player catches a monster 2 | var reset = function () { 3 | hero.x = canvas.width / 2; 4 | hero.y = canvas.height - 64; 5 | }; 6 | 7 | // The main game loop 8 | var main = function () { 9 | var now = Date.now(); 10 | var delta = now - then; 11 | 12 | update(delta / 1000); 13 | render(); 14 | 15 | then = now; 16 | 17 | // Request to do this again ASAP 18 | requestAnimationFrame(main); 19 | }; 20 | 21 | // Cross-browser support for requestAnimationFrame 22 | var w = window; 23 | requestAnimationFrame = w.requestAnimationFrame || w.webkitRequestAnimationFrame || w.msRequestAnimationFrame || w.mozRequestAnimationFrame; 24 | 25 | // Let's play this game! 26 | var then = Date.now(); 27 | reset(); 28 | main(); 29 | -------------------------------------------------------------------------------- /space-invaders/js/objects.js: -------------------------------------------------------------------------------- 1 | // Game objects 2 | var hero = {}; 3 | var playerShots = []; 4 | var enemyShots = []; 5 | 6 | var shotSpeed = 200; 7 | 8 | var RIGHT = 1, LEFT = 0; 9 | var enemies = []; 10 | var enemyDirection = RIGHT; 11 | var loadEnemy = function(offset, json){ 12 | var size = 32; 13 | 14 | for (var i = 0; i< json.length; i++){ 15 | var pos = json[i]; 16 | var enemy = {x: offset.x + pos.x * size, y: offset.y + pos.y * size} ; 17 | enemies.push(enemy); 18 | } 19 | } 20 | 21 | loadJSON('enemies.json', 22 | function(data) { 23 | 24 | loadEnemy({ x: 50, y: 50 }, data.four); 25 | loadEnemy({ x: 180, y: 50 }, data.zero); 26 | loadEnemy({ x: 300, y: 50 }, data.four); 27 | }, 28 | function(xhr) { console.error(xhr); } 29 | ); 30 | -------------------------------------------------------------------------------- /space-invaders/js/update.js: -------------------------------------------------------------------------------- 1 | 2 | // Update game objects 3 | var update = function (modifier) { 4 | controls(modifier); 5 | 6 | for (var i = 0; i< playerShots.length; i++){ 7 | var shot = playerShots[i]; 8 | shot.y = shot.y - modifier * shotSpeed; 9 | 10 | for (var e= 0; e < enemies.length; e++){ 11 | var enemy = enemies[e]; 12 | if (collision(shot, enemy)) { 13 | console.log("Collision with enemy ", shot, enemy); 14 | playerShots.splice(i, 1); 15 | enemies.splice(e, 1); 16 | break; 17 | } 18 | } 19 | } 20 | 21 | for (var i = 0; i< enemyShots.length; i++){ 22 | var shot = enemyShots[i]; 23 | shot.y = shot.y + modifier * shotSpeed; 24 | 25 | if (collision(shot, hero)) { 26 | console.log("Collision with hero ", shot, hero); 27 | enemyShots.splice(i, 1); 28 | hero.dead = true; 29 | } 30 | } 31 | //updateEnemies(); 32 | // Are they touching? 33 | }; 34 | 35 | var collision = function(shot, enemy){ 36 | return shot.x <= (enemy.x + 32) 37 | && enemy.x <= (shot.x + 32) 38 | && shot.y <= (enemy.y + 32) 39 | && enemy.y <= (shot.y + 32) 40 | } 41 | 42 | //AI METHODS 43 | setInterval(function(){updateEnemies()}, 500); 44 | 45 | setInterval(function(){enemyShot()}, 1000); 46 | -------------------------------------------------------------------------------- /space-invaders/js/utils.js: -------------------------------------------------------------------------------- 1 | var loadJSON = function (path, success, error) 2 | { 3 | var oReq = new XMLHttpRequest(); 4 | oReq.onload = function reqListener () { 5 | jsonData = JSON.parse(this.responseText); 6 | console.log("Loading JSON data", jsonData); 7 | success(jsonData); 8 | };; 9 | oReq.open("get", path, true); 10 | oReq.send(); 11 | } 12 | --------------------------------------------------------------------------------