2017-02-24 19:57:13 -05:00
|
|
|
var GRID_SIZE = 80;
|
|
|
|
var CELL_WIDTH = 40;
|
2017-02-25 18:52:52 -05:00
|
|
|
var MAX_PLAYERS = 255;
|
|
|
|
|
|
|
|
var Grid = require("./grid.js");
|
2017-02-24 19:57:13 -05:00
|
|
|
var Player = require("./player.js");
|
2017-02-25 18:52:52 -05:00
|
|
|
var core = require("./game-core.js");
|
2017-02-24 19:57:13 -05:00
|
|
|
|
2017-02-23 23:10:36 -05:00
|
|
|
function Game(id)
|
|
|
|
{
|
2017-02-25 18:52:52 -05:00
|
|
|
var nextInd = 0;
|
2017-02-23 23:10:36 -05:00
|
|
|
var players = [];
|
|
|
|
var newPlayers = [];
|
2017-02-25 18:52:52 -05:00
|
|
|
var newPlayerFrames = [];
|
2017-02-23 23:10:36 -05:00
|
|
|
var frame = 0;
|
|
|
|
|
2017-02-24 19:57:13 -05:00
|
|
|
var filled = 0;
|
|
|
|
var grid = new Grid(GRID_SIZE, function(row, col, before, after) {
|
|
|
|
if (!!after ^ !!before)
|
|
|
|
{
|
|
|
|
if (after)
|
|
|
|
filled++;
|
|
|
|
else
|
|
|
|
filled--;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2017-02-23 23:10:36 -05:00
|
|
|
|
|
|
|
this.id = id;
|
|
|
|
|
2017-02-24 19:57:13 -05:00
|
|
|
this.addPlayer = function(client, name) {
|
2017-02-25 18:52:52 -05:00
|
|
|
if (players.length >= MAX_PLAYERS)
|
|
|
|
return false;
|
|
|
|
|
2017-02-24 19:57:13 -05:00
|
|
|
var start = findEmpty(grid);
|
|
|
|
if (!start)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
var params = {
|
|
|
|
posX: start.col * CELL_WIDTH,
|
|
|
|
posY: start.row * CELL_WIDTH,
|
2017-02-25 18:52:52 -05:00
|
|
|
currentHeading: Math.floor(Math.random() * 4),
|
2017-02-24 19:57:13 -05:00
|
|
|
name: name,
|
2017-02-25 18:52:52 -05:00
|
|
|
num: nextInd
|
|
|
|
};
|
2017-02-24 19:57:13 -05:00
|
|
|
|
|
|
|
var p = new Player(false, grid, params);
|
|
|
|
p.client = client;
|
2017-02-25 18:52:52 -05:00
|
|
|
players.push(p);
|
|
|
|
newPlayers.push(p);
|
|
|
|
newPlayerFrames.push(p);
|
|
|
|
nextInd++;
|
|
|
|
core.initPlayer(p);
|
2017-02-24 19:57:13 -05:00
|
|
|
|
2017-02-25 18:52:52 -05:00
|
|
|
var splayers = players.map(function(val) {return val.serialData();});
|
|
|
|
client.emit("game", {
|
|
|
|
"num": p.num,
|
|
|
|
"gameid": id,
|
|
|
|
"frame": frame,
|
|
|
|
"players": splayers,
|
|
|
|
"grid": gridSerialData(grid, players)
|
|
|
|
});
|
2017-02-23 23:10:36 -05:00
|
|
|
|
2017-02-25 18:52:52 -05:00
|
|
|
//TODO: limit number of requests per frame.
|
|
|
|
client.on("requestFrame", function (fn) {
|
|
|
|
var splayers = players.map(function(val) {return val.serialData();});
|
|
|
|
fn({
|
|
|
|
"num": p.num,
|
|
|
|
"gameid": id,
|
|
|
|
"frame": frame,
|
|
|
|
"players": splayers,
|
|
|
|
"grid": gridSerialData(grid, players)
|
|
|
|
});
|
|
|
|
});
|
|
|
|
client.on("frame", function(data, errorHan){
|
|
|
|
if (typeof data === "function")
|
|
|
|
{
|
|
|
|
errorHan(false, "No data supplied.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof errorHan !== "function")
|
|
|
|
errorHan = function() {};
|
|
|
|
|
|
|
|
if (!data)
|
|
|
|
errorHan(false, "No data supplied.");
|
|
|
|
else if (!checkInt(data.frame, 0, Infinity))
|
|
|
|
errorHan(false, "Requires a valid non-negative frame integer.");
|
|
|
|
else if (data.frame < frame)
|
|
|
|
errorHan(false, "Late frame received.");
|
|
|
|
else if (data.frame > frame)
|
|
|
|
errorHan(false, "Invalid frame received.");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (data.heading)
|
|
|
|
{
|
|
|
|
if (checkInt(data.heading, 0, 4))
|
|
|
|
{
|
|
|
|
p.heading = data.heading;
|
|
|
|
errorHan(true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
errorHan(false, "New heading must be an integer of range [0, 4).");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2017-02-24 19:57:13 -05:00
|
|
|
return true;
|
2017-02-25 18:52:52 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
this.tickFrame = function() {
|
|
|
|
var snews = newPlayers.map(function(val) {return val.serialData();});
|
|
|
|
var moves = players.map(function(val) {return {heading: val.heading};});
|
|
|
|
|
|
|
|
var data = {moves: moves};
|
|
|
|
if (snews.length > 0)
|
|
|
|
{
|
|
|
|
data.newPlayers = snews;
|
|
|
|
newPlayers = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
players.forEach(function(val) {val.client.emit("notifyFrame", data)});
|
|
|
|
|
|
|
|
frame++;
|
|
|
|
update();
|
|
|
|
};
|
|
|
|
|
|
|
|
function update()
|
|
|
|
{
|
|
|
|
var dead = [];
|
|
|
|
core.updateFrame(grid, players, newPlayerFrames, dead);
|
|
|
|
dead.forEach(function(val) { val.client.disconnect(true); });
|
2017-02-23 23:10:36 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-25 18:52:52 -05:00
|
|
|
function checkInt(value, min, max)
|
|
|
|
{
|
|
|
|
if (typeof value !== "number")
|
|
|
|
return false;
|
|
|
|
if (value < min || value >= max)
|
|
|
|
return false;
|
|
|
|
if (Math.floor(value) !== value)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function gridSerialData(grid, players)
|
|
|
|
{
|
|
|
|
var buff = Buffer.alloc(grid.size * grid.size);
|
|
|
|
|
|
|
|
var numToIndex = new Array(players[players.length - 1].num + 1);
|
|
|
|
for (var i = 0; i < players.length; i++)
|
|
|
|
numToIndex[players[i].num] = i + 1;
|
|
|
|
|
|
|
|
for (var r = 0; r < grid.size; r++)
|
|
|
|
for (var c = 0; c < grid.size; c++)
|
|
|
|
{
|
|
|
|
var ele = grid.get(r, c);
|
|
|
|
buff[r * grid.size + c] = ele ? numToIndex[ele.num] : 0;
|
|
|
|
}
|
|
|
|
return buff;
|
|
|
|
}
|
|
|
|
|
2017-02-24 19:57:13 -05:00
|
|
|
function findEmpty(grid)
|
|
|
|
{
|
|
|
|
var available = [];
|
|
|
|
|
|
|
|
for (var r = 1; r < grid.size - 1; r++)
|
|
|
|
for (var c = 1; c < grid.size - 1; c++)
|
|
|
|
{
|
|
|
|
var cluttered = false;
|
|
|
|
checkclutter: for (var dr = -1; dr <= 1; dr++)
|
|
|
|
{
|
|
|
|
for (var dc = -1; dc <= 1; dc++)
|
|
|
|
{
|
|
|
|
if (grid.get(r + dr, c + dc))
|
|
|
|
{
|
|
|
|
cluttered = true;
|
|
|
|
break checkclutter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!cluttered)
|
|
|
|
available.push({row: r, col: c});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (available.length === 0)
|
|
|
|
return null;
|
|
|
|
else
|
|
|
|
return available[Math.floor(available.length * Math.random())];
|
|
|
|
}
|
|
|
|
|
2017-02-23 23:10:36 -05:00
|
|
|
module.exports = Game;
|