forked from ilikecats/papercats
Separate rendering and client code, fixes #3
This commit is contained in:
parent
7ff47d8efc
commit
c8cd3d474b
276
game-client.js
276
game-client.js
@ -1,51 +1,38 @@
|
|||||||
/* global $ */
|
/* global $ */
|
||||||
var Player = require("./player.js");
|
var client = require("./player-client.js");
|
||||||
var renderer = require("./game-renderer.js");
|
|
||||||
var consts = require("./game-consts.js");
|
var consts = require("./game-consts.js");
|
||||||
var core = require("./game-core.js");
|
|
||||||
var io = require('socket.io-client');
|
var io = require('socket.io-client');
|
||||||
|
|
||||||
var GRID_SIZE = consts.GRID_SIZE;
|
var GRID_SIZE = consts.GRID_SIZE;
|
||||||
var CELL_WIDTH = consts.CELL_WIDTH;
|
var CELL_WIDTH = consts.CELL_WIDTH;
|
||||||
|
|
||||||
renderer.allowAnimation = true;
|
client.allowAnimation = true;
|
||||||
|
client.renderer = require("./game-renderer.js");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides requestAnimationFrame in a cross browser way.
|
* Provides requestAnimationFrame in a cross browser way. (edited so that this is also compatible with node.)
|
||||||
* @author paulirish / http://paulirish.com/
|
* @author paulirish / http://paulirish.com/
|
||||||
*/
|
*/
|
||||||
// window.requestAnimationFrame = function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) {
|
// window.requestAnimationFrame = function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) {
|
||||||
// window.setTimeout( callback, 1000 / 60 );
|
// window.setTimeout( callback, 1000 / 60 );
|
||||||
// };
|
// };
|
||||||
if ( !window.requestAnimationFrame ) {
|
var global;
|
||||||
window.requestAnimationFrame = ( function() {
|
if (!global)
|
||||||
return window.webkitRequestAnimationFrame ||
|
global = window;
|
||||||
window.mozRequestAnimationFrame ||
|
if ( !global.requestAnimationFrame ) {
|
||||||
window.oRequestAnimationFrame ||
|
global.requestAnimationFrame = ( function() {
|
||||||
window.msRequestAnimationFrame ||
|
return global.webkitRequestAnimationFrame ||
|
||||||
|
global.mozRequestAnimationFrame ||
|
||||||
|
global.oRequestAnimationFrame ||
|
||||||
|
global.msRequestAnimationFrame ||
|
||||||
function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) {
|
function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) {
|
||||||
window.setTimeout( callback, 1000 / 60 );
|
global.setTimeout( callback, 1000 / 60 );
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
var norun = false;
|
|
||||||
function run() {
|
function run() {
|
||||||
if (norun)
|
client.connectGame('//' + window.location.hostname + ':8081', $('#name').val(), function(success, msg) {
|
||||||
return; //Prevent multiple clicks.
|
|
||||||
norun = true;
|
|
||||||
|
|
||||||
user = null;
|
|
||||||
deadFrames = 0;
|
|
||||||
|
|
||||||
//Socket connection.
|
|
||||||
//, {transports: ['websocket'], upgrade: false}
|
|
||||||
connectServer();
|
|
||||||
socket.emit('hello', {
|
|
||||||
name: $("#name").val(),
|
|
||||||
type: 0, //Free-for-all
|
|
||||||
gameid: -1 //Requested game-id, or -1 for anyone.
|
|
||||||
}, function(success, msg) {
|
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
console.info("Connected to game!");
|
console.info("Connected to game!");
|
||||||
@ -59,7 +46,6 @@ function run() {
|
|||||||
console.error("Unable to connect to game: " + msg);
|
console.error("Unable to connect to game: " + msg);
|
||||||
var error = $("#error");
|
var error = $("#error");
|
||||||
error.text(msg);
|
error.text(msg);
|
||||||
norun = false;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -84,7 +70,7 @@ $(function() {
|
|||||||
socket.on('connect_error', function() {
|
socket.on('connect_error', function() {
|
||||||
if (!success)
|
if (!success)
|
||||||
error.text("Cannot connect with server. This probably is due to misconfigured proxy server. (Try using a different browser)");
|
error.text("Cannot connect with server. This probably is due to misconfigured proxy server. (Try using a different browser)");
|
||||||
})
|
});
|
||||||
socket.emit("checkConn", function() {
|
socket.emit("checkConn", function() {
|
||||||
success = true;
|
success = true;
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
@ -106,12 +92,9 @@ $(function() {
|
|||||||
}, 2000);
|
}, 2000);
|
||||||
});
|
});
|
||||||
|
|
||||||
var user, socket, frame;
|
|
||||||
|
|
||||||
//Event listeners
|
//Event listeners
|
||||||
$(document).keydown(function(e) {
|
$(document).keydown(function(e) {
|
||||||
if (!user || user.dead)
|
|
||||||
return;
|
|
||||||
var newHeading = -1;
|
var newHeading = -1;
|
||||||
switch (e.which)
|
switch (e.which)
|
||||||
{
|
{
|
||||||
@ -122,231 +105,6 @@ $(document).keydown(function(e) {
|
|||||||
default: return; //exit handler for other keys.
|
default: return; //exit handler for other keys.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newHeading === user.currentHeading || ((newHeading % 2 === 0) ^
|
client.changeHeading(newHeading);
|
||||||
(user.currentHeading % 2 === 0)))
|
|
||||||
{
|
|
||||||
//user.heading = newHeading;
|
|
||||||
if (socket)
|
|
||||||
socket.emit("frame", {
|
|
||||||
frame: frame,
|
|
||||||
heading: newHeading
|
|
||||||
}, function(success, msg) {
|
|
||||||
if (!success)
|
|
||||||
console.error(msg);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var grid = renderer.grid;
|
|
||||||
var timeout = undefined;
|
|
||||||
var dirty = false;
|
|
||||||
var deadFrames = 0;
|
|
||||||
var requesting = -1; //frame that we are requesting at.
|
|
||||||
var frameCache = []; //Frames after our request.
|
|
||||||
|
|
||||||
//TODO: check if we can connect to server.
|
|
||||||
function connectServer() {
|
|
||||||
io.j = [];
|
|
||||||
io.sockets = [];
|
|
||||||
socket = io('http://' + window.location.hostname + ':8081', {
|
|
||||||
'forceNew': true,
|
|
||||||
upgrade: false,
|
|
||||||
transports: ['websocket']
|
|
||||||
});
|
|
||||||
socket.on('connect', function(){
|
|
||||||
console.info("Connected to server.");
|
|
||||||
});
|
|
||||||
socket.on('game', function(data) {
|
|
||||||
if (timeout != undefined)
|
|
||||||
clearTimeout(timeout);
|
|
||||||
|
|
||||||
//Initialize game.
|
|
||||||
//TODO: display data.gameid --- game id #
|
|
||||||
frame = data.frame;
|
|
||||||
renderer.reset();
|
|
||||||
|
|
||||||
//Load players.
|
|
||||||
data.players.forEach(function(p) {
|
|
||||||
var pl = new Player(grid, p);
|
|
||||||
renderer.addPlayer(pl);
|
|
||||||
});
|
|
||||||
user = renderer.getPlayerFromNum(data.num);
|
|
||||||
if (!user) throw new Error();
|
|
||||||
renderer.setUser(user);
|
|
||||||
|
|
||||||
//Load grid.
|
|
||||||
var gridData = new Uint8Array(data.grid);
|
|
||||||
for (var r = 0; r < grid.size; r++)
|
|
||||||
for (var c = 0; c < grid.size; c++)
|
|
||||||
{
|
|
||||||
var ind = gridData[r * grid.size + c] - 1;
|
|
||||||
grid.set(r, c, ind === -1 ? null : renderer.getPlayer(ind));
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.paint();
|
|
||||||
frame = data.frame;
|
|
||||||
|
|
||||||
if (requesting !== -1)
|
|
||||||
{
|
|
||||||
//Update those cache frames after we updated game.
|
|
||||||
var minFrame = requesting;
|
|
||||||
requesting = -1;
|
|
||||||
while (frameCache.length > frame - minFrame)
|
|
||||||
processFrame(frameCache[frame - minFrame]);
|
|
||||||
frameCache = [];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('notifyFrame', processFrame);
|
|
||||||
|
|
||||||
socket.on('dead', function() {
|
|
||||||
socket.disconnect(); //In case we didn't get the disconnect call.
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('disconnect', function(){
|
|
||||||
if (!user)
|
|
||||||
return;
|
|
||||||
console.info("Server has disconnected. Creating new game.");
|
|
||||||
socket.disconnect();
|
|
||||||
user.die();
|
|
||||||
dirty = true;
|
|
||||||
paintLoop();
|
|
||||||
|
|
||||||
//TODO: Show score stats.
|
|
||||||
//Show score stats.
|
|
||||||
$("#stats").removeClass("hidden");
|
|
||||||
$("#stats").animate({
|
|
||||||
opacity: .9999
|
|
||||||
}, 3000, function() {
|
|
||||||
showStats();
|
|
||||||
});
|
|
||||||
|
|
||||||
//Then fade back into the login screen.
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function showStats() {
|
|
||||||
$("#begin").removeClass("hidden");
|
|
||||||
$("#begin").animate({
|
|
||||||
opacity: .9999
|
|
||||||
}, 1000, function() {
|
|
||||||
$("#stats").addClass("hidden").css("opacity", 0);
|
|
||||||
norun = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function processFrame(data)
|
|
||||||
{
|
|
||||||
if (timeout != undefined)
|
|
||||||
clearTimeout(timeout);
|
|
||||||
|
|
||||||
if (requesting !== -1 && requesting < data.frame)
|
|
||||||
{
|
|
||||||
frameCache.push(data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.frame - 1 !== frame)
|
|
||||||
{
|
|
||||||
console.error("Frames don't match up!");
|
|
||||||
socket.emit('requestFrame'); //Restore data.
|
|
||||||
requesting = data.frame;
|
|
||||||
frameCache.push(data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame++;
|
|
||||||
if (data.newPlayers)
|
|
||||||
{
|
|
||||||
data.newPlayers.forEach(function(p) {
|
|
||||||
if (p.num === user.num)
|
|
||||||
return;
|
|
||||||
var pl = new Player(grid, p);
|
|
||||||
renderer.addPlayer(pl);
|
|
||||||
core.initPlayer(grid, pl);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var found = new Array(renderer.playerSize());
|
|
||||||
data.moves.forEach(function(val, i) {
|
|
||||||
var player = renderer.getPlayerFromNum(val.num);
|
|
||||||
if (!player) return;
|
|
||||||
if (val.left) player.die();
|
|
||||||
found[i] = true;
|
|
||||||
player.heading = val.heading;
|
|
||||||
});
|
|
||||||
for (var i = 0; i < renderer.playerSize(); i++)
|
|
||||||
{
|
|
||||||
//Implicitly leaving game.
|
|
||||||
if (!found[i])
|
|
||||||
{
|
|
||||||
var player = renderer.getPlayer();
|
|
||||||
player && player.die();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.update();
|
|
||||||
|
|
||||||
var locs = {};
|
|
||||||
for (var i = 0; i < renderer.playerSize(); i++)
|
|
||||||
{
|
|
||||||
var p = renderer.getPlayer(i);
|
|
||||||
locs[p.num] = [p.posX, p.posY, p.waitLag];
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.emit("verify", {
|
|
||||||
frame: frame,
|
|
||||||
locs: locs
|
|
||||||
}, function(frame, success, adviceFix, msg) {
|
|
||||||
if (!success && requesting === -1)
|
|
||||||
{
|
|
||||||
console.error(frame + ": " + msg);
|
|
||||||
if (adviceFix)
|
|
||||||
socket.emit('requestFrame');
|
|
||||||
}
|
|
||||||
}.bind(this, frame));
|
|
||||||
|
|
||||||
dirty = true;
|
|
||||||
requestAnimationFrame(function() {
|
|
||||||
paintLoop();
|
|
||||||
});
|
|
||||||
timeout = setTimeout(function() {
|
|
||||||
console.warn("Server has timed-out. Disconnecting.");
|
|
||||||
socket.disconnect();
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function paintLoop()
|
|
||||||
{
|
|
||||||
if (!dirty)
|
|
||||||
return;
|
|
||||||
renderer.paint();
|
|
||||||
dirty = false;
|
|
||||||
|
|
||||||
if (user.dead)
|
|
||||||
{
|
|
||||||
if (timeout)
|
|
||||||
clearTimeout(timeout);
|
|
||||||
if (deadFrames === 60) //One second of frame
|
|
||||||
{
|
|
||||||
var before = renderer.allowAnimation;
|
|
||||||
renderer.allowAnimation = false;
|
|
||||||
renderer.update();
|
|
||||||
renderer.paint();
|
|
||||||
renderer.allowAnimation = before;
|
|
||||||
user = null;
|
|
||||||
deadFrames = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.disconnect();
|
|
||||||
deadFrames++;
|
|
||||||
dirty = true;
|
|
||||||
renderer.update();
|
|
||||||
requestAnimationFrame(paintLoop);
|
|
||||||
}
|
|
||||||
}
|
|
152
game-renderer.js
152
game-renderer.js
@ -3,7 +3,7 @@ var Rolling = require("./rolling.js");
|
|||||||
var Color = require("./color.js");
|
var Color = require("./color.js");
|
||||||
var Grid = require("./grid.js");
|
var Grid = require("./grid.js");
|
||||||
var consts = require("./game-consts.js");
|
var consts = require("./game-consts.js");
|
||||||
var core = require("./game-core.js");
|
var client = require("./player-client.js");
|
||||||
|
|
||||||
var GRID_SIZE = consts.GRID_SIZE;
|
var GRID_SIZE = consts.GRID_SIZE;
|
||||||
var CELL_WIDTH = consts.CELL_WIDTH;
|
var CELL_WIDTH = consts.CELL_WIDTH;
|
||||||
@ -33,28 +33,9 @@ $(function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var animateGrid, playerPortion, portionsRolling,
|
||||||
|
barProportionRolling, animateTo, offset, user, zoom, showedDead;
|
||||||
var allowAnimation = true;
|
var grid = client.grid;
|
||||||
var animateGrid, players, allPlayers, playerPortion, portionsRolling,
|
|
||||||
barProportionRolling, grid, animateTo, offset, user, zoom, kills, showedDead;
|
|
||||||
|
|
||||||
grid = new Grid(GRID_SIZE, function(row, col, before, after) {
|
|
||||||
//Keep track of areas.
|
|
||||||
if (before)
|
|
||||||
playerPortion[before.num]--;
|
|
||||||
if (after)
|
|
||||||
playerPortion[after.num]++;
|
|
||||||
|
|
||||||
//Queue animation
|
|
||||||
if (before === after || !allowAnimation)
|
|
||||||
return;
|
|
||||||
animateGrid.set(row, col, {
|
|
||||||
before: before,
|
|
||||||
after: after,
|
|
||||||
frame: 0
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function updateSize()
|
function updateSize()
|
||||||
{
|
{
|
||||||
@ -76,12 +57,9 @@ function updateSize()
|
|||||||
centerOnPlayer(user, offset);
|
centerOnPlayer(user, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function reset() {
|
||||||
animateGrid = new Grid(GRID_SIZE);
|
animateGrid = new Grid(GRID_SIZE);
|
||||||
grid.reset();
|
|
||||||
|
|
||||||
players = [];
|
|
||||||
allPlayers = [];
|
|
||||||
playerPortion = [];
|
playerPortion = [];
|
||||||
portionsRolling = [];
|
portionsRolling = [];
|
||||||
barProportionRolling = [];
|
barProportionRolling = [];
|
||||||
@ -91,11 +69,11 @@ function init() {
|
|||||||
|
|
||||||
user = null;
|
user = null;
|
||||||
zoom = 1;
|
zoom = 1;
|
||||||
kills = 0;
|
|
||||||
showedDead = false;
|
showedDead = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
reset();
|
||||||
|
|
||||||
//Paint methods.
|
//Paint methods.
|
||||||
function paintGridBorder(ctx)
|
function paintGridBorder(ctx)
|
||||||
@ -136,7 +114,7 @@ function paintGrid(ctx)
|
|||||||
var x = c * CELL_WIDTH, y = r * CELL_WIDTH, baseColor, shadowColor;
|
var x = c * CELL_WIDTH, y = r * CELL_WIDTH, baseColor, shadowColor;
|
||||||
|
|
||||||
var animateSpec = animateGrid.get(r, c);
|
var animateSpec = animateGrid.get(r, c);
|
||||||
if (allowAnimation && animateSpec)
|
if (client.allowAnimation && animateSpec)
|
||||||
{
|
{
|
||||||
if (animateSpec.before) //fading animation
|
if (animateSpec.before) //fading animation
|
||||||
{
|
{
|
||||||
@ -172,7 +150,7 @@ function paintGrid(ctx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!allowAnimation)
|
if (!client.allowAnimation)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//Paint squares with drop in animation.
|
//Paint squares with drop in animation.
|
||||||
@ -183,7 +161,7 @@ function paintGrid(ctx)
|
|||||||
animateSpec = animateGrid.get(r, c);
|
animateSpec = animateGrid.get(r, c);
|
||||||
x = c * CELL_WIDTH, y = r * CELL_WIDTH;
|
x = c * CELL_WIDTH, y = r * CELL_WIDTH;
|
||||||
|
|
||||||
if (animateSpec && allowAnimation)
|
if (animateSpec && client.allowAnimation)
|
||||||
{
|
{
|
||||||
var viewable = r >= minRow && r < maxRow && c >= minCol && c < maxCol;
|
var viewable = r >= minRow && r < maxRow && c >= minCol && c < maxCol;
|
||||||
if (animateSpec.after && viewable)
|
if (animateSpec.after && viewable)
|
||||||
@ -212,6 +190,7 @@ function paintGrid(ctx)
|
|||||||
|
|
||||||
function paintUIBar(ctx)
|
function paintUIBar(ctx)
|
||||||
{
|
{
|
||||||
|
|
||||||
//UI Bar background
|
//UI Bar background
|
||||||
ctx.fillStyle = "#24422c";
|
ctx.fillStyle = "#24422c";
|
||||||
ctx.fillRect(0, 0, canvasWidth, BAR_HEIGHT);
|
ctx.fillRect(0, 0, canvasWidth, BAR_HEIGHT);
|
||||||
@ -240,13 +219,13 @@ function paintUIBar(ctx)
|
|||||||
ctx.fillText((userPortions * 100).toFixed(3) + "%", 5 + barOffset, CELL_WIDTH - 5);
|
ctx.fillText((userPortions * 100).toFixed(3) + "%", 5 + barOffset, CELL_WIDTH - 5);
|
||||||
|
|
||||||
//Number of kills
|
//Number of kills
|
||||||
var killsText = "Kills: " + kills;
|
var killsText = "Kills: " + client.kills;
|
||||||
var killsOffset = 20 + BAR_WIDTH + barOffset;
|
var killsOffset = 20 + BAR_WIDTH + barOffset;
|
||||||
ctx.fillText(killsText, killsOffset, CELL_WIDTH - 5);
|
ctx.fillText(killsText, killsOffset, CELL_WIDTH - 5);
|
||||||
|
|
||||||
//Calcuate rank
|
//Calcuate rank
|
||||||
var sorted = [];
|
var sorted = [];
|
||||||
players.forEach(function(val) {
|
client.getPlayers().forEach(function(val) {
|
||||||
sorted.push({player: val, portion: playerPortion[val.num]});
|
sorted.push({player: val, portion: playerPortion[val.num]});
|
||||||
});
|
});
|
||||||
sorted.sort(function(a, b) {
|
sorted.sort(function(a, b) {
|
||||||
@ -262,12 +241,11 @@ function paintUIBar(ctx)
|
|||||||
if (sorted.length > 0)
|
if (sorted.length > 0)
|
||||||
{
|
{
|
||||||
var maxPortion = sorted[0].portion;
|
var maxPortion = sorted[0].portion;
|
||||||
for (var i = 0; i < players.length; i++)
|
client.getPlayers().forEach(function(player) {
|
||||||
{
|
var rolling = barProportionRolling[player.num];
|
||||||
var rolling = barProportionRolling[players[i].num];
|
rolling.value = playerPortion[player.num] / maxPortion;
|
||||||
rolling.value = playerPortion[players[i].num] / maxPortion;
|
|
||||||
rolling.update();
|
rolling.update();
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Show leaderboard.
|
//Show leaderboard.
|
||||||
@ -303,6 +281,7 @@ function paintUIBar(ctx)
|
|||||||
|
|
||||||
function paint(ctx)
|
function paint(ctx)
|
||||||
{
|
{
|
||||||
|
|
||||||
ctx.fillStyle = '#e2ebf3'; //'whitesmoke';
|
ctx.fillStyle = '#e2ebf3'; //'whitesmoke';
|
||||||
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
||||||
|
|
||||||
@ -318,7 +297,7 @@ function paint(ctx)
|
|||||||
ctx.translate(-offset[0] + BORDER_WIDTH, -offset[1] + BORDER_WIDTH);
|
ctx.translate(-offset[0] + BORDER_WIDTH, -offset[1] + BORDER_WIDTH);
|
||||||
|
|
||||||
paintGrid(ctx);
|
paintGrid(ctx);
|
||||||
players.forEach(function (p) {
|
client.getPlayers().forEach(function (p) {
|
||||||
var fr = p.waitLag;
|
var fr = p.waitLag;
|
||||||
if (fr < ANIMATE_FRAMES)
|
if (fr < ANIMATE_FRAMES)
|
||||||
p.render(ctx, fr / ANIMATE_FRAMES);
|
p.render(ctx, fr / ANIMATE_FRAMES);
|
||||||
@ -352,7 +331,7 @@ function update() {
|
|||||||
{
|
{
|
||||||
if (animateTo[i] !== offset[i])
|
if (animateTo[i] !== offset[i])
|
||||||
{
|
{
|
||||||
if (allowAnimation)
|
if (client.allowAnimation)
|
||||||
{
|
{
|
||||||
var delta = animateTo[i] - offset[i];
|
var delta = animateTo[i] - offset[i];
|
||||||
var dir = Math.sign(delta);
|
var dir = Math.sign(delta);
|
||||||
@ -365,29 +344,16 @@ function update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Calculate player portions.
|
//Calculate player portions.
|
||||||
for (var i = 0; i < players.length; i++)
|
client.getPlayers().forEach(function(player) {
|
||||||
{
|
var roll = portionsRolling[player.num];
|
||||||
var roll = portionsRolling[players[i].num];
|
roll.value = playerPortion[player.num] / GRID_SIZE / GRID_SIZE;
|
||||||
roll.value = playerPortion[players[i].num] / GRID_SIZE / GRID_SIZE;
|
|
||||||
roll.update();
|
roll.update();
|
||||||
}
|
});
|
||||||
|
|
||||||
//Zoom goes from 1 to .5, decreasing as portion goes up. TODO: maybe can modify this?
|
//Zoom goes from 1 to .5, decreasing as portion goes up. TODO: maybe can modify this?
|
||||||
if (portionsRolling[user.num])
|
if (portionsRolling[user.num])
|
||||||
zoom = 1 / (portionsRolling[user.num].lag + 1);
|
zoom = 1 / (portionsRolling[user.num].lag + 1);
|
||||||
|
|
||||||
var dead = [];
|
|
||||||
core.updateFrame(grid, players, dead, function addKill(killer, other)
|
|
||||||
{
|
|
||||||
if (players[killer] === user && killer !== other)
|
|
||||||
kills++;
|
|
||||||
});
|
|
||||||
dead.forEach(function(val) {
|
|
||||||
console.log(val.name || "Unnamed" + " is dead");
|
|
||||||
delete allPlayers[val.num];
|
|
||||||
delete portionsRolling[val.num];
|
|
||||||
});
|
|
||||||
|
|
||||||
//TODO: animate player is dead. (maybe explosion?), and tail rewinds itself.
|
//TODO: animate player is dead. (maybe explosion?), and tail rewinds itself.
|
||||||
if (user) centerOnPlayer(user, animateTo);
|
if (user) centerOnPlayer(user, animateTo);
|
||||||
}
|
}
|
||||||
@ -428,51 +394,59 @@ function getBounceOffset(frame)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showStats() {
|
||||||
|
//TODO: Show score stats.
|
||||||
|
$("#begin").removeClass("hidden");
|
||||||
|
$("#begin").animate({
|
||||||
|
opacity: .9999
|
||||||
|
}, 1000, function() {
|
||||||
|
$("#stats").addClass("hidden").css("opacity", 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = exports = {
|
module.exports = exports = {
|
||||||
addPlayer: function(player) {
|
addPlayer: function(player) {
|
||||||
if (allPlayers[player.num])
|
|
||||||
return; //Already added.
|
|
||||||
allPlayers[player.num] = players[players.length] = player;
|
|
||||||
playerPortion[player.num] = 0;
|
playerPortion[player.num] = 0;
|
||||||
portionsRolling[player.num] = new Rolling(9 / GRID_SIZE / GRID_SIZE, ANIMATE_FRAMES);
|
portionsRolling[player.num] = new Rolling(9 / GRID_SIZE / GRID_SIZE, ANIMATE_FRAMES);
|
||||||
barProportionRolling[player.num] = new Rolling(0, ANIMATE_FRAMES);
|
barProportionRolling[player.num] = new Rolling(0, ANIMATE_FRAMES);
|
||||||
return players.length - 1;
|
|
||||||
},
|
},
|
||||||
getPlayer: function(ind) {
|
disconnect: function() {
|
||||||
if (ind < 0 || ind >= players.length)
|
//Show score stats.
|
||||||
throw new RangeError("Player index out of bounds (" + ind + ").");
|
$("#stats").removeClass("hidden");
|
||||||
return players[ind];
|
$("#stats").animate({
|
||||||
|
opacity: .9999
|
||||||
|
}, 3000, function() {
|
||||||
|
showStats();
|
||||||
|
//Then fade back into the login screen.
|
||||||
|
});
|
||||||
},
|
},
|
||||||
getPlayerFromNum: function(num) {
|
removePlayer: function(player) {
|
||||||
return allPlayers[num];
|
delete playerPortion[player.num];
|
||||||
},
|
delete portionsRolling[player.num];
|
||||||
playerSize: function() {
|
delete barProportionRolling[player.num];
|
||||||
return players.length;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setUser: function(player) {
|
setUser: function(player) {
|
||||||
user = player;
|
user = player;
|
||||||
centerOnPlayer(user, offset);
|
centerOnPlayer(user, offset);
|
||||||
},
|
},
|
||||||
incrementKill: function() {
|
reset: reset,
|
||||||
kills++;
|
updateGrid: function(row, col, before, after) {
|
||||||
},
|
//Keep track of areas.
|
||||||
reset: function() {
|
if (before)
|
||||||
init();
|
playerPortion[before.num]--;
|
||||||
|
if (after)
|
||||||
|
playerPortion[after.num]++;
|
||||||
|
|
||||||
|
//Queue animation
|
||||||
|
if (before === after || !client.allowAnimation)
|
||||||
|
return;
|
||||||
|
animateGrid.set(row, col, {
|
||||||
|
before: before,
|
||||||
|
after: after,
|
||||||
|
frame: 0
|
||||||
|
});
|
||||||
},
|
},
|
||||||
paint: paintDoubleBuff,
|
paint: paintDoubleBuff,
|
||||||
update: update
|
update: update
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.defineProperties(exports, {
|
|
||||||
allowAnimation: {
|
|
||||||
get: function() { return allowAnimation; },
|
|
||||||
set: function(val) { allowAnimation = !!val; },
|
|
||||||
enumerable: true
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
get: function() { return grid; },
|
|
||||||
enumerable: true
|
|
||||||
}
|
|
||||||
});
|
|
354
player-client.js
Normal file
354
player-client.js
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
var Player = require('./player.js');
|
||||||
|
var Grid = require('./grid.js');
|
||||||
|
var consts = require('./game-consts.js');
|
||||||
|
var core = require('./game-core.js');
|
||||||
|
var io = require('socket.io-client');
|
||||||
|
|
||||||
|
var GRID_SIZE = consts.GRID_SIZE;
|
||||||
|
var CELL_WIDTH = consts.CELL_WIDTH;
|
||||||
|
|
||||||
|
var running = false;
|
||||||
|
var user, socket, frame;
|
||||||
|
var players, allPlayers;
|
||||||
|
|
||||||
|
var kills;
|
||||||
|
|
||||||
|
var timeout = undefined;
|
||||||
|
var dirty = false;
|
||||||
|
var deadFrames = 0;
|
||||||
|
var requesting = -1; //frame that we are requesting at.
|
||||||
|
var frameCache = []; //Frames after our request.
|
||||||
|
|
||||||
|
var allowAnimation = true;
|
||||||
|
|
||||||
|
var grid = new Grid(consts.GRID_SIZE, function(row, col, before, after) {
|
||||||
|
invokeRenderer('updateGrid', [row, col, before, after]);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides requestAnimationFrame in a cross browser way. (edited so that this is also compatible with node.)
|
||||||
|
* @author paulirish / http://paulirish.com/
|
||||||
|
*/
|
||||||
|
// window.requestAnimationFrame = function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) {
|
||||||
|
// window.setTimeout( callback, 1000 / 60 );
|
||||||
|
// };
|
||||||
|
var global;
|
||||||
|
if (!global)
|
||||||
|
global = window;
|
||||||
|
if ( !global.requestAnimationFrame ) {
|
||||||
|
global.requestAnimationFrame = ( function() {
|
||||||
|
return global.webkitRequestAnimationFrame ||
|
||||||
|
global.mozRequestAnimationFrame ||
|
||||||
|
global.oRequestAnimationFrame ||
|
||||||
|
global.msRequestAnimationFrame ||
|
||||||
|
function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) {
|
||||||
|
global.setTimeout( callback, 1000 / 60 );
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Public API
|
||||||
|
function connectGame(url, name, callback) {
|
||||||
|
if (running)
|
||||||
|
return; //Prevent multiple runs.
|
||||||
|
running = true;
|
||||||
|
|
||||||
|
user = null;
|
||||||
|
deadFrames = 0;
|
||||||
|
|
||||||
|
//Socket connection.
|
||||||
|
io.j = [];
|
||||||
|
io.sockets = [];
|
||||||
|
socket = io(url, {
|
||||||
|
'forceNew': true,
|
||||||
|
upgrade: false,
|
||||||
|
transports: ['websocket']
|
||||||
|
});
|
||||||
|
socket.on('connect', function(){
|
||||||
|
console.info('Connected to server.');
|
||||||
|
});
|
||||||
|
socket.on('game', function(data) {
|
||||||
|
if (timeout != undefined)
|
||||||
|
clearTimeout(timeout);
|
||||||
|
|
||||||
|
//Initialize game.
|
||||||
|
//TODO: display data.gameid --- game id #
|
||||||
|
frame = data.frame;
|
||||||
|
|
||||||
|
reset();
|
||||||
|
|
||||||
|
//Load players.
|
||||||
|
data.players.forEach(function(p) {
|
||||||
|
var pl = new Player(grid, p);
|
||||||
|
addPlayer(pl);
|
||||||
|
});
|
||||||
|
user = allPlayers[data.num];
|
||||||
|
if (!user)
|
||||||
|
throw new Error();
|
||||||
|
setUser(user);
|
||||||
|
|
||||||
|
//Load grid.
|
||||||
|
var gridData = new Uint8Array(data.grid);
|
||||||
|
for (var r = 0; r < grid.size; r++)
|
||||||
|
for (var c = 0; c < grid.size; c++)
|
||||||
|
{
|
||||||
|
var ind = gridData[r * grid.size + c] - 1;
|
||||||
|
grid.set(r, c, ind === -1 ? null : players[ind]);
|
||||||
|
}
|
||||||
|
|
||||||
|
invokeRenderer('paint', []);
|
||||||
|
frame = data.frame;
|
||||||
|
|
||||||
|
if (requesting !== -1)
|
||||||
|
{
|
||||||
|
//Update those cache frames after we updated game.
|
||||||
|
var minFrame = requesting;
|
||||||
|
requesting = -1;
|
||||||
|
while (frameCache.length > frame - minFrame)
|
||||||
|
processFrame(frameCache[frame - minFrame]);
|
||||||
|
frameCache = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('notifyFrame', processFrame);
|
||||||
|
|
||||||
|
socket.on('dead', function() {
|
||||||
|
socket.disconnect(); //In case we didn't get the disconnect call.
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('disconnect', function(){
|
||||||
|
if (!user)
|
||||||
|
return;
|
||||||
|
console.info('Server has disconnected. Creating new game.');
|
||||||
|
socket.disconnect();
|
||||||
|
user.die();
|
||||||
|
dirty = true;
|
||||||
|
paintLoop();
|
||||||
|
running = false;
|
||||||
|
invokeRenderer('disconnect', []);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.emit('hello', {
|
||||||
|
name: name,
|
||||||
|
type: 0, //Free-for-all
|
||||||
|
gameid: -1 //Requested game-id, or -1 for anyone.
|
||||||
|
}, function(success, msg) {
|
||||||
|
if (success)
|
||||||
|
console.info('Connected to game!');
|
||||||
|
else {
|
||||||
|
console.error('Unable to connect to game: ' + msg);
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
if (callback)
|
||||||
|
callback(success, msg);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeHeading(newHeading) {
|
||||||
|
if (!user || user.dead)
|
||||||
|
return;
|
||||||
|
if (newHeading === user.currentHeading || ((newHeading % 2 === 0) ^
|
||||||
|
(user.currentHeading % 2 === 0)))
|
||||||
|
{
|
||||||
|
//user.heading = newHeading;
|
||||||
|
if (socket) {
|
||||||
|
socket.emit('frame', {
|
||||||
|
frame: frame,
|
||||||
|
heading: newHeading
|
||||||
|
}, function(success, msg) {
|
||||||
|
if (!success)
|
||||||
|
console.error(msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlayers() {
|
||||||
|
return players.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Private API
|
||||||
|
function addPlayer(player) {
|
||||||
|
if (allPlayers[player.num])
|
||||||
|
return; //Already added.
|
||||||
|
allPlayers[player.num] = players[players.length] = player;
|
||||||
|
invokeRenderer('addPlayer', [player]);
|
||||||
|
return players.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function invokeRenderer(name, args) {
|
||||||
|
var renderer = exports.renderer;
|
||||||
|
if (renderer && typeof renderer[name] === 'function')
|
||||||
|
renderer[name].apply(exports, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function processFrame(data)
|
||||||
|
{
|
||||||
|
if (timeout != undefined)
|
||||||
|
clearTimeout(timeout);
|
||||||
|
|
||||||
|
if (requesting !== -1 && requesting < data.frame)
|
||||||
|
{
|
||||||
|
frameCache.push(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.frame - 1 !== frame)
|
||||||
|
{
|
||||||
|
console.error('Frames don\'t match up!');
|
||||||
|
socket.emit('requestFrame'); //Restore data.
|
||||||
|
requesting = data.frame;
|
||||||
|
frameCache.push(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame++;
|
||||||
|
if (data.newPlayers)
|
||||||
|
{
|
||||||
|
data.newPlayers.forEach(function(p) {
|
||||||
|
if (p.num === user.num)
|
||||||
|
return;
|
||||||
|
var pl = new Player(grid, p);
|
||||||
|
addPlayer(pl);
|
||||||
|
core.initPlayer(grid, pl);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var found = new Array(players.length);
|
||||||
|
data.moves.forEach(function(val, i) {
|
||||||
|
var player = allPlayers[val.num];
|
||||||
|
if (!player) return;
|
||||||
|
if (val.left) player.die();
|
||||||
|
found[i] = true;
|
||||||
|
player.heading = val.heading;
|
||||||
|
});
|
||||||
|
for (var i = 0; i < players.length; i++)
|
||||||
|
{
|
||||||
|
//Implicitly leaving game.
|
||||||
|
if (!found[i])
|
||||||
|
{
|
||||||
|
var player = players[i];
|
||||||
|
player && player.die();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update();
|
||||||
|
|
||||||
|
var locs = {};
|
||||||
|
for (var i = 0; i < players.length; i++)
|
||||||
|
{
|
||||||
|
var p = players[i];
|
||||||
|
locs[p.num] = [p.posX, p.posY, p.waitLag];
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.emit('verify', {
|
||||||
|
frame: frame,
|
||||||
|
locs: locs
|
||||||
|
}, function(frame, success, adviceFix, msg) {
|
||||||
|
if (!success && requesting === -1)
|
||||||
|
{
|
||||||
|
console.error(frame + ': ' + msg);
|
||||||
|
if (adviceFix)
|
||||||
|
socket.emit('requestFrame');
|
||||||
|
}
|
||||||
|
}.bind(this, frame));
|
||||||
|
|
||||||
|
dirty = true;
|
||||||
|
requestAnimationFrame(function() {
|
||||||
|
paintLoop();
|
||||||
|
});
|
||||||
|
timeout = setTimeout(function() {
|
||||||
|
console.warn('Server has timed-out. Disconnecting.');
|
||||||
|
socket.disconnect();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function paintLoop()
|
||||||
|
{
|
||||||
|
if (!dirty)
|
||||||
|
return;
|
||||||
|
invokeRenderer('paint', []);
|
||||||
|
dirty = false;
|
||||||
|
|
||||||
|
if (user.dead)
|
||||||
|
{
|
||||||
|
if (timeout)
|
||||||
|
clearTimeout(timeout);
|
||||||
|
if (deadFrames === 60) //One second of frame
|
||||||
|
{
|
||||||
|
var before =allowAnimation;
|
||||||
|
allowAnimation = false;
|
||||||
|
update();
|
||||||
|
invokeRenderer('paint', []);
|
||||||
|
allowAnimation = before;
|
||||||
|
user = null;
|
||||||
|
deadFrames = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.disconnect();
|
||||||
|
deadFrames++;
|
||||||
|
dirty = true;
|
||||||
|
update();
|
||||||
|
requestAnimationFrame(paintLoop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
user = null;
|
||||||
|
|
||||||
|
grid.reset();
|
||||||
|
players = [];
|
||||||
|
allPlayers = [];
|
||||||
|
kills = 0;
|
||||||
|
|
||||||
|
invokeRenderer('reset');
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUser(player) {
|
||||||
|
user = player;
|
||||||
|
invokeRenderer('setUser', [player]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
var dead = [];
|
||||||
|
core.updateFrame(grid, players, dead, function addKill(killer, other)
|
||||||
|
{
|
||||||
|
if (players[killer] === user && killer !== other)
|
||||||
|
kills++;
|
||||||
|
});
|
||||||
|
dead.forEach(function(val) {
|
||||||
|
console.log((val.name || 'Unnamed') + ' is dead');
|
||||||
|
delete allPlayers[val.num];
|
||||||
|
invokeRenderer('removePlayer', [val]);
|
||||||
|
});
|
||||||
|
|
||||||
|
invokeRenderer('update', []);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Export stuff
|
||||||
|
var funcs = [connectGame, changeHeading, getPlayers];
|
||||||
|
funcs.forEach(function (f) {
|
||||||
|
exports[f.name] = f;
|
||||||
|
});
|
||||||
|
|
||||||
|
exports.renderer = null;
|
||||||
|
Object.defineProperties(exports, {
|
||||||
|
allowAnimation: {
|
||||||
|
get: function() { return allowAnimation; },
|
||||||
|
set: function(val) { allowAnimation = !!val; },
|
||||||
|
enumerable: true
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
get: function() { return grid; },
|
||||||
|
enumerable: true
|
||||||
|
},
|
||||||
|
kills: {
|
||||||
|
get: function() { return kills; },
|
||||||
|
enumerable: true
|
||||||
|
}
|
||||||
|
});
|
7601
public/bundle.js
7601
public/bundle.js
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user