Source: Snake.js

/*jslint plusplus: true, white: true, indent: 2, maxlen: 90 */
/**
 *  @author    Wolfgang Kowarschick
 *  @copyright 2013, Wolfgang Kowarschick
 *  <br/>
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted under the terms of the 
 *  Creative Commons License Attribution-NonCommercial-ShareAlike 3.0 Unported    
 *  (CC BY-NC-SA 3.0: http://creativecommons.org/licenses/by-nc-sa/3.0/). 
 */

(function($wk$, $app$)
{ "use strict";
////////////////////////////////////////////////////////////////////////////////
// Class "Snake"
////////////////////////////////////////////////////////////////////////////////

//import
var WKcSignaler = $wk$.observer.WKcSignaler,
    Body        = $app$.game.snake.Body,
    Food        = $app$.game.snake.Food,
    Wall        = $app$.game.snake.Wall,
        
    l_direction,
    
    l_signal_hit_wall, l_signal_hit_snake;

/** 
 *  @class
 *  @name $app$.game.snake.Snake
 *  
 *  @param {Object} p_init
 *         Should contain three initialization objects to initialize the snake:
 *         <code>p_init.tail</code>,
 *         <code>p_init.length</code>, and
 *         <code>p_init.direction</code>.
 *  @param {$app$.game.snake.Plane} p_plane 
 *         The plane where the snake is to be placed. 
 */
function Snake(p_init, p_plane)
{ WKcSignaler.call(this);  // Call the constructor of the super class.

  this.v_plane          = p_plane;
  this.v_init_tail      = p_init.tail;
  this.v_init_direction = l_direction[p_init.direction];
  this.v_init_length    = p_init.length;
}

/** @constant {Object} $app$.game.snake.Snake.NORTH */
Snake.NORTH = { x:  0, y: -1 };
/** @constant {Object} $app$.game.snake.Snake.EAST  */
Snake.EAST  = { x: +1, y:  0 };
/** @constant {Object} $app$.game.snake.Snake.SOUTH */
Snake.SOUTH = { x:  0, y: +1 };
/** @constant {Object} $app$.game.snake.Snake.WEST  */
Snake.WEST  = { x: -1, y:  0 };

Snake.NORTH.left  = Snake.WEST;
Snake.NORTH.right = Snake.EAST;
Snake.EAST.left   = Snake.NORTH;
Snake.EAST.right  = Snake.SOUTH;
Snake.SOUTH.left  = Snake.EAST;
Snake.SOUTH.right = Snake.WEST;
Snake.WEST.left   = Snake.SOUTH;
Snake.WEST.right  = Snake.NORTH;

l_direction = { north: Snake.NORTH, east: Snake.EAST, 
                south: Snake.SOUTH, west: Snake.WEST
              };

/** @constant {String} $app$.game.snake.Snake.LEFT  */
Snake.LEFT  = "left";
/** @constant {String} $app$.game.snake.Snake.RIGHT */
Snake.RIGHT = "right";

/** @constant {Object} $app$.game.snake.Snake.GROWN (a signal type) */
Snake.GROWN  = "grown";

/** @constant {Object} $app$.game.snake.Snake.KILLED (a signal type) */
Snake.KILLED = "killed";

/** @constant {Object} $app$.game.snake.Snake.HIT_WALL */
Snake.HIT_WALL = "hitWall";

/** @constant {Object} $app$.game.snake.Snake.HIT_SNAKE */
Snake.HIT_SNAKE = "hitSnake";

l_signal_hit_wall  = { type: Snake.KILLED, reason: Snake.HIT_WALL  }; 

l_signal_hit_snake = { type: Snake.KILLED, reason: Snake.HIT_SNAKE };

//Inherit all attributes and methods from "WKcSignaler".
Snake.prototype = Object.create(WKcSignaler.prototype);

/** 
 *  The current length of the snake.
 *  @var {int} $app$.game.snake.Snake#length  
 */
    
/**
 *  Resets the snake. 
 *  
 *  @method
 *  @name $app$.game.snake.Snake#reset
 */
Snake.prototype.reset =
function()
{ var i, n,
      l_tail = this.v_init_tail;

  // Cleanup the plane
  this.v_plane.reset();
 
  // Place the initial snake on the plane.
  this.v_head      = 
  this.v_tail      = new Body(l_tail.x, l_tail.y);
  this.v_direction = this.v_init_direction;
  this.v_plane.addItem(this.v_head);
  for (i = 1, n = this.v_init_length; i < n; i++)
  { this.v_growing = true;
    this.move();
  }
  this.length = n;
  // Inform all observers about the change of the state.
  this.signal(Snake.GROWN);
};
    
/**
 *  Moves the snake one field further. If it hits a food item, it grows.
 *  If it hits a wall or body item, it dies.
 *  
 *  @method
 *  @name $app$.game.snake.Snake#move
 *  
 *  @returns {Boolean} 
 *           <code>true</code>, if and only if the snake is dead.
 */
Snake.prototype.move =
function()
{ var l_plane     = this.v_plane,
      l_head      = this.v_head,
      l_head_new,
      l_tail      = this.v_tail,
      l_direction = this.v_direction,
      l_x         = l_head.x + l_direction.x,
      l_y         = l_head.y + l_direction.y,
      l_item      = l_plane.getItem(l_x, l_y);
 
  if (!l_item || l_item instanceof Food)
  { l_head_new  = new Body(l_x, l_y);
    l_head.pred = l_head_new;
    this.v_head = l_head_new;
    l_plane.addItem(l_head_new);
    // If food has been eaten, the snake is growing.
    if (l_item)
    { this.v_growing = true;
      this.length++;
      l_plane.foodNr--;
          
      // Inform all observers about the change of the state.
      this.signal(Snake.GROWN);
    }
    
    // If the snake is not growing, the current tail is removed.
    if (!this.v_growing)
    { this.v_tail = l_tail.pred;
      l_plane.removeItem(l_tail);
    }
    
    this.v_growing = false;
  }
  else // The snake is dead!
  { // Inform all observer about the change of the state.
    if (l_item instanceof Wall)
    { this.signal(l_signal_hit_wall); }
    else // l_item instanceof Body
    { this.signal(l_signal_hit_snake); }
  }
    };
     
/**
 *  Turns the snake 90° to the left or to the right. 
 *  
 *  @method
 *  @name $app$.game.snake.Snake#turnLeft
 *  
 *  @param {String} p_direction
 *                  Either <code>Snake.LEFT</code> or <code>Snake.RIGHT</code>.
 */
Snake.prototype.turn =
function(p_direction) 
{ this.v_direction = this.v_direction[p_direction]; };
      
$app$.game.snake.Snake = Snake;

////////////////////////////////////////////////////////////////////////////////
// End of Class "Snake"
////////////////////////////////////////////////////////////////////////////////
}(this.$wk$, this.$app$));
W. Kowarschick © 2013 (CC BY-NC-SA 3.0: http://creativecommons.org/licenses/by-nc-sa/3.0/)
Documentation generated by JSDoc 3.3.0-dev on Sun Dec 15 2013 11:49:53 GMT+0100 (MEZ) using the DocStrap template.