/* * * jquery.snake.js - a nibbles clone * Copyright (c) 2008 Richard Willis * MIT license : http://www.opensource.org/licenses/mit-license.php * Project : http://jquery-snakey.googlecode.com/ * Contact : willis.rh@gmail.com * */ var Snake = { $map : {}, $cherry : {}, $overlay : {}, seg : {}, wallseg : {}, cache : {}, cacheimages : ['../img/snake/cherry.jpg'], animateTimer : 0, score : 0, grid : 0, level : 1, lives : 3, speed : 0, cherriesEaten : 0, wall : 0, // are the outer map walls an obsticle? setup : function() { // pre-cache images for(var i in Snake.cacheimages) { var img = new Image(); img.src = Snake.cacheimages[i]; } // build map Snake.$map = $("#map1"); Snake.$map.width = Snake.$map.innerWidth(); Snake.$map.height = Snake.$map.innerHeight(); // build and prepend overlay to map Snake.$overlay = $('
').hide().prependTo(Snake.$map); // build and append cherry to map Snake.$cherry = $('
').appendTo(Snake.$map); // listen for key press, store keycode Snake.cache.keyCode = [0,0]; document.onkeydown = function(e){ // preventing default event behaviour causes issues with IE; // need to research further! keycode = (e == null) ? event.keyCode : e.which; switch(keycode) { case 71 : (!$.browser.msie && e.preventDefault()); Snake.toggleGrid(); break; case 80 : (!$.browser.msie && e.preventDefault()); Snake.pause(); break; case 78 : (!$.browser.msie && e.preventDefault()); Snake.newGame(true); break; case 37 : case 38 : case 39 : case 40 : (!$.browser.msie && e.preventDefault()); Snake.cache.keyCode[0] = Snake.cache.keyCode[1]; Snake.cache.keyCode[1] = keycode; break; default : break; } }; }, start : function() { // set initial speed Snake.speed = Level[Snake.level][0].speed; // show the cherry, and start the animation Snake.$cherry.fadeIn(function(){ Snake.animateTimer = setInterval(Snake.animate, Snake.speed); }); }, newGame : function(reset) { Snake.cherriesEaten = 0; // reset animation timer clearInterval(Snake.animateTimer); Snake.animateTimer = 0; // reset score Snake.score = reset ? 0 : Snake.score; $("#stats-score").html(Snake.score+""); // reset level Snake.level = reset ? 1 : Snake.level; $("#stats-level").html(Snake.level); // reset lives Snake.lives = reset ? 3 : Snake.lives; $("#stats-lives").html(Snake.lives); // reset level cherries eaten and total $("#stats-eaten").html(Snake.cherriesEaten+""); $("#stats-totcherries").html(Level[Snake.level][0].cherries); // remove any wall & snake segments $(".wall, .snake").remove(); // hide the cherry Snake.$cherry.hide(); // update map message $("#map-msg").hide().html('Level '+Snake.level+'
Eat '+Level[Snake.level][0].cherries+' cherries
'+ '('+Snake.lives+' '+(Snake.lives>1?'lives':'life')+' remaining)
' ).fadeIn(500, function(){ setTimeout(function(){ // hide map message $("#map-msg").fadeOut(500, function(){ // hide overlay Snake.$overlay.hide(); // reset and generate wall Snake.wallseg = {}; Snake.Wall.generate(); // generate cherry Snake.Cherry.generate(false); // reset, remove and re-append snake segments to map Snake.seg = {length:Level[Snake.level][0].length}; for(var i=0;i').appendTo(Snake.$map); Snake.seg[i].top = Snake.seg[i].left = 0; } // start animation setTimeout(function(){ // reset direction Snake.cache.keyCode[0] = 0; Snake.cache.keyCode[1] = 39; Snake.start(); }, 1000); }); }, 2500); }); }, animate : function() { // adjust segment position list for(var i=1;i Snake.$map.width-10) { Snake.wall && Snake.gameOver(); Snake.seg[0].left = 0; } }else if (keycode == 40) { //down Snake.seg[0].top += 10; if (Snake.seg[0].top > Snake.$map.height-10) { Snake.wall && Snake.gameOver(); Snake.seg[0].top = 0; } } else if (keycode == 38) { //up Snake.seg[0].top -= 10; if (Snake.seg[0].top < 0) { Snake.wall && Snake.gameOver(); Snake.seg[0].top = Snake.$map.height-10; } } else if (keycode == 37) { //left Snake.seg[0].left -= 10; if (Snake.seg[0].left < 0) { Snake.wall && Snake.gameOver(); Snake.seg[0].left = Snake.$map.width-10; } } // check if snake has eaten a cherry (Snake.seg[0].left == Snake.Cherry.left && Snake.seg[0].top == Snake.Cherry.top) && Snake.advance(); // unset Snake.seg[0], gotta be an easier way! var seg = {}; for(var i=1;i< Snake.seg.length;i++) {seg[i-1]=Snake.seg[i];} // check if snake has slithered into itself (Snake.in_obj(Snake.seg[0], seg)) && Snake.gameOver(); // check if snake has slithered into a wall obstacle (Snake.in_obj(Snake.seg[0], Snake.wallseg)) && Snake.gameOver(); // check if cherries eaten match total: finished level.. advance to next level (Snake.cherriesEaten == Level[Snake.level][0].cherries) && Snake.advanceLevel(); // reposition snake segments on map for(var i=0;i') .css({left:Snake.seg[1].left+"px",top:Snake.seg[1].top+"px",display:"block"}) .appendTo(Snake.$map); // position new snake segment Snake.seg[x].top = Snake.seg[x-1].top; Snake.seg[x].left = Snake.seg[x-1].left; // reposition cherry Snake.Cherry.generate(); // adjust score Snake.score += 10; $("#stats-score").html(Snake.score); // update cherries eaten Snake.cherriesEaten++; $("#stats-eaten").html(Snake.cherriesEaten); // adjust speed Snake.speed -= 1; $("#stats-speed").html(Snake.speed); clearInterval(Snake.animateTimer); Snake.animateTimer = setInterval(Snake.animate, Snake.speed); return false; }, advanceLevel : function() { if (Snake.level == Level.length-1) { Snake.finishedGame(); } else { Snake.level++; Snake.speed = Level[Snake.level][0].speed; Snake.newGame(); } }, toggleGrid : function(){ var background; if (!Snake.grid) { background = "transparent url(img/snake/grid_bg.gif)"; Snake.grid = 1; } else { background = "transparent"; Snake.grid = 0; } Snake.$map.css({background:background}); }, pause : function(){ if (Snake.animateTimer == 0) { Snake.start(); Snake.$overlay.hide(); $("#map-msg").fadeOut(); } else { clearInterval(Snake.animateTimer); Snake.animateTimer = 0; Snake.$overlay.show(); $("#map-msg").html("
Paused").fadeIn(); } }, gameOver : function(){ if (Snake.lives-1) { Snake.lives--; Snake.newGame(); } else { Snake.pause(); $("#map-msg").html('
You Died
Play again?
'); } }, finishedGame : function(){ Snake.pause(); $("#map-msg").html('
Well Done! You finished.
Play again?
'); }, Cherry : { left : 0, top : 0, generate : function(show){ do { Snake.Cherry.left = Math.round((Math.random()*(Snake.$map.width-10))/10)*10; Snake.Cherry.top = Math.round((Math.random()*(Snake.$map.height-10))/10)*10; } while (Snake.in_obj(Snake.Cherry, Snake.wallseg) || Snake.in_obj(Snake.Cherry, Snake.seg)); Snake.$cherry.css({ left:Snake.Cherry.left+"px", top:Snake.Cherry.top+"px" }); show == undefined && Snake.$cherry.hide().fadeIn(); } }, // wall obstacles Wall : { generate : function(){ var walls = Level[Snake.level], c = 0, t, l, i, n; // append multiple walls for(i=1;i').css({top:t+"px",left:l+"px"}).appendTo(Snake.$map); Snake.wallseg[c].left = l; Snake.wallseg[c].top = t; c ++; t += 10; } } } }, // check for an object in an object collection in_obj : function(obj_needle, obj_haystack) { for(var i in obj_haystack) { if (obj_haystack[i].left === obj_needle.left && obj_haystack[i].top === obj_needle.top){ return true; } } return false; } }, Level = [ , [ {cherries : 5, length : 10, speed : 100}, {seg : 30, top : 50, left : 200} ], [ {cherries : 5, length : 15, speed : 95}, {seg : 30, top : 50, left : 100}, {seg : 30, top : 50, left : 300} ], [ {cherries : 5, length : 20, speed : 90}, {seg : 12, top : 10, left : 50}, {seg : 12, top : 0, left : 350}, {seg : 12, top : 270, left : 350}, {seg : 12, top : 280, left : 50}, {seg : 24, top : 80, left : 200} ], [ {cherries : 5, length : 25, speed : 85}, {seg : 19, top : 0, left : 200}, {seg : 20, top : 200, left : 200}, {seg : 40, top : 0, left : 0}, {seg : 40, top : 0, left : 390} ], [ {cherries : 5, length : 30, speed : 80}, {seg : 20, top : 10, left : 50}, {seg : 20, top : 0, left : 150}, {seg : 20, top : 10, left : 250}, {seg : 20, top : 0, left : 350}, {seg : 20, top : 200, left : 20}, {seg : 20, top : 200, left : 120}, {seg : 20, top : 200, left : 220}, {seg : 20, top : 200, left : 320} ], [ {cherries : 5, length : 35, speed : 75}, {seg : 2, top : 10, left : 200}, {seg : 2, top : 40, left : 200}, {seg : 2, top : 70, left : 200}, {seg : 2, top : 100, left : 200}, {seg : 2, top : 130, left : 200}, {seg : 2, top : 160, left : 200}, {seg : 2, top : 190, left : 200}, {seg : 2, top : 220, left : 200}, {seg : 2, top : 250, left : 200}, {seg : 2, top : 280, left : 200}, {seg : 2, top : 310, left : 200}, {seg : 2, top : 340, left : 200}, {seg : 2, top : 370, left : 200}, {seg : 20, top : 100, left : 280}, {seg : 20, top : 100, left : 120}, {seg : 30, top : 50, left : 60}, {seg : 30, top : 50, left : 340}, {seg : 40, top : 0, left : 0}, {seg : 40, top : 0, left : 390} ] ];