"use strict";
var express = require('express');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io').listen(server);
const { exec } = require("child_process");
/**
* Instances of GameType.
*/
var gameTypes = {};
/**
* Instance of Game that are currently being played on this server. Keyed on
* the prototype game's id.
*/
var games = {};
class GameType {
constructor(id, name, variations) {
this.id = id;
this.name = name;
this.variations = variations;
this.instances = {}; // Running instances of type Game keyed on Game.id.
this.nextGameId = 1;
}
newGame(variationId, name) {
for ( var variationIndex = 0; variationIndex < this.variations.length; variationIndex ++ ) {
var variation = this.variations[variationIndex];
if ( variation.id == variationId ) {
var game = variation.copy(`${variation.id}${this.nextGameId ++}`);
game.name = name;
games[game.id] = game;
this.instances[game.id] = game;
return game;
}
}
return null;
}
listGames(includePrivate) {
var instances = {};
for (var id in this.instances) {
var game = this.instances[id];
if (includePrivate || game.isPublic) {
instances[id] = {
gameId: game.id,
name: game.name,
playerCount: Object.keys(game.players).length,
maxPlayers: game.maxPlayers,
lastActive : game.lastActive.getTime()
};
}
}
return instances;
}
}
function addGameTypeVariations( id, name, variations ) {
gameTypes[id] = new GameType( id, name, variations );
}
function addGameType(variation) {
gameTypes[variation.id] = new GameType(variation.id, variation.name, [variation] );
}
function resetAction(game) {
io.to(game.id).emit( 'movePieces', game.reset() );
}
function sendCharade(game, socket, type, filename, size) {
try {
const nth = Math.floor( Math.random() * size );
const command = `head -n ${nth} charades/${filename} | tail -1`;
exec(command, (error, stdout, stderr) => {
const chatInfo = {
name : '',
message : `(${type}) ${stdout}`,
playerNumber : -1,
isSpectator : false
};
socket.emit( 'chat', chatInfo );
});
} catch (e) {
console.log( `sendCharade failed : ${e}` );
}
}
function takeRandomCharadeAction(game, socket) {
const type = Math.floor( Math.random() * 4 );
if ( type == 1 ) {
takeSongCharadeAction(game,socket);
} else if (type == 2) {
takeTVCharadeAction(game,socket);
} else if (type == 3) {
takeBookCharadeAction(game,socket);
} else {
takeFilmCharadeAction(game,socket);
}
}
function takeFilmCharadeAction(game, socket) {
sendCharade(game,socket, "Film", "top1000Films.txt", 1000 );
}
function takeSongCharadeAction(game, socket) {
sendCharade(game,socket, "Song", "top1000Songs.txt", 1000 );
}
function takeTVCharadeAction(game, socket) {
sendCharade(game,socket, "TV Show", "top250TVShows.txt", 250 );
}
function takeBookCharadeAction(game, socket) {
sendCharade(game,socket, "Book", "top1000Books.txt", 998 );
}
function shuffleAction(game) {
io.to(game.id).emit( 'movePieces', game.shuffle() );
}
function throwAction(game) {
io.to(game.id).emit( 'throwDice', game.throwDice('dice') );
}
function createNameTagsAction(game) {
game.createNameTags();
}
function createXmasDecorationsAction(game) {
game.createXmasDecorations();
}
function createBirthdayDecorationsAction(game) {
game.createBirthdayDecorations();
}
function throwBlueAction(game) {
io.to(game.id).emit( 'throwDice', game.throwDice('diceBlue') );
}
function createMoveGroupAction( fromAreaName, toAreaName, shuffle ) {
return function(game) {
io.to(game.id).emit( 'movePieces', game.moveGroup( fromAreaName, toAreaName, shuffle ) );
};
}
class Game {
constructor( id, name ) {
this.id = id;
this.name = name;
this.isPublic = true; // Spectators can join the game.
this.maxPlayers = 2; // Maximum number of players (player with higher indices will be SPECTATORS).
this.instructions = ''; // HTML, not plain text
this.players = {}; // Keyed on the socket's id.
this.assets = {}; // Keyed on name
this.pieces = {}; // Keyed on name.
this.backgrounds = {}; // Keyed on name.
this.buttons = {}; // Keyed on name.
this.specialAreas = {}; // keyed by a string (the area does not have a name).
this.glue = {};
this.topMost = 0; // The maximum zIndex of all this.pieces.zIndex
this.minSnap2 = 36; // Snap by 6 pixels (36 is the square of the distance).
this.lastActive = new Date(); // The last action that the server received from a player.
this.assets['nameTag'] = {
type : 'ninepatch',
name : 'nameTag',
url : 'assets/nameTag.png',
sizeX : 99,
sizeY : 27,
columns: [5,89,5],
rows: [5,17,5],
margin: [2,10,2,10]
};
this.buttons['nametags'] = {
name: 'nametags',
text: 'Add Tags',
instructions: 'Adds a movable name tag for each player',
action: createNameTagsAction,
assetName: 'button',
x: -1100,
y: 100
};
this.buttons['xmas'] = {
name: 'xmas',
text: 'Add Christmas Decorations',
instructions: 'Adds Christmas decoration pieces that can be moved about',
action: createXmasDecorationsAction,
assetName: 'button',
x: -1100,
y: 100
};
this.buttons['birthday'] = {
name: 'birthday',
text: 'Add Birthday Decorations',
instructions: 'Adds Birthday decoration pieces that can be moved about',
action: createBirthdayDecorationsAction,
assetName: 'button',
x: -1100,
y: 100
};
}
active() {
this.lastActive = new Date();
}
copy(id) {
var result = new Game(id);
result.instructions = this.instructions;
shallowCopyObjectMap( this.assets, result.assets );
shallowCopyObjectMap( this.backgrounds, result.backgrounds );
shallowCopyObjectMap( this.buttons, result.buttons );
shallowCopyObjectMap( this.specialAreas, result.specialAreas );
shallowCopyObjectMap( this.pieces, result.pieces );
for (var glueId in this.glue) {
result.glue[glueId] = this.glue[glueId];
}
result.topMost = this.topMost;
result.minSnap2 = this.minSnap2;
result.prototype = this; // Used by shuffle()
result.shuffle();
return result;
}
initCribbage() {
console.log( `Initialising cribbage` );
this.deckX = 450;
this.deckY = 480;
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/cribbage.png',
sizeX : 800,
sizeY : 600
};
this.assets['button'] = {
type : 'spritesheet',
name : 'button',
url : 'assets/button.png',
sizeX : 104,
sizeY : 31
};
this.assets['cards'] = {
type : 'spritesheet',
name : 'cards',
url : 'assets/pieces/cards.png',
sizeX : 79,
sizeY : 123
};
this.assets['pinBlue'] = {
type : 'image',
name : 'pinBlue',
url : 'assets/pieces/pinBlue.png',
sizeX : 40,
sizeY : 40
};
this.assets['pinRed'] = {
type : 'image',
name : 'pinRed',
url : 'assets/pieces/pinRed.png',
sizeX : 40,
sizeY : 40
};
this.assets['dealer'] = {
type : 'image',
name : 'dealer',
url : 'assets/pieces/dealer.png',
sizeX : 46,
sizeY : 46
};
this.backgrounds['board'] = {
name : 'board',
assetName : 'board',
x : 400,
y : 300
};
this.specialAreas['player0'] = {
type: 'reveal',
x : 0,
y : 385,
width : 290,
height : 212,
playerNumber : 0
};
this.specialAreas['player1'] = {
type: 'reveal',
x : 504,
y : 385,
width : 290,
height : 212,
playerNumber : 1
};
this.specialAreas['showdown'] = {
type: 'reveal',
x : 0,
y : 0,
width : 800,
height : 390,
playerNumber : null
};
this.specialAreas['holes'] = {
type: 'snap',
x: 100,
y: 34,
width: 575,
height: 140,
snapX: 20,
snapY: 20
};
this.specialAreas['pack'] = {
type: 'shuffle',
x: this.deckX - 40,
y: this.deckY - 62,
width: 80,
height: 124
}
this.specialAreas['deckCounter'] = {
type: 'counter',
x: this.deckX,
y: this.deckY,
textX: this.deckX-8,
textY: this.deckY - 80
}
this.buttons['shuffle'] = {
name: 'shuffle',
text: 'Shuffle',
action: shuffleAction,
confirm: true,
assetName: 'button',
x: 450,
y: 560,
};
for ( var suit = 0; suit < 4; suit ++ ) {
for ( var value = 0; value < 13; value ++ ) {
var name = `card_${suit}_${value}`;
this.pieces[name] = {
name : name,
assetName : 'cards',
assetNumber : 52+2,
hiddenAssetNumber : 52+2,
visibleAssetNumber : suit * 13 + value,
x : this.deckX,
y : this.deckY,
zIndex : this.topMost++,
gluedTo : null
}
}
}
name = 'pinRed';
this.pieces[name] = {
name : name,
assetName : 'pinRed',
x : 83,
y : 45,
zIndex : this.topMost++,
gluedTo : null,
isPermanent : true
};
name = 'pinBlue';
this.pieces[name] = {
name : name,
assetName : 'pinBlue',
x : 83,
y : 168,
zIndex : this.topMost++,
gluedTo : null,
isPermanent : true
};
this.pieces['dealer'] = {
name: 'dealer',
assetName: 'dealer',
x: 400,
y: 400,
zIndex: this.topMost++,
isPermanent : true
};
return this;
}
initTwoPlayerCards() {
console.log( `Initialising twoPlayerCards` );
this.deckX = 60;
this.deckY = 300;
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/twoPlayerCards.png',
sizeX : 800,
sizeY : 600
};
this.assets['cards'] = {
type : 'spritesheet',
name : 'cards',
url : 'assets/pieces/cards.png',
sizeX : 79,
sizeY : 123
};
this.assets['button'] = {
type : 'spritesheet',
name : 'button',
url : 'assets/button.png',
sizeX : 104,
sizeY : 31
};
this.assets['dealer'] = {
type : 'image',
name : 'dealer',
url : 'assets/pieces/dealer.png',
sizeX : 46,
sizeY : 46
};
this.backgrounds['board'] = {
name : 'board',
assetName : 'board',
x : 400,
y : 300
};
this.specialAreas['player0'] = {
type: 'reveal',
x : 0,
y : 600-140,
width : 800,
height : 140,
playerNumber : 1
};
this.specialAreas['player1'] = {
type: 'reveal',
x : 0,
y : 0,
width : 800,
height : 140,
playerNumber : 0
};
this.specialAreas['showdown'] = {
type: 'reveal',
x : 160,
y : 140,
width : 800-160,
height : 600-140*2,
playerNumber : null
};
this.specialAreas['pack'] = {
type: 'shuffle',
x: this.deckX - 40,
y: this.deckY - 62,
width: 80,
height: 124
}
this.specialAreas['deckCounter'] = {
type: 'counter',
x: this.deckX,
y: this.deckY,
textX: this.deckX-8,
textY: this.deckY - 80
}
this.buttons['shuffle'] = {
name: 'shuffle',
text: 'Shuffle',
action: shuffleAction,
confirm: true,
assetName : 'button',
x: this.deckX,
y: this.deckY + 90
};
for ( var suit = 0; suit < 4; suit ++ ) {
for ( var value = 0; value < 13; value ++ ) {
var name = `card_${suit}_${value}`;
this.pieces[name] = {
name : name,
assetName : 'cards',
assetNumber : 52+2,
hiddenAssetNumber : 52+2,
visibleAssetNumber : suit * 13 + value,
x : this.deckX,
y : this.deckY,
zIndex : this.topMost++,
gluedTo : null
}
}
}
this.pieces['dealer'] = {
name: 'dealer',
assetName: 'dealer',
x: 160,
y: this.deckY,
zIndex: this.topMost++,
isPermanent : true
};
return this;
}
initTwoPlayerCardsMini() {
console.log( `Initialising twoPlayerCardsMini` );
this.deckX = 56;
this.deckY = 300;
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/twoPlayerCardsMini.png',
sizeX : 800,
sizeY : 600
};
this.assets['cards'] = {
type : 'spritesheet',
name : 'cards',
url : 'assets/pieces/miniPlayingCards.png',
sizeX : 41,
sizeY : 61
};
this.assets['button'] = {
type : 'spritesheet',
name : 'button',
url : 'assets/button.png',
sizeX : 104,
sizeY : 31
};
this.assets['dealer'] = {
type : 'image',
name : 'dealer',
url : 'assets/pieces/dealer.png',
sizeX : 46,
sizeY : 46
};
this.backgrounds['board'] = {
name : 'board',
assetName : 'board',
x : 400,
y : 300
};
this.specialAreas['player0'] = {
type: 'reveal',
x : 0,
y : 600-90,
width : 800,
height : 90,
playerNumber : 1
};
this.specialAreas['player1'] = {
type: 'reveal',
x : 0,
y : 0,
width : 800,
height : 90,
playerNumber : 0
};
this.specialAreas['showdown'] = {
type: 'reveal',
x : 85,
y : 90,
width : 800-85,
height : 600-90*2,
playerNumber : null
};
this.specialAreas['deck'] = {
type: 'bottom',
assetName: 'cards',
x : this.deckX - 20,
y : this.deckY - 30,
width : 40,
height : 60,
};
this.specialAreas['deckCounter'] = {
type: 'counter',
x: this.deckX,
y: this.deckY,
textX: this.deckX-8,
textY: this.deckY - 60
}
this.buttons['shuffle'] = {
name: 'shuffle',
text: 'Shuffle',
action: shuffleAction,
confirm: true,
assetName: 'button',
x: this.deckX,
y: this.deckY + 55
};
for ( var suit = 0; suit < 4; suit ++ ) {
for ( var value = 0; value < 13; value ++ ) {
var name = `card_${suit}_${value}`;
this.pieces[name] = {
name : name,
assetName : 'cards',
assetNumber : 52+2,
hiddenAssetNumber : 52+2,
visibleAssetNumber : suit * 13 + value,
x : this.deckX,
y : this.deckY,
zIndex : this.topMost++,
gluedTo : null
}
}
}
this.pieces['dealer'] = {
name: 'dealer',
assetName: 'dealer',
x: 85,
y: 120,
zIndex: this.topMost++,
isPermanent : true
};
return this;
}
initDraughts() {
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/chess.jpg',
sizeX : 800,
sizeY : 600
};
this.assets['white'] = {
type : 'image',
name : 'white',
url : 'assets/pieces/discWhite46.png',
sizeX : 46,
sizeY : 46
};
this.assets['black'] = {
type : 'image',
name : 'black',
url : 'assets/pieces/discBlack46.png',
sizeX : 46,
sizeY : 46
};
this.assets['crown'] = {
type : 'image',
name : 'crown',
url : 'assets/pieces/crown.png',
sizeX : 40,
sizeY : 23
};
this.assets['button'] = {
type : 'spritesheet',
name : 'button',
url : 'assets/button.png',
sizeX : 104,
sizeY : 31
};
this.specialAreas['grid'] = {
type: 'snap',
x: 230-50/2,
y: 129-50/2,
width: 49*8,
height: 49*8,
snapX: 49,
snapY: 49
};
this.glue['crown'] = ['white', 'black'];
this.backgrounds['board'] = {
name : 'board',
assetName : 'board',
x : 400,
y : 300
};
this.buttons['reset'] = {
name: 'reset',
text: 'Reset',
action: resetAction,
confirm: true,
assetName: 'button',
x: 720,
y: 570,
};
var x,y,i,name;
i = 0;
for (x = 0; x < 4; x++) {
for (y = 0; y < 3; y ++) {
name = `white_${i}`;
this.pieces[name] = {
name : name,
assetName : 'white',
x : 230 + 98 * x + ((y == 1) ? 0 : 50),
y : 125 + 50 * y,
zIndex : this.topMost++,
gluedTo : null
}
name = `black_${i}`;
this.pieces[name] = {
name : name,
assetName : 'black',
x : 230 + 98 * x + ((y == 1) ? 50 : 0),
y : 472 - 50 * y,
zIndex : this.topMost++,
gluedTo : null
}
i ++;
}
}
i = 0;
for (x = 0; x < 9; x ++ ) {
name = `crown_${i++}`;
this.pieces[name] = {
name : name,
assetName : 'crown',
x : x + 200 + x * 49,
y : 50,
zIndex : this.topMost++,
gluedTo : null
}
name = `crown_${i++}`;
this.pieces[name] = {
name : name,
assetName : 'crown',
x : x + 200 + x * 49,
y : 550,
zIndex : this.topMost++,
gluedTo : null
}
}
return this;
}
initChess() {
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/chess.jpg',
sizeX : 800,
sizeY : 600
};
this.assets['chessPieces'] = {
type : 'spritesheet',
name : 'chessPieces',
url : 'assets/pieces/chessPieces.png',
sizeX : 280/6,
sizeY : 50
};
this.assets['button'] = {
type : 'spritesheet',
name : 'button',
url : 'assets/button.png',
sizeX : 104,
sizeY : 31
};
this.specialAreas['grid'] = {
type: 'snap',
x: 230-50/2,
y: 129-50/2,
width: 49*8,
height: 49*8,
snapX: 49,
snapY: 49
};
this.backgrounds['board'] = {
name : 'board',
assetName : 'board',
x : 400,
y : 300
};
this.buttons['reset'] = {
name: 'reset',
text: 'Reset',
action: resetAction,
confirm: true,
assetName : 'button',
x: 720,
y: 570,
};
for ( var x = 0; x < 8; x ++ ) {
this.addChessPiece( 5, x, 1 );
}
this.addChessPiece( 2, 0, 0 );
this.addChessPiece( 4, 1, 0 );
this.addChessPiece( 3, 2, 0 );
this.addChessPiece( 0, 3, 0 );
this.addChessPiece( 1, 4, 0 );
this.addChessPiece( 3, 5, 0 );
this.addChessPiece( 4, 6, 0 );
this.addChessPiece( 2, 7, 0 );
return this;
}
addChessPiece( pieceNumber, x, y ) {
var name = `piece_${x}_${y}`;
this.pieces[name] = {
name : name,
assetName : 'chessPieces',
assetNumber: pieceNumber + 6,
x : 230 + 49 * x,
y : 129 + 49 * y,
zIndex : this.topMost++,
gluedTo : null
}
y = 7-y;
name = `piece_${x}_${y}`;
this.pieces[name] = {
name : name,
assetName : 'chessPieces',
assetNumber: pieceNumber,
x : 230 + 49 * x,
y : 129 + 49 * y,
zIndex : this.topMost++,
gluedTo : null
}
}
initBackgammon() {
console.log( `Initialising backgammon` );
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/backgammon.png',
sizeX : 800,
sizeY : 600
};
this.assets['white'] = {
type : 'image',
name : 'white',
url : 'assets/pieces/discWhite46.png',
sizeX : 46,
sizeY : 46
};
this.assets['black'] = {
type : 'image',
name : 'black',
url : 'assets/pieces/discBlack46.png',
sizeX : 46,
sizeY : 46
};
this.assets['button'] = {
type : 'spritesheet',
name : 'button',
url : 'assets/button.png',
sizeX : 104,
sizeY : 31
};
this.assets['dice'] = {
type : 'spritesheet',
name : 'dice',
url : 'assets/pieces/dice.png',
sizeX : 42,
sizeY : 38
};
this.backgrounds['board'] = {
name : 'board',
assetName : 'board',
x : 400,
y : 300
};
this.buttons['throw'] = {
name: 'throw',
text: 'Throw',
action: throwAction,
assetName : 'button',
x: 300,
y: 325
};
this.buttons['reset'] = {
name: 'reset',
text: 'Reset',
action: resetAction,
confirm: true,
assetName: 'button',
x: 150,
y: 300
};
this.specialAreas['gridBL'] = {
type: 'snap',
x: 55,
y: 345,
width: 310,
height: 210,
snapX: 330/12,
snapY: 46/2
};
this.specialAreas['gridTL'] = {
type: 'snap',
x: 55,
y: 48,
width: 310,
height: 210,
snapX: 330/12,
snapY: 46/2
};
this.specialAreas['gridBR'] = {
type: 'snap',
x: 55 + 390,
y: 345,
width: 310,
height: 210,
snapX: 330/12,
snapY: 46/2
};
this.specialAreas['gridTR'] = {
type: 'snap',
x: 55 + 390,
y: 48,
width: 310,
height: 210,
snapX: 330/12,
snapY: 46/2
};
this.addBackgammonPieces(732, 1, 2, 'white');
this.addBackgammonPieces(732, -1, 2, 'black');
this.addBackgammonPieces(458, 1, 5, 'black');
this.addBackgammonPieces(458, -1, 5, 'white');
this.addBackgammonPieces(68, 1, 5, 'white');
this.addBackgammonPieces(68, -1, 5, 'black');
this.addBackgammonPieces(288, 1, 3, 'black');
this.addBackgammonPieces(288, -1, 3, 'white');
this.pieces['dice_0'] = {
name : 'dice_0',
assetName : 'dice',
assetNumber : 0,
x : 280,
y : 285
};
this.pieces['dice_1'] = {
name : 'dice_1',
assetName : 'dice',
assetNumber : 0,
x : 320,
y : 285
};
return this;
}
addBackgammonPieces( x, dy, count, color ) {
var y = dy == 1 ? 60 : 540;
for (var i = 0; i < count; i ++ ) {
var name = color + Object.keys(this.pieces).length;
this.pieces[name] = {
name: name,
assetName: color,
x: x,
y: y + i * dy * 46
}
}
}
initCrossedWords() {
console.log( `Initialising CrossedWords` );
this.bagX = 620;
this.bagY = 550;
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/crossedWords.png',
sizeX : 800,
sizeY : 600
};
this.assets['letters'] = {
type : 'spritesheet',
name : 'letters',
url : 'assets/pieces/letters.png',
sizeX : 33,
sizeY : 33
};
this.assets['button'] = {
type: 'spritesheet',
name: 'button',
url: 'assets/button.png',
sizeX: 104,
sizeY: 31
};
this.backgrounds['board'] = {
name: 'board',
assetName: 'board',
x: 400,
y: 300
};
this.specialAreas['hand'] = {
type: 'reveal',
x: 0,
y: 520,
width: 520,
height:750,
playerNumber : -1
};
this.specialAreas['board'] = {
type: 'reveal',
x: 0,
y: 0,
width: 520,
height: 520,
playerNumber: null
};
this.specialAreas['boardSnap'] = {
type: 'snap',
x: 10,
y: 10,
width: 515,
height: 515,
snapX: 34.5,
snapY: 34.5
};
this.specialAreas['bag'] = {
type: 'shuffle',
x: this.bagX - 20,
y: this.bagY - 20,
width: 40,
height: 40
};
this.specialAreas['tileCounter'] = {
type: 'counter',
x: this.bagX,
y: this.bagY,
textX: this.bagX+40,
textY: this.bagY-10
}
this.buttons['reset'] = {
name: 'reset',
text: 'Reset',
action: resetAction,
confirm: true,
assetName: 'button',
x: 680,
y: 20
};
this.addCrossedWordsLetter('_',2);
this.addCrossedWordsLetter('A',9);
this.addCrossedWordsLetter('B',2);
this.addCrossedWordsLetter('C',2);
this.addCrossedWordsLetter('D',4);
this.addCrossedWordsLetter('E',12);
this.addCrossedWordsLetter('F',2);
this.addCrossedWordsLetter('G',3);
this.addCrossedWordsLetter('H',2);
this.addCrossedWordsLetter('I',9);
this.addCrossedWordsLetter('J',1);
this.addCrossedWordsLetter('K',1);
this.addCrossedWordsLetter('L',4);
this.addCrossedWordsLetter('M',2);
this.addCrossedWordsLetter('N',6);
this.addCrossedWordsLetter('O',8);
this.addCrossedWordsLetter('P',2);
this.addCrossedWordsLetter('Q',1);
this.addCrossedWordsLetter('R',6);
this.addCrossedWordsLetter('S',4);
this.addCrossedWordsLetter('T',6);
this.addCrossedWordsLetter('U',4);
this.addCrossedWordsLetter('V',2);
this.addCrossedWordsLetter('W',2);
this.addCrossedWordsLetter('X',1);
this.addCrossedWordsLetter('Y',2);
this.addCrossedWordsLetter('Z',1);
return this;
}
initSpeedAnagrams() {
console.log( `Initialising Speed Anagram` );
this.instructions = '<a href="https://bananagrams.com/blog/how-to-play-bananagrams-instructions-for-getting-started">How to play</a>';
this.bagX = 400;
this.bagY = 300;
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/speedAnagrams.png',
sizeX : 800,
sizeY : 600
};
this.assets['letters'] = {
type : 'spritesheet',
name : 'letters',
url : 'assets/pieces/letters.png',
sizeX : 33,
sizeY : 33
};
this.assets['button'] = {
type: 'spritesheet',
name: 'button',
url: 'assets/button.png',
sizeX: 104,
sizeY: 31
};
this.backgrounds['board'] = {
name: 'board',
assetName: 'board',
x: 400,
y: 300
};
this.specialAreas['hand'] = {
type: 'reveal',
x: 0,
y: 520,
width: 520,
height:750,
playerNumber : -1
};
this.specialAreas['board1'] = {
type: 'reveal',
x: 0,
y: 0,
width: 390,
height: 600,
playerNumber: null
};
this.specialAreas['board2'] = {
type: 'reveal',
x: 410,
y: 0,
width: 390,
height: 600,
playerNumber: null
};
this.specialAreas['boardSnap'] = {
type: 'snap',
x: 0,
y: 0,
width: 800,
height: 600,
snapX: 34.5,
snapY: 34.5
};
this.specialAreas['bag'] = {
type: 'shuffle',
x: this.bagX - 20,
y: this.bagY - 20,
width: 40,
height: 40
};
this.specialAreas['tileCounter'] = {
type: 'counter',
x: this.bagX,
y: this.bagY,
textX: this.bagX-12,
textY: this.bagY+20
}
this.buttons['reset'] = {
name: 'reset',
text: 'Reset',
action: resetAction,
confirm: true,
assetName: 'button',
x: 400,
y: 20
};
this.addCrossedWordsLetter('A',13);
this.addCrossedWordsLetter('B',3);
this.addCrossedWordsLetter('C',3);
this.addCrossedWordsLetter('D',6);
this.addCrossedWordsLetter('E',18);
this.addCrossedWordsLetter('F',3);
this.addCrossedWordsLetter('G',4);
this.addCrossedWordsLetter('H',3);
this.addCrossedWordsLetter('I',12);
this.addCrossedWordsLetter('J',2);
this.addCrossedWordsLetter('K',2);
this.addCrossedWordsLetter('L',5);
this.addCrossedWordsLetter('M',3);
this.addCrossedWordsLetter('N',8);
this.addCrossedWordsLetter('O',11);
this.addCrossedWordsLetter('P',3);
this.addCrossedWordsLetter('Q',2);
this.addCrossedWordsLetter('R',9);
this.addCrossedWordsLetter('S',6);
this.addCrossedWordsLetter('T',9);
this.addCrossedWordsLetter('U',6);
this.addCrossedWordsLetter('V',3);
this.addCrossedWordsLetter('W',3);
this.addCrossedWordsLetter('X',2);
this.addCrossedWordsLetter('Y',3);
this.addCrossedWordsLetter('Z',2);
return this;
}
addCrossedWordsLetter( letter, count ) {
for ( var i = 0; i < count; i ++ ) {
var name = 'letter' + letter + '_' + i;
var assetNumber = (letter == '_') ? 0 : letter.charCodeAt(0) - 64;
const piece = {
name: name,
assetName: 'letters',
assetNumber: 0,
hiddenAssetNumber: 0,
visibleAssetNumber: assetNumber,
x: this.bagX,
y: this.bagY,
zIndex: this.topMost ++
};
this.pieces[name] = piece;
}
}
initCrossedNumbers() {
console.log( `Initialising CrossedNumbers` );
this.instructions = 'Read the rules on <a href="https://www.instructables.com/id/Number-Scrabble-The-Game-aka-Math-Scrabble/">instructables.com</a>';
this.bagX = 620;
this.bagY = 550;
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/crossedWords.png',
sizeX : 800,
sizeY : 600
};
this.assets['tiles'] = {
type : 'spritesheet',
name : 'tiles',
url : 'assets/pieces/numbers.png',
sizeX : 33,
sizeY : 33
};
this.assets['button'] = {
type: 'spritesheet',
name: 'button',
url: 'assets/button.png',
sizeX: 104,
sizeY: 31
};
this.backgrounds['board'] = {
name: 'board',
assetName: 'board',
x: 400,
y: 300
};
this.specialAreas['hand'] = {
type: 'reveal',
x: 0,
y: 520,
width: 520,
height:750,
playerNumber : -1
};
this.specialAreas['board'] = {
type: 'reveal',
x: 0,
y: 0,
width: 520,
height: 520,
playerNumber: null
};
this.specialAreas['boardSnap'] = {
type: 'snap',
x: 10,
y: 10,
width: 515,
height: 515,
snapX: 34.5,
snapY: 34.5
};
this.specialAreas['bag'] = {
type: 'shuffle',
x: this.bagX - 20,
y: this.bagY - 20,
width: 40,
height: 40
};
this.specialAreas['tileCounter'] = {
type: 'counter',
x: this.bagX,
y: this.bagY,
textX: this.bagX+40,
textY: this.bagY-10
}
this.buttons['reset'] = {
name: 'reset',
text: 'Reset',
action: resetAction,
confirm: true,
assetName: 'button',
x: 680,
y: 20
};
this.addCrossedNumbersTile('blank', 0, 2);
this.addCrossedNumbersTile('0', 1, 5);
this.addCrossedNumbersTile('1', 2, 5);
this.addCrossedNumbersTile('2', 3, 5);
this.addCrossedNumbersTile('3', 4, 5);
this.addCrossedNumbersTile('4', 5, 5);
this.addCrossedNumbersTile('5', 6, 5);
this.addCrossedNumbersTile('6', 7, 5);
this.addCrossedNumbersTile('7', 8, 5);
this.addCrossedNumbersTile('8', 9, 5);
this.addCrossedNumbersTile('9', 10, 5);
this.addCrossedNumbersTile('+', 11, 7);
this.addCrossedNumbersTile('-', 12, 7);
this.addCrossedNumbersTile('x', 13, 5);
this.addCrossedNumbersTile('/', 14, 5);
this.addCrossedNumbersTile('sqrt', 15, 2);
this.addCrossedNumbersTile('sqr', 16, 2);
this.addCrossedNumbersTile('=', 17, 20);
return this;
}
addCrossedNumbersTile( letter, assetNumber, count ) {
for ( var i = 0; i < count; i ++ ) {
var name = 'tile_' + letter + '_' + i;
const piece = {
name: name,
assetName: 'tiles',
assetNumber: 0,
hiddenAssetNumber: 0,
visibleAssetNumber: assetNumber,
x: this.bagX,
y: this.bagY,
zIndex: this.topMost ++
};
this.pieces[name] = piece;
}
}
initCards() {
console.log( `Initialising cards` );
this.deckX = 90;
this.deckY = 480;
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/cards.png',
sizeX : 800,
sizeY : 600
};
this.assets['cards'] = {
type : 'spritesheet',
name : 'cards',
url : 'assets/pieces/cards.png',
sizeX : 79,
sizeY : 123
};
this.assets['button'] = {
type : 'spritesheet',
name : 'button',
url : 'assets/button.png',
sizeX : 104,
sizeY : 31
};
this.assets['dealer'] = {
type : 'image',
name : 'dealer',
url : 'assets/pieces/dealer.png',
sizeX : 46,
sizeY : 46
};
this.assets['leader'] = {
type : 'image',
name : 'leader',
url : 'assets/pieces/leader.png',
sizeX : 46,
sizeY : 46
};
this.assets['dealerHidden'] = {
type : 'image',
name : 'dealerHidden',
url : 'assets/pieces/discWhite46.png',
sizeX : 46,
sizeY : 46
};
this.backgrounds['board'] = {
name : 'board',
assetName : 'board',
x : 400,
y : 300
};
this.specialAreas['hand'] = {
type: 'reveal',
x : 180,
y : 390,
width : 620,
height : 212,
playerNumber : -1
};
this.specialAreas['table'] = {
type: 'reveal',
x : 0,
y : 0,
width : 800,
height : 390,
playerNumber : null
};
this.specialAreas['deck'] = {
type: 'bottom',
assetName: 'cards',
x : this.deckX - 40,
y : this.deckY - 60,
width : 80,
height : 120,
};
this.specialAreas['deckCounter'] = {
type: 'counter',
x: this.deckX,
y: this.deckY,
textX: this.deckX-8,
textY: this.deckY - 80
}
this.buttons['shuffle'] = {
name: 'shuffle',
text: 'Shuffle',
action: shuffleAction,
confirm: true,
assetName: 'button',
x: 90,
y: 560,
};
for ( var suit = 0; suit < 4; suit ++ ) {
for ( var value = 0; value < 13; value ++ ) {
var name = `card_${suit}_${value}`;
this.pieces[name] = {
name : name,
assetName : 'cards',
assetNumber : 52+2,
hiddenAssetNumber : 52+2,
visibleAssetNumber : suit * 13 + value,
x : this.deckX,
y : this.deckY,
zIndex : this.topMost++,
gluedTo : null
}
}
}
this.pieces['dealer'] = {
name: 'dealer',
assetName: 'dealer',
hiddenAssetName: 'dealerHidden',
visibleAssetName: 'dealer',
x: 170,
y: 400,
zIndex: this.topMost++
};
this.pieces['leader'] = {
name: 'leader',
assetName: 'leader',
hiddenAssetName: 'dealerHidden',
visibleAssetName: 'leader',
x: 190,
y: 400,
zIndex: this.topMost++
};
return this;
}
initCardsMini() {
console.log( `Initialising cards (mini)` );
this.deckX = 60;
this.deckY = 530;
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/cardsMini.png',
sizeX : 800,
sizeY : 600
};
this.assets['cards'] = {
type : 'spritesheet',
name : 'cards',
url : 'assets/pieces/miniPlayingCards.png',
sizeX : 41,
sizeY : 61
};
this.assets['button'] = {
type : 'spritesheet',
name : 'button',
url : 'assets/button.png',
sizeX : 104,
sizeY : 31
};
this.assets['dealer'] = {
type : 'image',
name : 'dealer',
url : 'assets/pieces/dealer.png',
sizeX : 46,
sizeY : 46
};
this.assets['dealerHidden'] = {
type : 'image',
name : 'dealerHidden',
url : 'assets/pieces/discWhite46.png',
sizeX : 46,
sizeY : 46
};
this.backgrounds['board'] = {
name : 'board',
assetName : 'board',
x : 400,
y : 300
};
this.specialAreas['hand'] = {
type: 'reveal',
x : 120,
y : 490,
width : 800-120,
height : 600-490,
playerNumber : -1
};
this.specialAreas['table'] = {
type: 'reveal',
x : 100,
y : 100,
width : 600,
height : 290,
playerNumber : null
};
this.specialAreas['deck'] = {
type: 'bottom',
assetName: 'cards',
x : this.deckX - 20,
y : this.deckY - 30,
width : 40,
height : 60,
};
this.specialAreas['deckCounter'] = {
type: 'counter',
x: this.deckX,
y: this.deckY,
textX: this.deckX + 30,
textY: this.deckY
}
this.buttons['shuffle'] = {
name: 'shuffle',
text: 'Shuffle',
action: shuffleAction,
assetName: 'button',
confirm: true,
x: this.deckX,
y: this.deckY + 55
};
for ( var suit = 0; suit < 4; suit ++ ) {
for ( var value = 0; value < 13; value ++ ) {
var name = `card_${suit}_${value}`;
this.pieces[name] = {
name : name,
assetName : 'cards',
assetNumber : 52+2,
hiddenAssetNumber : 52+2,
visibleAssetNumber : suit * 13 + value,
x : this.deckX,
y : this.deckY,
zIndex : this.topMost++,
gluedTo : null
}
}
}
this.pieces['dealer'] = {
name: 'dealer',
assetName: 'dealer',
hiddenAssetName: 'dealerHidden',
visibleAssetName: 'dealer',
x: 170,
y: 400,
zIndex: this.topMost++
};
return this;
}
initSpiteAndMalice() {
console.log( `Initialising spite and malice` );
this.instructions = 'You can read the rules from <a href="https://en.wikipedia.org/wiki/Spite_and_Malice">Wikipedia</a>';
this.deckX = 400;
this.deckY = 455;
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/spiteAndMalice.png',
sizeX : 800,
sizeY : 600
};
this.assets['cards'] = {
type : 'spritesheet',
name : 'cards',
url : 'assets/pieces/miniPlayingCards.png',
sizeX : 41,
sizeY : 61
};
this.assets['button'] = {
type : 'spritesheet',
name : 'button',
url : 'assets/button.png',
sizeX : 104,
sizeY : 31
};
this.assets['dealer'] = {
type : 'image',
name : 'dealer',
url : 'assets/pieces/dealer.png',
sizeX : 46,
sizeY : 46
};
this.assets['dealerHidden'] = {
type : 'image',
name : 'dealerHidden',
url : 'assets/pieces/discWhite46.png',
sizeX : 46,
sizeY : 46
};
this.backgrounds['board'] = {
name : 'board',
assetName : 'board',
x : 400,
y : 300
};
this.specialAreas['hand'] = {
type: 'reveal',
x : 476,
y : 530,
width : 800-476,
height : 70,
playerNumber : -1
};
this.specialAreas['table'] = {
type: 'reveal',
x : 0,
y : 0,
width : 800,
height : 421,
playerNumber : null
};
this.specialAreas['deck'] = {
type: 'bottom',
assetName: 'cards',
x : this.deckX - 20,
y : this.deckY - 30,
width : 40,
height : 60
};
this.specialAreas['deckCounter'] = {
type: 'counter',
x: this.deckX,
y: this.deckY,
textX: this.deckX + 30,
textY: this.deckY
}
for ( var player = 0; player < 4; player ++ ) {
this.specialAreas[`pile${player}`] = {
type: 'bottom',
assetName: 'cards',
x: 97 + player * 204 - 30,
y: 455 - 40,
width : 60,
height : 80
}
this.specialAreas[`topOfPile${player}`] = {
type: 'snap',
x: 97 + player * 204 - 30,
y: 373 - 40,
width : 60,
height : 80,
snapX: 60,
snapY: 80
}
this.specialAreas[`pileCounter${player}`] = {
type: 'counter',
x: 97 + player * 204,
y: 455,
textX: 130 + player * 204,
textY: 451
};
this.specialAreas[`columns${player}`] = {
type: 'snap',
x: 10 + player * 200,
y: 133,
width: 180,
height: 210,
snapX: 45,
snapY: 30
};
}
for ( var deck = 0; deck < 4; deck ++ ) {
for ( var suit = 0; suit < 4; suit ++ ) {
for ( var value = 0; value < 13; value ++ ) {
var name = `card_${deck}_${suit}_${value}`;
this.pieces[name] = {
name : name,
assetName : 'cards',
assetNumber : 52+2,
hiddenAssetNumber : 52+2,
visibleAssetNumber : suit * 13 + value,
x : this.deckX,
y : this.deckY,
zIndex : this.topMost++,
gluedTo : null
}
}
}
}
this.buttons['shuffle'] = {
name: 'shuffle',
text: 'Shuffle',
action: shuffleAction,
confirm: true,
assetName: 'button',
x: this.deckX,
y: this.deckY + 75
};
this.buttons['restock'] = {
name: 'restock',
text: 'Restock',
action: createMoveGroupAction('discard', 'deck', true),
assetName : 'button',
x: 194,
y: 590
};
this.specialAreas['discard'] = {
type: 'insert',
assetName: 'cards',
x: 194 - 30,
y: 544 - 40,
width: 60,
height: 80,
};
this.specialAreas['discardCounter'] = {
type: 'counter',
x: 194,
y: 544,
textX: 194 + 26,
textY: 538
};
for ( var stack = 0; stack < 4; stack ++ ) {
this.specialAreas[`stack${stack}`] = {
type: 'snap',
x: 237 + stack * 108 - 30,
y: 71 - 40,
width: 60,
height: 80,
snapX: 60,
snapY: 80
}
this.buttons[`discard${stack}`] = {
name: `discard${stack}`,
text: 'Discard',
action: createMoveGroupAction(`stack${stack}`, 'discard', true ),
assetName: 'button',
x: 237 + stack * 108,
y: 15
}
}
this.pieces['dealer'] = {
name: 'dealer',
assetName: 'dealer',
hiddenAssetName: 'dealerHidden',
visibleAssetName: 'dealer',
x: 158,
y: 377,
zIndex: this.topMost++
};
return this;
}
initRisk() {
this.deckX = 330;
this.deckY = 520;
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/risk.jpg',
sizeX : 800,
sizeY : 600
};
this.assets['button'] = {
type : 'spritesheet',
name : 'button',
url : 'assets/button.png',
sizeX : 104,
sizeY : 31
};
this.assets['troops1'] = {
type : 'spritesheet',
name : 'troops1',
url : 'assets/pieces/troops1.png',
sizeX : 14,
sizeY : 177/7
};
this.assets['troops5'] = {
type : 'spritesheet',
name : 'troops5',
url : 'assets/pieces/troops5.png',
sizeX : 27,
sizeY : 177/7
};
this.assets['troops10'] = {
type : 'spritesheet',
name : 'troops10',
url : 'assets/pieces/troops10.png',
sizeX : 26,
sizeY : 177/7
};
this.assets['cards'] = {
type: 'spritesheet',
name: 'cards',
url: 'assets/pieces/riskCards.png',
sizeX : 94,
sizeY: 150
};
this.assets['dice'] = {
type: 'spritesheet',
name: 'dice',
url: 'assets/pieces/dice.png',
sizeX : 42,
sizeY: 38
};
this.assets['diceBlue'] = {
type: 'spritesheet',
name: 'diceBlue',
url: 'assets/pieces/diceBlue.png',
sizeX : 42,
sizeY: 38
};
this.backgrounds['board'] = {
name : 'board',
assetName : 'board',
x : 400,
y : 300
};
this.specialAreas['hand'] = {
type: 'reveal',
x : 0,
y : 100,
width : 80,
height : 500,
playerNumber : -1
};
this.specialAreas['show'] = {
type: 'reveal',
x : 400,
y : 0,
width : 500,
height : 600,
playerNumber : null
};
this.specialAreas['deck'] = {
type: 'bottom',
assetName: 'cards',
x : this.deckX-40,
y : this.deckY-60,
width : 80,
height : 120
};
this.specialAreas['deckCounter'] = {
type: 'counter',
color: '#000',
x: this.deckX,
y: this.deckY,
textX: this.deckX-8,
textY: this.deckY - 90
}
this.buttons['reset'] = {
name: 'reset',
text: 'Reset',
action: resetAction,
confirm: true,
assetName: 'button',
x: 720,
y: 570
};
this.topMost = 0;
// Cards
for (var c = 0; c < 9*5-1; c ++) {
var name = `card_${c}`;
this.pieces[name] = {
name: name,
assetName: 'cards',
assetNumber: c,
hiddenAssetNumber : 9*5-1,
visibleAssetNumber : c,
x : this.deckX,
y : this.deckY,
zIndex: this.topMost++
};
}
this.topMost = 200;
// Troop reserves
for ( var p = 0; p < 6; p ++ ) {
for ( var i = 0; i < 40; i ++ ) {
var name = `troops1_${p}_${i}`;
this.pieces[name]={
name: name,
assetName: 'troops1',
assetNumber: p,
x: 548,
y: 420 + p * 30,
zIndex: this.topMost++
};
}
for ( var i = 0; i < 10; i ++ ) {
var name = `troops5_${p}_${i}`;
this.pieces[name] = {
name: name,
assetName: 'troops5',
assetNumber: p,
x: 576,
y: 420 + p * 30,
zIndex: this.topMost++
};
}
for ( var i = 0; i < 10; i ++ ) {
var name = `troops10_${p}_${i}`;
this.pieces[name]={
name: name,
assetName: 'troops10',
assetNumber: p,
x: 610,
y: 420 + p * 30,
zIndex: this.topMost++
};
}
}
this.pieces['dice_0'] = {
name : 'dice_0',
assetName : 'dice',
assetNumber : 0,
x : 400,
y : 547,
zIndex: this.topMost++
};
this.pieces['dice_1'] = {
name : 'dice_1',
assetName : 'dice',
assetNumber : 0,
x : 450,
y : 547,
zIndex: this.topMost++
};
this.pieces['dice_2'] = {
name : 'dice_2',
assetName : 'dice',
assetNumber : 0,
x : 500,
y : 547,
zIndex: this.topMost++
};
this.buttons['attack'] = {
name: 'attack',
text: 'Throw',
action: throwAction,
assetName: 'button',
x: 450,
y: 587
};
this.pieces['dice_3'] = {
name : 'dice_3',
assetName : 'diceBlue',
assetNumber : 0,
x : 150,
y : 547,
zIndex: this.topMost++
};
this.pieces['dice_4'] = {
name : 'dice_4',
assetName : 'diceBlue',
assetNumber : 0,
x : 200,
y : 547,
zIndex: this.topMost++
};
this.buttons['defend'] = {
name: 'defend',
text: 'Throw',
action: throwBlueAction,
assetName: 'button',
x: 175,
y: 587
};
this.reset();
return this;
}
initChineseCheckers() {
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/chineseCheckers.png',
sizeX : 800,
sizeY : 600
};
this.assets['button'] = {
type : 'spritesheet',
name : 'button',
url : 'assets/button.png',
sizeX : 105,
sizeY : 31
};
this.assets['spheres'] = {
type : 'spritesheet',
name : 'spheres',
url : 'assets/pieces/spheres.png',
sizeX : 37.7,
sizeY : 38
};
this.backgrounds['board'] = {
name : 'board',
assetName : 'board',
x : 400,
y : 300
};
this.buttons['reset'] = {
name: 'reset',
text: 'Reset',
action: resetAction,
confirm: true,
assetName: 'button',
x: 720,
y: 570
};
this.specialAreas['grid'] = {
type: 'snap',
x: 12,
y: 16,
width: 800,
height: 600,
snapX: 21,
snapY: 21
};
for ( var x = 0; x < 5; x ++ ) {
for (var y = 0; y < x; y ++ ) {
this.addCCPiece( x*2 - 18, y*2 - x +1, 0 );
this.addCCPiece( -x*2, y*2 - x -8, 1 );
this.addCCPiece( x*2, y*2 - x -8, 2 );
this.addCCPiece( 18 - x*2, y*2 - x +1, 3 );
this.addCCPiece( x*2, y*2 - x + 10, 4 );
this.addCCPiece( -x*2, y*2 - x + 10, 5 );
}
}
for ( var i = 0; i < 5; i ++ ) {
this.addCCPiece( -15, 4, 0 );
this.addCCPiece( -11, -10, 1 );
this.addCCPiece( 4, -13, 2 );
this.addCCPiece( 15, -4, 3 );
this.addCCPiece( 11, 10, 4 );
this.addCCPiece( -4, 13, 5 );
}
return this;
}
addCCPiece( x, y, assetNumber ) {
var name = `piece${this.topMost}`;
this.pieces[name] = {
name: name,
assetName: 'spheres',
assetNumber: assetNumber,
x: 400 + x * 21,
y: 300 + y * 21,
zIndex: this.topMost++
};
}
initConnectFour() {
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/connectFour.png',
sizeX : 800,
sizeY : 600
};
this.assets['button'] = {
type : 'spritesheet',
name : 'button',
url : 'assets/button.png',
sizeX : 104,
sizeY : 31
};
this.assets['blue'] = {
type: 'image',
name: 'blue',
url : 'assets/pieces/discBlue72.png',
sizeX: 72,
sizeY: 72
}
this.assets['red'] = {
type: 'image',
name: 'red',
url : 'assets/pieces/discRed72.png',
sizeX: 72,
sizeY: 72
}
this.backgrounds['board'] = {
name : 'board',
assetName : 'board',
x : 400,
y : 300
};
this.buttons['reset'] = {
name: 'reset',
text: 'Reset',
action: resetAction,
confirm: true,
assetName : 'button',
x: 400,
y: 30
};
this.specialAreas['grid'] = {
type: 'snap',
x: 120,
y: 70,
width: 80*7,
height: 80*6,
snapX: 80,
snapY: 80
};
for ( var i = 0; i < 21; i ++ ) {
name = `red${i}`;
this.pieces[name] = {
name: name,
assetName: 'red',
x: 50,
y: 500
};
var name = `blue${i}`;
this.pieces[name] = {
name: name,
assetName: 'blue',
x: 800-50,
y: 500
};
}
return this;
}
initSuperSeven() {
this.instructions = 'Read the <a href="instructions/superSeven.html">Rules of Super7</a>.';
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/superSeven.png',
sizeX : 800,
sizeY : 600
};
this.assets['ballBigBlue'] = {
type : 'image',
name : 'ballBigBlue',
url : 'assets/pieces/ballBigBlue.png',
sizeX : 143,
sizeY : 143
};
this.assets['ballBigRed'] = {
type : 'image',
name : 'ballBigRed',
url : 'assets/pieces/ballBigRed.png',
sizeX : 143,
sizeY : 143
};
this.assets['ballBlue'] = {
type : 'image',
name : 'ballBlue',
url : 'assets/pieces/ballBlue.png',
sizeX : 51,
sizeY : 51
};
this.assets['ballRed'] = {
type : 'image',
name : 'ballRed',
url : 'assets/pieces/ballRed.png',
sizeX : 51,
sizeY : 51
};
this.assets['button'] = {
type : 'spritesheet',
name : 'button',
url : 'assets/button.png',
sizeX : 104,
sizeY : 31
};
this.assets['dice'] = {
type: 'spritesheet',
name: 'dice',
url: 'assets/pieces/dice.png',
sizeX : 42,
sizeY: 38
};
this.specialAreas['grid'] = {
type: 'snap',
x: 30,
y: 30,
width: 540,
height: 540,
snapX: 60,
snapY: 60
};
this.backgrounds['board'] = {
name : 'board',
assetName : 'board',
x : 400,
y : 300
};
for ( var i = 0; i < 50; i ++ ) {
this.pieces[`blue_${i}`] = {
name: `blue_${i}`,
assetName: 'ballBlue',
x: 730,
y: 332,
zIndex: this.topMost++
};
this.pieces[`red_${i}`] = {
name: `red_${i}`,
assetName: 'ballRed',
x: 630,
y: 332,
zIndex: this.topMost++
};
}
for ( var i = 0; i < 5; i ++ ) {
this.pieces[`bigBlue_${i}`] = {
name: `bigBlue_${i}`,
assetName: 'ballBigBlue',
x: 658,
y: 450,
zIndex: this.topMost++
};
this.pieces[`bigRed_${i}`] = {
name: `bigRed_${i}`,
assetName: 'ballBigRed',
x: 715,
y: 450,
zIndex: this.topMost++
};
}
this.pieces['dice_0'] = {
name: 'dice_0',
assetName: 'dice',
assetNumber: 0,
x: 652,
y: 246,
zIndex: this.topMost++
};
this.pieces['dice_1'] = {
name: 'dice_1',
assetName: 'dice',
assetNumber: 0,
x: 705,
y: 246,
zIndex: this.topMost++
};
this.buttons['throw'] = {
name: 'throw',
text: 'Throw',
action: throwAction,
assetName: 'button',
x: 680,
y: 285
};
this.buttons['reset'] = {
name: 'reset',
text: 'Reset',
action: resetAction,
confirm: true,
assetName: 'button',
x: 680,
y: 570
};
return this;
}
initHex() {
this.instructions = 'Read the <a href="https://en.wikipedia.org/wiki/Hex_(board_game)">Rules of Hex</a>.';
this.assets['board'] = {
type : 'image',
name : 'board',
url : 'assets/backgrounds/hex.png',
sizeX : 800,
sizeY : 600
};
this.assets['ballBlue'] = {
type : 'image',
name : 'ballBlue',
url : 'assets/pieces/ballBlue.png',
sizeX : 51,
sizeY : 51
};
this.assets['ballRed'] = {
type : 'image',
name : 'ballRed',
url : 'assets/pieces/ballRed.png',
sizeX : 51,
sizeY : 51
};
this.assets['button'] = {
type : 'spritesheet',
name : 'button',
url : 'assets/button.png',
sizeX : 104,
sizeY : 31
};
this.specialAreas['grid'] = {
type: 'snap',
x: 19,
y: 66,
width: 800,
height: 500,
snapX: 24.62,
snapY: 42.95
};
this.backgrounds['board'] = {
name : 'board',
assetName : 'board',
x : 400,
y : 300
};
for ( var i = 0; i < 30; i ++ ) {
this.pieces[`blue1_${i}`] = {
name: `blue1_${i}`,
assetName: 'ballBlue',
x: 127,
y: 346,
zIndex: this.topMost++
};
this.pieces[`blue2_${i}`] = {
name: `blue2_${i}`,
assetName: 'ballBlue',
x: 675,
y: 259,
zIndex: this.topMost++
};
this.pieces[`red1_${i}`] = {
name: `red1_${i}`,
assetName: 'ballRed',
x: 303,
y: 43,
zIndex: this.topMost++
};
this.pieces[`red2_${i}`] = {
name: `red2_${i}`,
assetName: 'ballRed',
x: 498,
y: 561,
zIndex: this.topMost++
};
}
this.buttons['reset'] = {
name: 'reset',
text: 'Reset',
action: resetAction,
confirm: true,
assetName: 'button',
x: 720,
y: 567
};
return this;
}
initCharades() {
this.instructions = 'Use video conferencing!';
this.assets['button'] = {
type : 'spritesheet',
name : 'button',
url : 'assets/button.png',
sizeX : 104,
sizeY : 31
};
this.buttons['random'] = {
name: 'random',
text: 'Random',
action: takeRandomCharadeAction,
confirm: false,
assetName: 'button',
x: 400,
y: 300
};
this.buttons['film'] = {
name: 'film',
text: 'Film',
action: takeFilmCharadeAction,
confirm: false,
assetName: 'button',
x: 100,
y: 400
};
this.buttons['song'] = {
name: 'song',
text: 'Song',
action: takeSongCharadeAction,
confirm: false,
assetName: 'button',
x: 300,
y: 400
};
this.buttons['tv'] = {
name: 'tv',
text: 'TV Show',
action: takeTVCharadeAction,
confirm: false,
assetName: 'button',
x: 500,
y: 400
};
this.buttons['book'] = {
name: 'book',
text: 'Book',
action: takeBookCharadeAction,
confirm: false,
assetName: 'button',
x: 700,
y: 400
};
return this;
}
reset() {
var prototype = this.prototype;
if (prototype == null) return;
for (var id in this.pieces) {
var piece = this.pieces[id];
var prototypePiece = prototype.pieces[id];
piece.ownedByPlayerNumber = null;
piece.gluedTo = null;
piece.draggingBy = null;
if (prototypePiece) {
if (prototypePiece.isPermanent != true) {
piece.x = prototypePiece.x;
piece.y = prototypePiece.y;
}
if (prototypePiece.assetNumber !== undefined) {
piece.assetNumber = prototypePiece.assetNumber;
}
}
}
this.shuffle();
return this.pieces;
}
shuffle() {
var prototype = this.prototype;
if (prototype == null) return;
var pieces = {};
var zIndices = new Array();
// Find the pieces that need to be shuffled, and store all the old zIndicies in
// the zIndices array.
// The pieces are also moved back to their original positions.
for ( var id in this.pieces ) {
var piece = this.pieces[id];
var prototypePiece = prototype.pieces[id];
if (piece.assetName == 'cards' || piece.assetName == 'letters' || piece.assetName == 'tiles' ) {
pieces[id] = piece;
piece.ownedByPlayerNumber = null;
pieces[piece.name] = piece;
zIndices[zIndices.length] = piece.zIndex;
piece.x = prototypePiece.x;
piece.y = prototypePiece.y;
}
}
// The second pass changes the zIndex of each of the pieces found in the first pass.
for (id in pieces) {
var piece = pieces[id];
var rnd = getRandomInt(zIndices.length);
piece.zIndex = zIndices[rnd];
if (rnd < zIndices.length -1) {
zIndices[rnd] = zIndices.pop();
} else {
zIndices.pop();
}
}
return pieces;
}
toShuffleArea( area, piece ) {
piece.x = area.x + area.width/2;
piece.y = area.y + area.height/2;
return this.shuffleArea( area );
}
shuffleArea( area ) {
const pieces = {};
const x = area.x + area.width/2;
const y = area.y + area.height/2;
var count = 0;
for ( var id in this.pieces ) {
var p = this.pieces[id];
if (p.x == x && p.y == y) count ++
}
for ( var id in this.pieces ) {
var p = this.pieces[id];
if (p.x == x && p.y == y) {
pieces[p.name] = p;
p.zIndex = getRandomInt( count );
}
}
return pieces;
}
toBottomArea( area, piece ) {
piece.x = area.x + area.width/2;
piece.y = area.y + area.height/2;
var pieces = {};
piece.zIndex = -1;
for ( var id in this.pieces ) {
var p = this.pieces[id];
if (p.x == piece.x && p.y == piece.y) {
pieces[p.name] = p;
p.zIndex ++;
}
}
return pieces;
}
toInsertArea( area, piece ) {
const x = area.x + area.width/2;
const y = area.y + area.height/2;
var pieces = {};
for ( var id in this.pieces ) {
var p = this.pieces[id];
if (p.x == x && p.y == y) {
pieces[p.name] = p;
}
}
// Find a random piece in the area
if (Object.keys(pieces).length > 0) {
const rnd = getRandomInt( Object.keys(pieces).length );
const otherPiece = pieces[Object.keys(pieces)[rnd]]
if (otherPiece) {
// Swap the z-index with a random piece from the pile.
const temp = piece.zIndex;
piece.zIndex = otherPiece.zIndex;
otherPiece.zIndex = temp;
}
}
piece.x = x;
piece.y = y;
pieces[piece.name] = piece;
return pieces;
}
throwDice(assetName) {
if (!assetName) assetName = 'dice';
var dice = {};
for ( var id in this.pieces ) {
var piece = this.pieces[id];
if (piece.assetName == assetName) {
piece.assetNumber = getRandomInt(6);
dice[piece.name] = piece;
}
}
return dice;
}
moveGroup(fromAreaName, toAreaName, shuffle) {
const fromArea = this.specialAreas[fromAreaName];
const toArea = this.specialAreas[toAreaName];
const fromX = fromArea.x + fromArea.width/2;
const fromY = fromArea.y + fromArea.height/2;
const toX = toArea.x + toArea.width/2;
const toY = toArea.y + toArea.height/2;
const pieces = {};
for ( var id in this.pieces ) {
var piece = this.pieces[id];
if (piece.x == fromX && piece.y == fromY) {
pieces[piece.name] = piece;
piece.x = toX;
piece.y = toY;
}
}
if ( shuffle ) {
return this.shuffleArea( toArea );
}
return pieces;
}
isPlayerNumberUsed( playerNumber ) {
for ( var id in this.players ) {
if ( this.players[id].playerNumber == playerNumber ) {
return true;
}
}
return false;
}
findPlayerNumber() {
for ( var i=0; i < 20; i ++ ) {
if (this.isPlayerNumberUsed(i)) {
continue;
}
return i;
}
return 0;
}
findGlueTo( piece, x, y ) {
var gluesTo = this.glue[ piece.assetName ];
if (gluesTo) {
var minDist2 = 100000;
var result = null;
for (const pName in this.pieces) {
const p = this.pieces[pName];
if (p !== piece && gluesTo.includes(p.assetName)) {
const d2 = (x - p.x) * (x - p.x) + (y - p.y) * (y - p.y);
if ( d2 < minDist2 && d2 < this.minSnap2 ) {
minDist2 = d2;
result = p;
}
}
}
}
return result;
}
findRevealArea( x, y ) {
for ( var id in this.specialAreas ) {
const ra = this.specialAreas[id];
if ( ra.type == 'reveal' && x < ra.x + ra.width && x >= ra.x && y < ra.y + ra.height && y >= ra.y ) {
return ra
}
}
return null;
}
findShuffleArea( x, y ) {
for ( var id in this.specialAreas ) {
const ra = this.specialAreas[id];
if ( ra.type == 'shuffle' && x < ra.x + ra.width && x >= ra.x && y < ra.y + ra.height && y >= ra.y ) {
return ra
}
}
return null;
}
findBottomArea( piece ) {
for ( var id in this.specialAreas ) {
const ra = this.specialAreas[id];
if ( ra.type == 'bottom'
&& ra.assetName == piece.assetName
&& piece.x < ra.x + ra.width && piece.x >= ra.x
&& piece.y < ra.y + ra.height && piece.y >= ra.y ) {
return ra
}
}
return null;
}
findInsertArea( piece ) {
for ( var id in this.specialAreas ) {
const ra = this.specialAreas[id];
if ( ra.type == 'insert'
&& ra.assetName == piece.assetName
&& piece.x < ra.x + ra.width && piece.x >= ra.x
&& piece.y < ra.y + ra.height && piece.y >= ra.y ) {
return ra
}
}
return null;
}
createNameTags() {
const newPieces = {};
const changedPieces = {};
for ( var id in this.players ) {
var player = this.players[id];
if (!player.isSpectator) {
var existing = this.pieces[`nameTag${player.playerNumber}`];
if (existing) {
changedPieces[existing.name] = existing;
existing.text = player.name;
} else {
var piece = {
name: `nameTag${player.playerNumber}`,
assetName: 'nameTag',
text: player.name,
textStyle: { fontFamily: 'Arial', fontSize: '16px' },
x: 100,
y: 100 + player.playerNumber * 50,
zIndex: this.topMost++
};
this.pieces[piece.name] = piece;
newPieces[piece.name] = piece;
}
}
}
io.to(this.id).emit( 'addPieces', newPieces );
io.to(this.id).emit( 'movePieces', changedPieces );
}
createXmasDecorations() {
const newAssets = {};
const newPieces = {};
const x = 200;
const y = 200;
newAssets['baubles1'] = {
type : 'image',
name : 'baubles1',
url : 'assets/pieces/xmas/baubles1.png',
sizeX : 200,
sizeY : 78
};
newPieces[`xmas${this.topMost}`] = {
name: `xmas${this.topMost}`,
assetName: 'baubles1',
x: x,
y: y,
zIndex: this.topMost++
};
newAssets['holly1'] = {
type : 'image',
name : 'holly1',
url : 'assets/pieces/xmas/holly1.png',
sizeX : 150,
sizeY : 100
};
newPieces[`xmas${this.topMost}`] = {
name: `xmas${this.topMost}`,
assetName: 'holly1',
x: x,
y: y,
zIndex: this.topMost++
};
newAssets['merry1'] = {
type : 'image',
name : 'merry1',
url : 'assets/pieces/xmas/merry1.png',
sizeX : 200,
sizeY : 130
};
newPieces[`xmas${this.topMost}`] = {
name: `xmas${this.topMost}`,
assetName: 'merry1',
x: x,
y: y,
zIndex: this.topMost++
};
newAssets['reindeer1'] = {
type : 'image',
name : 'reindeer1',
url : 'assets/pieces/xmas/reindeer1.png',
sizeX : 100,
sizeY : 143
};
newPieces[`xmas${this.topMost}`] = {
name: `xmas${this.topMost}`,
assetName: 'reindeer1',
x: x,
y: y,
zIndex: this.topMost++
};
newAssets['reindeer2'] = {
type : 'image',
name : 'reindeer2',
url : 'assets/pieces/xmas/reindeer2.png',
sizeX : 130,
sizeY : 138
};
newPieces[`xmas${this.topMost}`] = {
name: `xmas${this.topMost}`,
assetName: 'reindeer2',
x: x,
y: y,
zIndex: this.topMost++
};
newAssets['santa1'] = {
type : 'image',
name : 'santa1',
url : 'assets/pieces/xmas/santa1.png',
sizeX : 92,
sizeY : 110
};
newPieces[`xmas${this.topMost}`] = {
name: `xmas${this.topMost}`,
assetName: 'santa1',
x: x,
y: y,
zIndex: this.topMost++
};
newAssets['santa2'] = {
type : 'image',
name : 'santa2',
url : 'assets/pieces/xmas/santa2.png',
sizeX : 150,
sizeY : 134
};
newPieces[`xmas${this.topMost}`] = {
name: `xmas${this.topMost}`,
assetName: 'santa2',
x: x,
y: y,
zIndex: this.topMost++
};
newAssets['snowman1'] = {
type : 'image',
name : 'snowman1',
url : 'assets/pieces/xmas/snowman1.png',
sizeX : 90,
sizeY : 125
};
newPieces[`xmas${this.topMost}`] = {
name: `xmas${this.topMost}`,
assetName: 'snowman1',
x: x,
y: y,
zIndex: this.topMost++
};
newAssets['tree1'] = {
type : 'image',
name : 'tree1',
url : 'assets/pieces/xmas/tree1.png',
sizeX : 150,
sizeY : 231
};
newPieces[`xmas${this.topMost}`] = {
name: `xmas${this.topMost}`,
assetName: 'tree1',
x: x,
y: y,
zIndex: this.topMost++
};
for (var name in newPieces) {
this.pieces[name] = newPieces[name];
}
for (var name in newAssets) {
this.assets[name] = newAssets[name];
}
io.to(this.id).emit( 'assets', newAssets );
io.to(this.id).emit( 'addPieces', newPieces );
io.to(this.id).emit( 'movePieces', this.pieces );
}
createBirthdayDecorations() {
const newAssets = {};
const newPieces = {};
const x = 200;
const y = 200;
newAssets['balloons'] = {
type : 'image',
name : 'balloons',
url : 'assets/pieces/birthday/balloons.png',
sizeX : 194,
sizeY : 243
};
newPieces[`birthday${this.topMost}`] = {
name: `birthday${this.topMost}`,
assetName: 'balloons',
x: x,
y: y,
zIndex: this.topMost++
};
newAssets['banner'] = {
type : 'image',
name : 'banner',
url : 'assets/pieces/birthday/banner.png',
sizeX : 400,
sizeY : 253
};
newPieces[`birthday${this.topMost}`] = {
name: `birthday${this.topMost}`,
assetName: 'banner',
x: x,
y: y,
zIndex: this.topMost++
};
newAssets['cake'] = {
type : 'image',
name : 'cake',
url : 'assets/pieces/birthday/cake.png',
sizeX : 250,
sizeY : 238
};
newPieces[`birthday${this.topMost}`] = {
name: `birthday${this.topMost}`,
assetName: 'cake',
x: x,
y: y,
zIndex: this.topMost++
};
newAssets['popper'] = {
type : 'image',
name : 'popper',
url : 'assets/pieces/birthday/popper.png',
sizeX : 208,
sizeY : 189
};
newPieces[`birthday${this.topMost}`] = {
name: `birthday${this.topMost}`,
assetName: 'popper',
x: x,
y: y,
zIndex: this.topMost++
};
newAssets['presents'] = {
type : 'image',
name : 'presents',
url : 'assets/pieces/birthday/presents.png',
sizeX : 250,
sizeY : 213
};
newPieces[`birthday${this.topMost}`] = {
name: `birthday${this.topMost}`,
assetName: 'presents',
x: x,
y: y,
zIndex: this.topMost++
};
for (var name in newPieces) {
this.pieces[name] = newPieces[name];
}
for (var name in newAssets) {
this.assets[name] = newAssets[name];
}
io.to(this.id).emit( 'assets', newAssets );
io.to(this.id).emit( 'addPieces', newPieces );
io.to(this.id).emit( 'movePieces', this.pieces );
console.log(`Adding assets ${Object.keys(newAssets).length}`);
console.log(`addPieces ${Object.keys(newPieces).length}`);
}
};
console.log( 'Creating GameTypes' );
addGameTypeVariations( 'cards', 'Cards', [
new Game( 'cards', 'Large Cards' ).initCards(),
new Game( 'cardsMini', 'Small Cards' ).initCardsMini(),
new Game( 'spiteAndMalice', 'Spite and Malice' ).initSpiteAndMalice()
]);
addGameTypeVariations( 'twoPlayerCards', 'Two Player Cards', [
new Game( 'twoPlayerCards', 'Large Cards' ).initTwoPlayerCards(),
new Game( 'twoPlayerCardsMini', 'Small Cards' ).initTwoPlayerCardsMini() ,
new Game( 'cribbage', 'Cribbage' ).initCribbage()
]);
addGameType( new Game( 'draughts', 'Draughts' ).initDraughts() );
addGameType( new Game( 'chess', 'Chess' ).initChess() );
addGameTypeVariations( 'crossedWords', 'Crossed Words', [
new Game( 'crossedWords', 'Crossed Words' ).initCrossedWords(),
new Game( 'crossedNumbers', 'Crossed Numbers' ).initCrossedNumbers(),
new Game( 'speedAnagrams', 'Speed Anagrams' ).initSpeedAnagrams()
]);
addGameType( new Game( 'backgammon', 'Backgammon' ).initBackgammon() );
addGameType( new Game( 'risk', 'Risk' ).initRisk() );
addGameType( new Game( 'chineseCheckers', 'Chinese Checkers' ).initChineseCheckers() );
addGameType( new Game( 'connectFour', 'Connect Four' ).initConnectFour() );
addGameType( new Game( 'superSeven', 'Super 7' ).initSuperSeven() );
addGameType( new Game( 'hex', 'Hex' ).initHex() );
addGameType( new Game( 'charades', 'Charades' ).initCharades() );
console.log( 'Created GameTypes' );
function shallowCopyObjectMap( sourceMap, destinationMap ) {
for (var key in sourceMap) {
var item = sourceMap[key];
var newItem = {};
for (var id in item) {
newItem[id] = item[id];
}
destinationMap[key] = newItem;
}
}
function findGame( socket ) {
if (socket.gameId) {
return games[socket.gameId];
}
const rooms = Object.keys(socket.rooms);
const gameId = rooms[1];
return games[gameId];
}
function debugGameNotFound( socket, name ) {
console.log( `User id ${socket.id}. Failed to find game (${name}).` );
console.log( `Finding game from socket id : ${socket.id}` );
console.log( `Room count : ${Object.keys(socket.rooms).length}`);
console.log( `2nd room id : ${Object.keys(socket.rooms)[1]}`);
console.log( `Game ? ${games[Object.keys(socket.rooms)[1]]}`);
}
io.on('connection', function (socket) {
socket.on('listGameTypes', function(data) {
var gameTypesInfo = {};
for ( var id in gameTypes ) {
var gameType = gameTypes[id];
gameTypesInfo[id] = {
id: id,
name: gameType.name,
gameCount: Object.keys(gameType.instances).length
};
}
socket.emit('gameTypes', gameTypesInfo );
});
socket.on('lobby', function(data) {
const lobbyData = createLobbyData( data.gameTypeId );
socket.join( data.gameTypeId );
socket.emit('lobby', lobbyData );
});
socket.on('newGame', function(data) {
const gameTypeId = data.gameTypeId;
const variationId = data.variationId;
const gameType = gameTypes[gameTypeId];
if (gameType) {
var game = gameType.newGame(variationId, data.name);
if ( game ) {
game.maxPlayers = data.playerCount;
socket.emit( 'startGame', {gameId: game.id} );
io.to(gameType.id).emit('lobby', createLobbyData(gameTypeId) );
}
}
});
socket.on( 'game' , function(data) {
const gameId = data.gameId;
const game = games[ gameId ];
if (!game) return;
socket.gameId = gameId;
var playerNumber = game.findPlayerNumber();
var playerName = '';
if ( playerNumber >= game.maxPlayers ) {
playerName = `Spectator${playerNumber-game.maxPlayers+1}`;
} else {
playerName = `Player${playerNumber + 1}`;
}
const player = {
id: socket.id,
name: playerName,
playerNumber: playerNumber,
isSpectator: playerNumber >= game.maxPlayers
};
if ( game.isPublic || !player.isSpectator ) {
socket.join( gameId );
game.players[socket.id] = player;
}
socket.emit('gameInfo', {
name: game.name,
instructions: game.instructions,
maxPlayers: game.maxPlayers,
playerCount: Object.keys(game.players).length
});
socket.emit('playerInfo', game.players[socket.id] );
socket.emit('assets', game.assets );
socket.emit('backgrounds', game.backgrounds);
socket.emit('pieces', game.pieces);
socket.emit('buttons', game.buttons);
socket.emit('specialAreas', game.specialAreas);
io.to(game.id).emit('connected', player);
});
socket.on('disconnect', function () {
const game = findGame( socket );
if (!game) {
//console.log(`User ${socket.id} disconnected from game ???`);
//debugGameNotFound(socket, 'disconnect');
return;
}
const player = game.players[socket.id];
if (! player) return;
for ( var id in game.pieces ) {
var piece = game.pieces[id];
if (piece.draggingBy == player.playerNumber) {
piece.draggingBy = null;
io.to(game.id).emit('movePiece', piece);
}
}
io.to(game.id).emit('disconnected', player);
delete game.players[socket.id];
});
socket.on('draggingPiece', function (dragInfo) {
const game = findGame( socket );
if (! game) return;
game.active();
const player = game.players[socket.id];
if (! player) return;
const playerNumber = player.playerNumber;
const piece = game.pieces[ dragInfo.name ];
if (!piece) return;
if (piece.draggingBy != null && piece.draggingBy != player.playerNumber) {
// Tell the client the REAL position of the piece again!
socket.emit( 'movePiece', piece );
return;
}
if ( piece.zIndex < game.topMost-1 ) {
piece.zIndex = game.topMost ++;
}
if (dragInfo.end==true) {
piece.draggingBy = null;
} else {
piece.draggingBy = playerNumber;
}
var revealArea = game.findRevealArea( dragInfo.x, dragInfo.y );
if (revealArea && revealArea.playerNumber == -1) {
// Reveal it to the player dragging, but hide from all other players.
piece.ownedByPlayerNumber = playerNumber;
} else {
piece.ownedByPlayerNumber = null;
}
var glueTo = game.findGlueTo( piece, dragInfo.x, dragInfo.y );
if (glueTo) {
piece.x = glueTo.x;
piece.y = glueTo.y;
if ( dragInfo.end ) {
piece.gluedTo = glueTo.name;
}
} else {
piece.x = dragInfo.x;
piece.y = dragInfo.y;
if ( dragInfo.end ) {
piece.gluedTo = null;
}
}
var shuffledPieces = null;
if ( dragInfo.end == true ) {
var area = game.findShuffleArea( dragInfo.x, dragInfo.y );
if (area) {
shuffledPieces = game.toShuffleArea( area, piece );
} else {
area = game.findBottomArea( piece );
if (area) {
shuffledPieces = game.toBottomArea( area, piece );
} else {
area = game.findInsertArea( piece );
if (area) {
shuffledPieces = game.toInsertArea( area, piece );
}
}
}
}
if ( shuffledPieces ) {
io.to(game.id).emit('movePieces', shuffledPieces);
} else {
io.to(game.id).emit('movePiece', piece);
for ( var pName in game.pieces ) {
const p = game.pieces[pName];
if ( p.gluedTo == piece.name ) {
p.draggingBy = piece.draggingBy;
p.x = piece.x;
p.y = piece.y;
if ( p.zIndex < game.topMost-1 ) {
p.zIndex = game.topMost ++;
}
io.to(game.id).emit('movePiece', p);
}
}
}
});
socket.on('click', function (clickInfo) {
const game = findGame(socket);
if (!game) {
console.log(`Can't find the game for click message ${clickInfo.name}`);
debugGameNotFound(socket, 'click');
return;
}
game.active();
const button = game.buttons[ clickInfo.name ];
if (button) {
button.action(game,socket);
} else {
console.log( `click button ${clickInfo.name} not found` );
}
});
socket.on('highlightPointer', function (pointerInfo) {
// Note, not safe from script kiddies as pointerInfo is not being checked in any way.
// In particular, we are trusting their 'isSpectator' flag.
const game = findGame(socket);
if (!game) return;
io.to(game.id).emit( 'highlightPointer', pointerInfo );
});
socket.on('chat', function (chatInfo) {
const game = findGame( socket );
if (!game) return;
game.active();
const player = game.players[socket.id];
if (!player) return;
const message = chatInfo.message;
if (message == 'private' && !player.isSpectator) {
game.isPublic = false;
const gameSockets = io.sockets.adapter.rooms[game.id].sockets;
io.to(game.id).emit( 'chat', {
playerNumber: player.playerNumber,
isSpectator: player.isSpectator,
name: player.name,
message: 'The game is now private.'
});
for ( var socketId in gameSockets ) {
var clientSocket = io.sockets.connected[socketId];
var otherPlayer = game.players[clientSocket.id];
if (otherPlayer.isSpectator) {
// Removing the spectator from the game room, so that they see no
// more updates, such as piece movements and chat.
clientSocket.leave( game.id );
delete( game.players[socketId] );
}
}
}
if (message == 'public' && !player.isSpectator) {
game.isPublic = true;
}
if (player.name != chatInfo.name) {
io.to(game.id).emit('changeName', {
playerNumber: player.playerNumber,
isSpectator: player.isSpectator,
from: player.name,
to: chatInfo.name
});
player.name = chatInfo.name;
}
var reply = {
playerNumber: player.playerNumber,
isSpectator: player.isSpectator,
name : player.name,
message : message
};
io.to(game.id).emit( 'chat', reply );
});
socket.on('changeName', function (info) {
const game = findGame( socket );
if (!game) return;
game.active();
const player = game.players[socket.id];
if (player.name != info.name) {
io.to(game.id).emit('changeName', {
playerNumber: player.playerNumber,
isSpectator: player.isSpectator,
from: player.name,
to: info.name
});
player.name = info.name;
}
});
});
function createLobbyData(gameTypeId) {
const gameType = gameTypes[gameTypeId];
if (!gameType) return { id: gameTypeId, name: 'Unknown', variations: [] };
const variations = [];
for ( var variationIndex = 0; variationIndex < gameType.variations.length; variationIndex ++ ) {
var variation = gameType.variations[variationIndex];
variations[variations.length] = { variationId: variation.id, name: variation.name }
}
const lobbyData = {
id: gameTypeId,
name: gameType.name,
variations: variations
};
lobbyData.gameList = gameType.listGames(false);
return lobbyData;
}
app.use(express.static(__dirname + '/public'));
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
server.listen(8081, function () {
console.log(`Listening on ${server.address().port}`);
});
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}