diff --git a/Grid.js b/Grid.js
new file mode 100644
index 0000000..f2fe38d
--- /dev/null
+++ b/Grid.js
@@ -0,0 +1,49 @@
+this.Grid = (function()
+{
+ function Grid(size, changeCallback)
+ {
+ var grid = new Array(size);
+
+ var data = {
+ grid: grid,
+ size: size
+ };
+
+ this.get = function(row, col)
+ {
+ if (isOutOfBounds(data, row, col))
+ throw new RangeError("Row or Column value out of bounds");
+ return grid[row] && grid[row][col];
+ }
+ this.set = function(row, col, value)
+ {
+ if (isOutOfBounds(data, row, col))
+ throw new RangeError("Row or Column value out of bounds");
+
+ if (!grid[row])
+ grid[row] = new Array(size);
+ var before = grid[row][col];
+ grid[row][col] = value;
+
+ if (typeof changeCallback === "function")
+ changeCallback(row, col, before, value);
+
+ return before;
+ }
+
+ this.isOutOfBounds = isOutOfBounds.bind(this, data);
+
+ Object.defineProperty(this, "size", {
+ get: function() {return size; },
+ enumerable: true
+ });
+
+ }
+
+ function isOutOfBounds(data, row, col)
+ {
+ return row < 0 || row >= data.size || col < 0 || col >= data.size;
+ }
+
+ return Grid;
+})()
\ No newline at end of file
diff --git a/color.js b/color.js
new file mode 100644
index 0000000..2ba343c
--- /dev/null
+++ b/color.js
@@ -0,0 +1,87 @@
+this.Color = function()
+{
+
+ function Color(h, s, l, a)
+ {
+ verifyRange(h, s, l);
+ if (a === undefined) a = 1;
+ else verifyRange(a);
+
+ Object.defineProperties(this, {
+ "hue": {value: h, enumerable: true},
+ "sat": {value: s, enumerable: true},
+ "lum": {value: l, enumerable: true},
+ "alpha": {value: a, enumerable: true},
+ });
+ }
+
+ function verifyRange()
+ {
+ for (var i = 0; i < arguments.length; i++)
+ {
+ if (arguments[i] < 0 || arguments[i] > 1)
+ throw new RangeError("H, S, L, and A parameters must be between the range [0, 1]");
+ }
+ }
+
+ Color.prototype.deriveLumination = function(amount)
+ {
+ var lum = this.lum + amount;
+ lum = Math.min(Math.max(lum, 0), 1);
+ return new Color(this.hue, this.sat, lum, this.alpha);
+ };
+
+ Color.prototype.deriveHue = function(amount)
+ {
+ var hue = this.hue - amount;
+ return new Color(hue - Math.floor(hue), this.sat, this.lum, this.alpha);
+ };
+
+ Color.prototype.deriveSaturation = function(amount)
+ {
+ var sat = this.sat + amount;
+ sat = Math.min(Math.max(sat, 0), 1);
+ return new Color(this.hue, sat, this.lum, this.alpha);
+ };
+
+ Color.prototype.deriveAlpha = function(newAlpha)
+ {
+ verifyRange(newAlpha);
+ return new Color(this.hue, this.sat, this.lum, newAlpha);
+ };
+
+ Color.prototype.rgbString = function() {
+ var rgb = hslToRgb(this.hue, this.sat, this.lum);
+ rgb[3] = this.a;
+ return 'rgba(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ', ' + this.alpha + ')';
+ };
+
+
+ //http://stackoverflow.com/a/9493060/7344257
+ function hslToRgb(h, s, l){
+ var r, g, b;
+
+ if(s == 0){
+ r = g = b = l; // achromatic
+ }else{
+ var hue2rgb = function hue2rgb(p, q, t){
+ if(t < 0) t += 1;
+ if(t > 1) t -= 1;
+ if(t < 1/6) return p + (q - p) * 6 * t;
+ if(t < 1/2) return q;
+ if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ return p;
+ };
+
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ var p = 2 * l - q;
+ r = hue2rgb(p, q, h + 1/3);
+ g = hue2rgb(p, q, h);
+ b = hue2rgb(p, q, h - 1/3);
+ }
+
+ return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
+ }
+
+ return Color;
+}()
\ No newline at end of file
diff --git a/game.js b/game.js
index e9ca7d8..55bfc6d 100644
--- a/game.js
+++ b/game.js
@@ -1,14 +1,12 @@
var Player;
-
if (!Player)
throw new Error("Requires player.js");
-//Thanks to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
-function getRandomInt(min, max) {
- min = Math.ceil(min);
- max = Math.floor(max);
- return Math.floor(Math.random() * (max - min)) + min;
-}
+var Grid;
+if (!Grid)
+ throw new Error("Requires grid.js");
+
+
/**
* Provides requestAnimationFrame in a cross browser way.
@@ -35,36 +33,47 @@ $(function() {
var CELL_WIDTH = 30;
var SPEED = 5;
var SHADOW_OFFSET = 5;
-
+ var MAX_FRAMES = 16;
+ var DROP_HEIGHT = 16;
+ var BOUNCE_FRAMES = 8;
+ var DROP_SPEED = 2;
var canvas = $("#main-ui")[0];
var ctx = canvas.getContext('2d');
var width = canvas.width = window.innerWidth - 20;
var height = canvas.height = window.innerHeight - 20;
-
+
var players = [];
- var grid = [];
+ var animateOff = true;
+ var animateGrid = new Grid(GRID_SIZE);
+ var grid = new Grid(GRID_SIZE, function(row, col, before, after) {
+ if (before === after || animateOff)
+ return;
+ animateGrid.set(row, col, {
+ before: before,
+ after: after,
+ frame: 0
+ });
+ });
//Load players.
- for (var p = 0; p < 1; p++)
+ for (var p = 0; p < 9; p++)
{
//TODO: socket loading.
- players[p] = new Player(null, grid, p);
+ players[p] = new Player(null, grid, p, getRandomInt(0, GRID_SIZE), getRandomInt(0, GRID_SIZE));
}
//Load grid.
- for (var r = 0; r < GRID_SIZE; r++)
+ for (var r = 0; r < grid.size; r++)
{
- grid[r] = [];
- for (var c = 0; c < GRID_SIZE; c++)
+ for (var c = 0; c < grid.size; c++)
{
//TODO: load data.
- if (Math.random() < .9)
- grid[r][c] = -1;
- else
- grid[r][c] = players[getRandomInt(0, players.length)];
+ if (Math.random() > .9)
+ grid.set(r, c, players[getRandomInt(0, players.length)]);
}
}
+ animateOff = false;
var frameCount = 0;
var animateTo = [0, 0];
@@ -72,6 +81,7 @@ $(function() {
//TODO: current player index
var user = players[0];
+ centerOnPlayer(user, offset);
function update()
{
@@ -115,9 +125,9 @@ $(function() {
squaresIntersect(players[i].startY, players[j].startY))
{
//...if one player is own his own territory, the other is out.
- if (grid[players[i].row][players[i].col] === players[i])
+ if (grid.get(players[i].row, players[i].col) === players[i])
removing[j] = true;
- else if (grid[players[j].row][players[j].col] === players[j])
+ else if (grid.get(players[j].row, players[j].col) === players[j])
removing[i] = true;
else
{
@@ -148,13 +158,25 @@ $(function() {
dead.forEach(function(val) {
console.log(val.name + " is dead");
});
+ for (var r = 0; r < grid.size; r++)
+ {
+ for (var c = 0; c < grid.size; c++)
+ {
+ if (dead.indexOf(grid.get(r, c)) !== -1)
+ grid.set(r, c, null);
+ }
+ }
//TODO: animate dead, and if this player is dead.
- var xOff = Math.floor(user.posX - (width - CELL_WIDTH) / 2);
- var yOff = Math.floor(user.posY - (height - CELL_WIDTH) / 2);
-
- animateTo[0] = Math.min(Math.max(xOff, 0), GRID_SIZE * CELL_WIDTH);
- animateTo[1] = Math.min(Math.max(yOff, 0), GRID_SIZE * CELL_WIDTH);
+ centerOnPlayer(user, animateTo);
+ }
+
+ function centerOnPlayer(player, pos)
+ {
+ var xOff = Math.floor(player.posX - (width - CELL_WIDTH) / 2);
+ var yOff = Math.floor(player.posY - (height - CELL_WIDTH) / 2);
+ pos[0] = Math.min(Math.max(xOff, 0), grid.size * CELL_WIDTH - width / 2);
+ pos[1] = Math.min(Math.max(yOff, 0), grid.size * CELL_WIDTH - width / 2);
}
function area(player)
@@ -168,6 +190,13 @@ $(function() {
return Math.abs(player.startX - xDest);
}
+ //Thanks to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
+ function getRandomInt(min, max) {
+ min = Math.ceil(min);
+ max = Math.floor(max);
+ return Math.floor(Math.random() * (max - min)) + min;
+ }
+
function squaresIntersect(a, b)
{
if (a < b)
@@ -176,44 +205,122 @@ $(function() {
return a < b + CELL_WIDTH;
}
+ function paintGridLines()
+ {
+ ctx.fillStyle = 'lightgray';
+ ctx.beginPath();
+ for (var x = modRotate(-offset[0], CELL_WIDTH); x < width; x += CELL_WIDTH)
+ {
+ ctx.moveTo(x, 0);
+ ctx.lineTo(x, height);
+ }
+ for (var y = modRotate(-offset[1], CELL_WIDTH); y < height; y+= CELL_WIDTH)
+ {
+ ctx.moveTo(0, y);
+ ctx.lineTo(width, y);
+ }
+ ctx.stroke();
+ }
+
+ function modRotate(val, mod)
+ {
+ var res = val % mod;
+ if (res >= 0)
+ return res;
+ else
+ return mod + res;
+ }
+
function paintGrid()
{
//Paint bottom grid lines.
- //ctx.fillStyle = 'lightgray';
- //ctx.beginPath();
- //for (var x = modRotate(-offset[0], CELL_WIDTH); x < width; x += CELL_WIDTH)
- //{
- // ctx.moveTo(x, 0);
- // ctx.lineTo(x, height);
- //}
- //for (var y = modRotate(-offset[1], CELL_WIDTH); y < height; y+= CELL_WIDTH)
- //{
- // ctx.moveTo(0, y);
- // ctx.lineTo(width, y);
- //}
- //ctx.stroke();
+ //paintGridLines();
- //Paint occupied areas.
- for (var r = Math.floor(offset[1] / CELL_WIDTH); r * CELL_WIDTH - offset[1] < height; r++)
+ //Paint occupied areas. (and fading ones).
+ for (var r = Math.floor(offset[1] / CELL_WIDTH); r < grid.size && r * CELL_WIDTH - offset[1] < height; r++)
{
- for (var c = Math.floor(offset[0] / CELL_WIDTH); c * CELL_WIDTH - offset[0] < width; c++)
+ for (var c = Math.floor(offset[0] / CELL_WIDTH); c < grid.size && c * CELL_WIDTH - offset[0] < width; c++)
{
- if (grid[r][c] !== -1)
+ var p = grid.get(r, c);
+ var x = c * CELL_WIDTH, y = r * CELL_WIDTH, baseColor, shadowColor;
+
+ var animateSpec = animateGrid.get(r, c);
+ if (!animateOff && animateSpec)
{
- var x = c * CELL_WIDTH,
- y = r * CELL_WIDTH,
- p = grid[r][c];
- ctx.fillStyle = p.shadowColor;
- ctx.fillRect(x, y + SHADOW_OFFSET, CELL_WIDTH, CELL_WIDTH);
- ctx.fillStyle = p.baseColor;
- ctx.fillRect(x, y, CELL_WIDTH, CELL_WIDTH);
+ if (animateSpec.before) //fading animation
+ {
+ var alpha = 1 - (animateSpec.frame / MAX_FRAMES);
+ baseColor = animateSpec.before.baseColor.deriveAlpha(alpha);
+ shadowColor = animateSpec.before.shadowColor.deriveAlpha(alpha);
+ }
+ else
+ continue;
+ }
+ else if (p)
+ {
+ baseColor = p.baseColor;
+ shadowColor = p.shadowColor;
+ }
+ else //No animation nor is this player owned.
+ continue;
+
+ ctx.fillStyle = shadowColor.rgbString();
+ ctx.fillRect(x, y + CELL_WIDTH, CELL_WIDTH, SHADOW_OFFSET);
+ ctx.fillStyle = baseColor.rgbString();
+ ctx.fillRect(x, y, CELL_WIDTH, CELL_WIDTH);
+ }
+ }
+
+ if (animateOff)
+ return;
+
+ //Paint squares with drop in animation.
+ for (var r = 0; r < grid.size; r++)
+ {
+ for (var c = 0; c < grid.size; c++)
+ {
+ animateSpec = animateGrid.get(r, c);
+ x = c * CELL_WIDTH, y = r * CELL_WIDTH;
+
+ if (animateSpec && !animateOff)
+ {
+ if (animateSpec.after)
+ {
+
+ var offsetBounce = 0;
+ if (animateSpec.frame >= MAX_FRAMES - BOUNCE_FRAMES)
+ {
+ var bounce = animateSpec.frame - MAX_FRAMES + BOUNCE_FRAMES;
+ var bounceHeight = BOUNCE_FRAMES / 2 * DROP_SPEED;
+ if (bounce >= BOUNCE_FRAMES / 2)
+ offsetBounce = bounceHeight - (bounce - BOUNCE_FRAMES / 2) * DROP_SPEED;
+ else
+ offsetBounce = bounce * DROP_SPEED;
+ }
+ else
+ offsetBounce = DROP_HEIGHT - DROP_SPEED * animateSpec.frame;
+
+ y -= offsetBounce;
+
+ shadowColor = animateSpec.after.shadowColor;
+ baseColor = animateSpec.after.baseColor.deriveLumination(-(offsetBounce / DROP_HEIGHT) * .1);
+
+ ctx.fillStyle = shadowColor.rgbString();
+ ctx.fillRect(x, y + SHADOW_OFFSET, CELL_WIDTH, CELL_WIDTH);
+ ctx.fillStyle = baseColor.rgbString();
+ ctx.fillRect(x, y, CELL_WIDTH, CELL_WIDTH);
+ }
+
+ animateSpec.frame++;
+ if (animateSpec.frame >= MAX_FRAMES)
+ animateGrid.set(r, c, null);
}
}
}
}
-
+ var showedDead = false;
function paintLoop()
{
ctx.fillStyle = 'whitesmoke';
@@ -226,10 +333,11 @@ $(function() {
});
ctx.setTransform(1, 0, 0, 1, 0, 0); //Reset transform.
- if (user.dead)
+ if (user.dead && !showedDead)
{
+ showedDead = true;
console.log("You died!");
- return;
+ //return;
}
//TODO: sync each loop with server. (server will give frame count.)
@@ -242,6 +350,8 @@ $(function() {
//Event listeners
$(document).keydown(function(e) {
+ if (user.dead)
+ return;
var newHeading = -1;
switch (e.which)
{
diff --git a/player.js b/player.js
index 237d80c..7472ad7 100644
--- a/player.js
+++ b/player.js
@@ -2,6 +2,10 @@ var Stack;
if (!Stack)
throw new Error("Require stack.js");
+var Color;
+if (!Color)
+ throw new Error("Requre color.js");
+
var Tail = (function() {
var CELL_WIDTH = 30;
var GRID_SIZE = 200;
@@ -44,7 +48,6 @@ var Tail = (function() {
if (!tailGrid[r])
tailGrid[r] = [];
tailGrid[r][c] = true;
-
}
function addTail(data, orientation)
@@ -87,7 +90,7 @@ var Tail = (function() {
function render2(data, ctx)
{
- ctx.fillStyle = data.player.tailColor;
+ ctx.fillStyle = data.player.tailColor.rgbString();
for (var r = 0; r < data.tailGrid.length; r++)
{
if (!data.tailGrid[r])
@@ -100,7 +103,10 @@ var Tail = (function() {
function render(data, ctx)
{
- ctx.fillStyle = data.player.tailColor;
+ if (data.tail.length === 0)
+ return;
+
+ ctx.fillStyle = data.player.tailColor.rgbString();
var prevOrient = -1;
var start = [data.startRow, data.startCol];
@@ -203,7 +209,7 @@ var Tail = (function() {
function onTail(c) { return data.tailGrid[c[0]] && data.tailGrid[c[0]][c[1]]; }
var start = [data.startRow, data.startCol];
- var been = new Array(grid.length);
+ var been = new Grid(grid.size);
var coords = [];
coords.push(start);
@@ -213,18 +219,16 @@ var Tail = (function() {
var r = coord[0];
var c = coord[1];
- if (r < 0 || c < 0 || r >= grid.length || c >= grid[r].length)
- continue; //Out of bounds!
+ if (grid.isOutOfBounds(r, c))
+ continue;
- if (been[r] && been[r][c])
+ if (been.get(r, c))
continue;
if (onTail(coord)) //on the tail.
{
- if (!been[r])
- been[r] = new Array(grid[r].length);
- been[r][c] = true;
- grid[r][c] = data.player;
+ been.set(r, c, true);
+ grid.set(r, c, data.player);
//Find all spots that this tail encloses.
floodFill(data, grid, r + 1, c, been);
@@ -255,19 +259,17 @@ var Tail = (function() {
var r = coord[0];
var c = coord[1];
- if (r < 0 || c < 0 || r >= grid.length || c >= grid[r].length)
+ if (grid.isOutOfBounds(r, c))
{
surrounded = false;
- continue; //Out of bounds!
+ continue;
}
//End this traverse on boundaries (where we been, on the tail, and when we enter our territory)
- if ((been[r] && been[r][c]) || onTail(coord) || grid[r][c] === data.player)
+ if (been.get(r, c) || onTail(coord) || grid.get(r, c) === data.player)
continue;
- if (!been[r])
- been[r] = new Array(grid[r].length);
- been[r][c] = true;
+ been.set(r, c, true);
if (surrounded)
filled.push(coord);
@@ -282,7 +284,7 @@ var Tail = (function() {
while (!filled.isEmpty())
{
coord = filled.pop();
- grid[coord[0]][coord[1]] = data.player;
+ grid.set(coord[0], coord[1], data.player);
}
}
@@ -302,24 +304,23 @@ this.Player = (function() {
var SPEED = 5;
var SHADOW_OFFSET = 10;
- function Player(socket, grid, num) {
+ function Player(socket, grid, num, row, col) {
var data = {};
//TODO: load player data and color.
var hue = Math.random();
- var base = hslToRgb(hue, .8, .5);
- this.baseColor = rgbString(base);
- this.shadowColor = rgbString(hslToRgb(hue, .8, .2));
- base[3] = .5;
- this.tailColor = rgbaString(base);
+ var base = new Color(hue, .8, .5);
+ this.baseColor = base;
+ this.shadowColor = base.deriveLumination(-.3);
+ this.tailColor = base.deriveLumination(.2).deriveAlpha(.5);
this.name = 'Player ' + (num + 1);
data.grid = grid;
data.curHeading = 2;
- data.row = Math.floor(Math.random() * 10) + 10;
- data.col = 10;//num;
+ data.row = row || 0;
+ data.col = col || 0;
data.dead = false;
data.tail = new Tail(this);
@@ -350,13 +351,13 @@ this.Player = (function() {
this.tail.render(ctx);
//Render player.
- ctx.fillStyle = this.shadowColor;
+ ctx.fillStyle = this.shadowColor.rgbString();
ctx.fillRect(this.posX, this.posY, CELL_WIDTH, CELL_WIDTH);
var mid = CELL_WIDTH / 2;
var grd = ctx.createRadialGradient(this.posX + mid, this.posY + mid - SHADOW_OFFSET, 1,
this.posX + mid, this.posY + mid - SHADOW_OFFSET, CELL_WIDTH);
- grd.addColorStop(0, this.baseColor);
+ grd.addColorStop(0, this.baseColor.rgbString());
grd.addColorStop(1, "white");
ctx.fillStyle = grd;
ctx.fillRect(this.posX, this.posY - SHADOW_OFFSET, CELL_WIDTH, CELL_WIDTH);
@@ -394,13 +395,13 @@ this.Player = (function() {
data.row = row;
data.col = col;
- if (row < 0 || row > data.grid.length || col < 0 || col > data.grid[row].length)
+ if (data.grid.isOutOfBounds(row, col))
{
data.dead = true;
return;
}
- if (data.grid[row][col] === this)
+ if (data.grid.get(row, col) === this)
{
//Safe zone!
this.tail.fillTail(data.grid);
@@ -423,39 +424,7 @@ this.Player = (function() {
};
}
- //http://stackoverflow.com/a/9493060/7344257
- function hslToRgb(h, s, l){
- var r, g, b;
-
- if(s == 0){
- r = g = b = l; // achromatic
- }else{
- var hue2rgb = function hue2rgb(p, q, t){
- if(t < 0) t += 1;
- if(t > 1) t -= 1;
- if(t < 1/6) return p + (q - p) * 6 * t;
- if(t < 1/2) return q;
- if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
- return p;
- };
-
- var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
- var p = 2 * l - q;
- r = hue2rgb(p, q, h + 1/3);
- g = hue2rgb(p, q, h);
- b = hue2rgb(p, q, h - 1/3);
- }
-
- return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
- }
-
- function rgbString(rgb) {
- return 'rgb(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ')';
- }
- function rgbaString(rgb) {
- return 'rgba(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ', ' + rgb[3] + ')';
- }
return Player;
})();
\ No newline at end of file
diff --git a/test.html b/test.html
index ef013e1..4a35a7e 100644
--- a/test.html
+++ b/test.html
@@ -1,4 +1,6 @@
+
+