/*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$));