Initial commit
This commit is contained in:
42
uno/client/Card.js
Normal file
42
uno/client/Card.js
Normal file
@@ -0,0 +1,42 @@
|
||||
export class Card {
|
||||
constructor(color, value, turnedUp) {
|
||||
this.color = color;
|
||||
this.value = value;
|
||||
this.turnedUp = turnedUp;
|
||||
}
|
||||
|
||||
allows(card) {
|
||||
return this.color === card.color || this.value === card.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the card
|
||||
* @param {Renderer} renderer
|
||||
*/
|
||||
draw(renderer, x = 0, y = 0) {
|
||||
const cardBody = renderer.begin().roundedRect(x, y, 100, 150, 10);
|
||||
if (this.turnedUp) {
|
||||
cardBody.fill(this.color).stroke(255).strokeWidth(5).close();
|
||||
renderer.begin().center(x, y, 100, 150).circle(0, 0, 40).fill(255).close();
|
||||
|
||||
switch (this.value) {
|
||||
case "CHOOSE":
|
||||
if (!this.actualColor) {
|
||||
renderer.begin().center(x, y, 100, 150).arc(0, 0, 35, 0, 0.5 * Math.PI).finish().fill('RED').close();
|
||||
renderer.begin().center(x, y, 100, 150).arc(0, 0, 35, 0.5 * Math.PI, Math.PI).finish().fill('GREEN').close();
|
||||
renderer.begin().center(x, y, 100, 150).arc(0, 0, 35, Math.PI, 1.5 * Math.PI).finish().fill('YELLOW').close();
|
||||
renderer.begin().center(x, y, 100, 150).arc(0, 0, 35, 1.5 * Math.PI, 2 * Math.PI).finish().fill('BLUE').close();
|
||||
} else {
|
||||
renderer.begin().center(x, y, 100, 150).circle(0, 0, 35).fill(this.actualColor).close();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
renderer.begin().center(x, y, 100, 150).text(this.value[0]).align('center').fontSize(48).baseline('middle').fill(0).close();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
const temp = cardBody.offset.x;
|
||||
cardBody.fill(150, 0, 255).stroke(255).strokeWidth(5).close();
|
||||
}
|
||||
}
|
||||
}
|
30
uno/client/CardDeck.js
Normal file
30
uno/client/CardDeck.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
Card
|
||||
} from "./Card.js";
|
||||
|
||||
export class CardDeck {
|
||||
constructor() {
|
||||
this.cards = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random card of this deck
|
||||
* @returns {Card}
|
||||
*/
|
||||
getRandomCard() {
|
||||
return this.cards[Math.floor(Math.random() * this.cards.length)];
|
||||
}
|
||||
}
|
||||
|
||||
export const cardDecks = [];
|
||||
|
||||
// Default card deck
|
||||
const defaultDeck = new CardDeck();
|
||||
const colors = ['RED', 'GREEN', 'BLUE', 'YELLOW'];
|
||||
colors.forEach(color => {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
defaultDeck.cards.push(new Card(color, i));
|
||||
}
|
||||
});
|
||||
|
||||
cardDecks.push(defaultDeck);
|
66
uno/client/EventHandler.js
Normal file
66
uno/client/EventHandler.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
isInRegion,
|
||||
getRelativeRegionCoordinates,
|
||||
getAbsoluteRegionCoordinates
|
||||
} from "./helpers.js";
|
||||
|
||||
export class EventHandler {
|
||||
constructor(element) {
|
||||
this.element = element;
|
||||
|
||||
this.listeners = [];
|
||||
|
||||
this.element.addEventListener('mousemove', e => {
|
||||
const {
|
||||
x,
|
||||
y,
|
||||
relX,
|
||||
relY,
|
||||
} = this.getCoordinates(e.clientX, e.clientY);
|
||||
|
||||
this.listeners.filter(l => l.event === 'hover' && isInRegion(relX, relY, l.region)).forEach(l => {
|
||||
const coords = getAbsoluteRegionCoordinates(x, y, l.region, this.element.width, this.element.height);
|
||||
const relCoords = getRelativeRegionCoordinates(relX, relY, l.region);
|
||||
const event = {
|
||||
x: coords.x,
|
||||
y: coords.y,
|
||||
relX: relCoords.x,
|
||||
relY: relCoords.y,
|
||||
event: e,
|
||||
};
|
||||
l.callback(event);
|
||||
});
|
||||
});
|
||||
|
||||
this.element.addEventListener('click', e => {
|
||||
const event = {
|
||||
x: e.clientX - this.element.offsetLeft,
|
||||
y: e.clientY - this.element.offsetTop,
|
||||
event: e,
|
||||
};
|
||||
this.listeners.filter(l => l.event === 'click').forEach(listener => listener.callback(event));
|
||||
});
|
||||
}
|
||||
|
||||
getCoordinates(x, y) {
|
||||
return {
|
||||
x: x - this.element.offsetLeft,
|
||||
y: y - this.element.offsetTop,
|
||||
relX: (x - this.element.offsetLeft) / this.element.clientWidth,
|
||||
relY: (y - this.element.offsetTop) / this.element.clientHeight,
|
||||
};
|
||||
}
|
||||
|
||||
on(type, callback, x = 0, y = 0, width = 1, height = 1) {
|
||||
this.listeners.push({
|
||||
event: type,
|
||||
region: {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height
|
||||
},
|
||||
callback: callback,
|
||||
});
|
||||
}
|
||||
}
|
206
uno/client/Game.js
Normal file
206
uno/client/Game.js
Normal file
@@ -0,0 +1,206 @@
|
||||
import {
|
||||
cardDecks
|
||||
} from "./CardDeck.js";
|
||||
import {
|
||||
Player
|
||||
} from "./Player.js";
|
||||
import {
|
||||
Stack
|
||||
} from "./Stack.js";
|
||||
import {
|
||||
SocketHandler
|
||||
} from "./socket.js";
|
||||
import {
|
||||
Card
|
||||
} from "./Card.js";
|
||||
|
||||
export class Game {
|
||||
constructor() {
|
||||
this.players = [];
|
||||
this.player = new Player(this);
|
||||
this.players.push(this.player);
|
||||
this.activePlayerID = null;
|
||||
this.players[0].isTurn = true;
|
||||
|
||||
this.clockwise = true;
|
||||
|
||||
this.deck = cardDecks[0];
|
||||
this.drawingStack = new Stack();
|
||||
this.playingStack = new Stack();
|
||||
|
||||
|
||||
this.config = {
|
||||
startingCards: 10,
|
||||
};
|
||||
|
||||
this.eventHandlers = [];
|
||||
this.eventListeners = [];
|
||||
|
||||
this.arrowRotation = 0;
|
||||
this.choosingColor = false;
|
||||
}
|
||||
|
||||
init() {
|
||||
this.player.init();
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
this.drawingStack.add(new Card("", "", false));
|
||||
}
|
||||
|
||||
this.registerListener('click', (data) => {
|
||||
if (this.choosingColor) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(data);
|
||||
const minX = this.renderer.width / 2 - 330;
|
||||
const maxX = minX + 130;
|
||||
const minY = this.renderer.height / 2 - 330;
|
||||
const maxY = minY + 180;
|
||||
if (data.x > minX && data.x < maxX && data.y > minY && data.y < maxX) {
|
||||
console.log('found');
|
||||
this.onCardDrawn();
|
||||
}
|
||||
});
|
||||
|
||||
this.registerListener('click', (data) => {
|
||||
if (!this.choosingColor) {
|
||||
return;
|
||||
}
|
||||
|
||||
const centerX = this.renderer.width / 2;
|
||||
const centerY = this.renderer.height / 2;
|
||||
const dx = data.x - centerX;
|
||||
const dy = data.y - centerY;
|
||||
const d = Math.sqrt(dx ** 2, dy ** 2)
|
||||
if (d <= 125) {
|
||||
if (dx > 0 && dy > 0) {
|
||||
this.onColorSelected('RED');
|
||||
} else if (dx > 0 && dy < 0) {
|
||||
this.onColorSelected('BLUE');
|
||||
} else if (dx < 0 && dy > 0) {
|
||||
this.onColorSelected('GREEN');
|
||||
} else {
|
||||
this.onColorSelected('YELLOW');
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
registerEventHandler(eventHandler) {
|
||||
this.eventHandlers.push(eventHandler);
|
||||
this.eventListeners.forEach(listener => eventHandler.on(...listener));
|
||||
}
|
||||
|
||||
registerListener(type, eventListener, x = 0, y = 0, width = 1, height = 1) {
|
||||
this.eventListeners.push([
|
||||
type,
|
||||
eventListener,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height
|
||||
]);
|
||||
this.eventHandlers.forEach(handler => handler.on(type, eventListener), x, y, width, height);
|
||||
}
|
||||
|
||||
onCardPlayed() {}
|
||||
onCardDrawn() {}
|
||||
onColorSelected(color) {}
|
||||
|
||||
playerJoined(player, cardCount) {
|
||||
this.players.push(player);
|
||||
for (let i = 0; i < cardCount; i++) {
|
||||
player.hand.push(new Card("", "", false));
|
||||
}
|
||||
}
|
||||
|
||||
playCard(card) {
|
||||
if (this.activePlayerID === this.player.id) {
|
||||
this.onCardPlayed(card);
|
||||
}
|
||||
}
|
||||
|
||||
draw(renderer) {
|
||||
// Drawing main stack
|
||||
renderer.push();
|
||||
renderer.anchor('center', 'center');
|
||||
this.playingStack.draw(renderer);
|
||||
renderer.pop();
|
||||
|
||||
// Drawing direction
|
||||
renderer.push();
|
||||
renderer.anchor('center', 'center');
|
||||
renderer.rotate(this.arrowRotation);
|
||||
this.renderer.begin().arc(0, 0, 200, 0, 0.8 * Math.PI).stroke(0).strokeWidth(20).close();
|
||||
this.renderer.begin().arc(0, 0, 200, Math.PI, 1.8 * Math.PI).stroke(0).strokeWidth(20).close();
|
||||
if (this.clockwise) {
|
||||
this.renderer.begin().rotate(0.8, 'RADIANS').translate(200, 0).triangle(-30, 0, 60, 60).fill(0).close();
|
||||
this.renderer.begin().rotate(1.8, 'RADIANS').translate(200, 0).triangle(-30, 0, 60, 60).fill(0).close();
|
||||
this.arrowRotation++;
|
||||
} else {
|
||||
this.renderer.begin().rotate(0, 'RADIANS').translate(200, 0).triangle(-30, 0, 60, -60).fill(0).close();
|
||||
this.renderer.begin().rotate(1, 'RADIANS').translate(200, 0).triangle(-30, 0, 60, -60).fill(0).close();
|
||||
this.arrowRotation--;
|
||||
}
|
||||
if (this.arrowRotation > 360) {
|
||||
this.arrowRotation -= 360;
|
||||
} else if (this.arrowRotation < 0) {
|
||||
this.arrowRotation += 360;
|
||||
}
|
||||
renderer.pop();
|
||||
|
||||
// Drawing backup stack
|
||||
renderer.push();
|
||||
renderer.anchor('center', 'center');
|
||||
renderer.translate(-250, -250);
|
||||
this.drawingStack.draw(renderer);
|
||||
renderer.pop();
|
||||
|
||||
// Drawing color selection
|
||||
if (this.choosingColor) {
|
||||
renderer.push();
|
||||
renderer.anchor('center', 'center');
|
||||
renderer.begin().arc(0, 0, 125, 0, 0.5 * Math.PI).finish().fill('RED').close();
|
||||
renderer.begin().arc(0, 0, 125, 0.5 * Math.PI, Math.PI).finish().fill('GREEN').close();
|
||||
renderer.begin().arc(0, 0, 125, Math.PI, 1.5 * Math.PI).finish().fill('YELLOW').close();
|
||||
renderer.begin().arc(0, 0, 125, 1.5 * Math.PI, 2 * Math.PI).finish().fill('BLUE').close();
|
||||
renderer.pop();
|
||||
}
|
||||
|
||||
// Drawing own hand
|
||||
renderer.push();
|
||||
renderer.anchor('center', 'bottom');
|
||||
if (this.activePlayerID === this.player.id) {
|
||||
renderer.begin().text("Your turn", 0, -200).fill(0).close();
|
||||
}
|
||||
|
||||
this.player.draw(renderer);
|
||||
renderer.pop();
|
||||
|
||||
// Drawing other players
|
||||
if (this.players.length > 1) {
|
||||
renderer.push();
|
||||
renderer.anchor('right', 'center');
|
||||
renderer.rotate(-90);
|
||||
this.players[1].draw(renderer);
|
||||
renderer.pop();
|
||||
}
|
||||
if (this.players.length > 2) {
|
||||
renderer.push();
|
||||
renderer.anchor('center', 'top');
|
||||
renderer.rotate(-180);
|
||||
renderer.begin().roundedRect(0, 0, 100, 150, 10).fill(0).close();
|
||||
this.players[2].draw(renderer);
|
||||
renderer.pop();
|
||||
}
|
||||
if (this.players.length > 3) {
|
||||
renderer.push();
|
||||
renderer.anchor('left', 'center');
|
||||
renderer.rotate(90);
|
||||
renderer.begin().roundedRect(0, 0, 100, 150, 10).fill(0).close();
|
||||
this.players[3].draw(renderer);
|
||||
renderer.pop();
|
||||
}
|
||||
}
|
||||
}
|
90
uno/client/Player.js
Normal file
90
uno/client/Player.js
Normal file
@@ -0,0 +1,90 @@
|
||||
export class Player {
|
||||
constructor(game, id, name) {
|
||||
this.game = game;
|
||||
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.hand = [];
|
||||
|
||||
this.isTurn = false;
|
||||
this.highlightedCard = null;
|
||||
|
||||
this.cardArea = {
|
||||
currentWidth: 0.5,
|
||||
allowedWidth: 0.5,
|
||||
currentCard: null,
|
||||
cardWidth: 0.1,
|
||||
};
|
||||
}
|
||||
|
||||
init() {
|
||||
this.registerEventListeners();
|
||||
}
|
||||
|
||||
registerEventListeners() {
|
||||
this.game.registerListener('hover', event => {
|
||||
const width = this.cardArea.currentWidth;
|
||||
const left = 0.5 - width / 2;
|
||||
const right = left + width - this.cardArea.cardWidth;
|
||||
this.highlightedCard = Math.min(Math.max(Math.floor((event.relX - left) / (right - left) * this.hand.length), 0), this.hand.length - 1);
|
||||
}, 0, 0.8, 1, 0.2);
|
||||
|
||||
this.game.registerListener('click', event => {
|
||||
console.log('clicked card');
|
||||
if (!this.isTurn) {
|
||||
return;
|
||||
}
|
||||
if (this.highlightedCard !== null) {
|
||||
this.playCard(this.highlightedCard);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addCard(card) {
|
||||
this.hand.push(card);
|
||||
}
|
||||
|
||||
setHand(cards) {
|
||||
this.hand = cards
|
||||
.sort((a, b) => a.value.localeCompare(b.value))
|
||||
.sort((a, b) => a.color.localeCompare(b.color));
|
||||
}
|
||||
|
||||
playCard(index) {
|
||||
console.log('played', this.name);
|
||||
|
||||
const card = this.hand[index];
|
||||
this.game.playCard(card);
|
||||
}
|
||||
|
||||
getCardOffset() {
|
||||
const width = 800;
|
||||
const offset = Math.min((width - 100) / this.hand.length, 60);
|
||||
return offset;
|
||||
}
|
||||
|
||||
getWidth(offset) {
|
||||
return offset * (this.hand.length - 1) + 100;
|
||||
}
|
||||
|
||||
draw(renderer) {
|
||||
const offset = this.getCardOffset();
|
||||
const width = this.getWidth(offset);
|
||||
let x = -width / 2;
|
||||
|
||||
this.cardArea.currentWidth = width / this.game.renderer.width;
|
||||
this.cardArea.cardWidth = 100 / this.game.renderer.width;
|
||||
|
||||
this.hand.forEach((card, index) => {
|
||||
if (this.highlightedCard === index) {
|
||||
this.cardArea.currentCard = x / this.game.renderer.width;
|
||||
card.draw(renderer, x);
|
||||
x += 100;
|
||||
} else {
|
||||
card.draw(renderer, x);
|
||||
x += offset;
|
||||
}
|
||||
});
|
||||
renderer.pop();
|
||||
}
|
||||
}
|
31
uno/client/Stack.js
Normal file
31
uno/client/Stack.js
Normal file
@@ -0,0 +1,31 @@
|
||||
export class Stack {
|
||||
constructor() {
|
||||
this.cards = [];
|
||||
}
|
||||
|
||||
allows(card) {
|
||||
return card.color === 'BLACK' || this.cards.length === 0 || this.cards[this.cards.length - 1].allows(card);
|
||||
}
|
||||
|
||||
add(card) {
|
||||
this.cards.push(card);
|
||||
if (this.cards.length > 5) {
|
||||
this.cards.splice(0, 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
setTopColor(color) {
|
||||
this.cards[this.cards.length - 1].actualColor = color;
|
||||
}
|
||||
|
||||
draw(renderer) {
|
||||
if (this.cards.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < 5 && i < this.cards.length; i++) {
|
||||
this.cards[i].draw(renderer, -i * 6, -i * 6);
|
||||
}
|
||||
}
|
||||
}
|
18
uno/client/helpers.js
Normal file
18
uno/client/helpers.js
Normal file
@@ -0,0 +1,18 @@
|
||||
export function isInRegion(x, y, region) {
|
||||
return x >= region.x && x <= region.x + region.width &&
|
||||
y >= region.y && y <= region.y + region.height;
|
||||
}
|
||||
|
||||
export function getRelativeRegionCoordinates(x, y, region) {
|
||||
return {
|
||||
x: (x - region.x) / region.width,
|
||||
y: (y - region.y) / region.height,
|
||||
};
|
||||
}
|
||||
|
||||
export function getAbsoluteRegionCoordinates(x, y, region, width, height) {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
}
|
39
uno/client/index.html
Normal file
39
uno/client/index.html
Normal file
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>UNO</title>
|
||||
<link rel="stylesheet" href="styles.css" type="text/css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="dialog-container title-screen">
|
||||
<div class="container">
|
||||
<h1 class="title margin-bottom">ETT.IO</h1>
|
||||
<button class="btn primary" id="btnCreateGame">Create New Game</button>
|
||||
<button class="btn primary margin-bottom" id="btnJoinGame">Join Game</button>
|
||||
<button class="btn">Credits</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog-container dialog hidden" id="dialogJoinGame">
|
||||
<div class="container">
|
||||
<div class="dialog-content">
|
||||
<p>Enter a game code:</p>
|
||||
<input type="text" id="inputGameCode">
|
||||
|
||||
<div class="dialog-footer">
|
||||
<button class="btn primary" id="btnJoinGameConfirm">Join Game</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<canvas id="canvas"></canvas>
|
||||
|
||||
<script type="module" src="menu.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
36
uno/client/menu.js
Normal file
36
uno/client/menu.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import {
|
||||
triggerCreateGame,
|
||||
triggerJoinGame,
|
||||
} from './uno.js';
|
||||
|
||||
const btnCreateGame = document.getElementById('btnCreateGame');
|
||||
const btnJoinGame = document.getElementById('btnJoinGame');
|
||||
const btnJoinGameConfirm = document.getElementById('btnJoinGameConfirm');
|
||||
|
||||
const inputGameCode = document.getElementById('inputGameCode');
|
||||
|
||||
btnCreateGame.addEventListener('click', () => {
|
||||
triggerCreateGame();
|
||||
fadeOutTitleScreen();
|
||||
});
|
||||
btnJoinGame.addEventListener('click', () => {
|
||||
showJoinDialog();
|
||||
});
|
||||
btnJoinGameConfirm.addEventListener('click', () => {
|
||||
const gameCode = inputGameCode.value;
|
||||
triggerJoinGame(gameCode);
|
||||
fadeOutTitleScreen();
|
||||
closeJoinDialog();
|
||||
})
|
||||
|
||||
function showJoinDialog() {
|
||||
document.getElementById('dialogJoinGame').classList.remove('hidden');
|
||||
}
|
||||
|
||||
function closeJoinDialog() {
|
||||
document.getElementById('dialogJoinGame').classList.add('hidden');
|
||||
}
|
||||
|
||||
function fadeOutTitleScreen() {
|
||||
document.getElementsByClassName('title-screen')[0].classList.add('hidden');
|
||||
}
|
574
uno/client/render.js
Normal file
574
uno/client/render.js
Normal file
@@ -0,0 +1,574 @@
|
||||
export class Renderer {
|
||||
constructor(canvas, game) {
|
||||
this.game = game;
|
||||
game.renderer = this;
|
||||
|
||||
this.canvas = canvas;
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
|
||||
this.width = canvas.width;
|
||||
this.height = canvas.height;
|
||||
|
||||
this.anchorOption = {
|
||||
hor: 'left',
|
||||
ver: 'top'
|
||||
};
|
||||
|
||||
this.virtualWidth = 100;
|
||||
this.virtualHeight = 100;
|
||||
}
|
||||
|
||||
setSize(width, height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
this.canvas.width = width;
|
||||
this.canvas.height = height;
|
||||
}
|
||||
|
||||
draw() {
|
||||
this.ctx.clearRect(0, 0, this.width, this.height);
|
||||
|
||||
this.game.draw(this);
|
||||
|
||||
window.requestAnimationFrame(() => this.draw());
|
||||
}
|
||||
|
||||
begin() {
|
||||
const drawing = new Drawing(this);
|
||||
drawing.anchor(this.anchorOption.hor, this.anchorOption.ver);
|
||||
return drawing;
|
||||
}
|
||||
|
||||
push() {
|
||||
this.ctx.save();
|
||||
}
|
||||
|
||||
pop() {
|
||||
this.ctx.restore();
|
||||
}
|
||||
|
||||
anchor(hor, ver) {
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
switch (hor) {
|
||||
case 'center':
|
||||
x = this.width / 2;
|
||||
break;
|
||||
case 'right':
|
||||
x = this.width;
|
||||
break;
|
||||
}
|
||||
switch (ver) {
|
||||
case 'center':
|
||||
y = this.height / 2;
|
||||
break;
|
||||
case 'bottom':
|
||||
y = this.height;
|
||||
}
|
||||
|
||||
this.translate(x, y);
|
||||
|
||||
this.anchorOption = {
|
||||
hor,
|
||||
ver
|
||||
};
|
||||
}
|
||||
|
||||
rotate(degrees) {
|
||||
this.ctx.rotate(degrees * Math.PI / 180);
|
||||
}
|
||||
|
||||
translate(x, y) {
|
||||
this.ctx.translate(x, y);
|
||||
}
|
||||
|
||||
scale(x, y) {
|
||||
this.ctx.scale(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
class Drawing {
|
||||
constructor(renderer) {
|
||||
this.renderer = renderer;
|
||||
this.ctx = renderer.ctx;
|
||||
|
||||
this.ctx.save();
|
||||
|
||||
this.anchorPoint = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
this.offset = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
|
||||
this.translation = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
this.rotation = 0;
|
||||
|
||||
this.fillColor = undefined;
|
||||
this.strokeColor = undefined;
|
||||
this.lineWidth = 1;
|
||||
this.lineCap = 'butt';
|
||||
this.lineJoin = 'miter';
|
||||
|
||||
this.type = undefined;
|
||||
this.args = {};
|
||||
|
||||
this.textOptions = {
|
||||
font: 'sans-serif',
|
||||
fontSize: 16,
|
||||
alignment: 'left',
|
||||
baseline: 'alphabetic',
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Shapes
|
||||
*/
|
||||
|
||||
|
||||
arc(x, y, radius, startAngle, endAngle) {
|
||||
this.type = 'arc';
|
||||
this.args = {
|
||||
x,
|
||||
y,
|
||||
radius,
|
||||
startAngle,
|
||||
endAngle
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
circle(x, y, radius) {
|
||||
this.type = 'circle';
|
||||
this.args = {
|
||||
x,
|
||||
y,
|
||||
radius
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
rect(x, y, width, height) {
|
||||
this.type = 'rect';
|
||||
this.args = {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
roundedRect(x, y, width, height, roundness) {
|
||||
this.type = 'roundedRect';
|
||||
this.args = {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
roundness
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
triangle(x, y, width, height) {
|
||||
this.type = 'triangle';
|
||||
this.args = {
|
||||
x1: x,
|
||||
y1: y,
|
||||
x2: x + width,
|
||||
y2: y,
|
||||
x3: x + width / 2,
|
||||
y3: y + height
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
Path
|
||||
*/
|
||||
|
||||
vertex(x, y) {
|
||||
this.type = 'line';
|
||||
if (!this.args.vertices) {
|
||||
this.args.vertices = [];
|
||||
}
|
||||
this.args.vertices.push([x, y]);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
finish() {
|
||||
this.args.close = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
Text
|
||||
*/
|
||||
|
||||
|
||||
text(string, x = 0, y = 0) {
|
||||
this.type = 'text';
|
||||
this.args = {
|
||||
x,
|
||||
y,
|
||||
string
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text alignment
|
||||
* @param {'start'|'end'|'left'|'center'|'right'} alignment
|
||||
* @returns {this}
|
||||
*/
|
||||
align(alignment) {
|
||||
this.textOptions.alignment = alignment;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets text baseline
|
||||
* @param {'alphabetic'|'top'|'hanging'|'middle'|'ideographic'|'bottom'} base
|
||||
* @return {Drawing}
|
||||
*/
|
||||
baseline(base) {
|
||||
this.textOptions.baseline = base;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fontSize(fontSize) {
|
||||
this.textOptions.fontSize = fontSize;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
Transformations
|
||||
*/
|
||||
|
||||
|
||||
anchor(hor, ver) {
|
||||
switch (hor) {
|
||||
case 'center':
|
||||
this.anchorPoint.x = this.renderer.width / 2;
|
||||
this.offset.x = -0.5;
|
||||
break;
|
||||
case 'right':
|
||||
this.anchorPoint.x = this.renderer.width;
|
||||
this.offset.x = -1;
|
||||
break;
|
||||
default:
|
||||
this.anchorPoint.x = 0;
|
||||
this.offset.x = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ver) {
|
||||
case 'center':
|
||||
this.anchorPoint.y = this.renderer.height / 2;
|
||||
this.offset.y = -0.5;
|
||||
break;
|
||||
case 'bottom':
|
||||
this.anchorPoint.y = this.renderer.height;
|
||||
this.offset.y = -1;
|
||||
break;
|
||||
default:
|
||||
this.anchorPoint.y = 0;
|
||||
this.offset.y = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
rotate(amount, mode = 'DEGREES') {
|
||||
if (mode === 'DEGREES') {
|
||||
amount *= 180;
|
||||
}
|
||||
this.rotation += amount * Math.PI;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
translate(x, y) {
|
||||
this.translation.x += x;
|
||||
this.translation.y += y;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate to center of rectangle
|
||||
* @param {number} x x position
|
||||
* @param {number} y y position
|
||||
* @param {number} width width
|
||||
* @param {number} height height
|
||||
* @returns {this}
|
||||
*/
|
||||
center(x, y, width, height) {
|
||||
this.translation.x += x + this.offset.x * width + width / 2;
|
||||
this.translation.y += y + this.offset.y * height + height / 2;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
Colors
|
||||
*/
|
||||
|
||||
|
||||
fill(...color) {
|
||||
this.fillColor = parseColor('rgb', ...color);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
stroke(...color) {
|
||||
this.strokeColor = parseColor('rgb', ...color);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets line width for strokes
|
||||
* @param {number} width
|
||||
*/
|
||||
strokeWidth(width) {
|
||||
this.lineWidth = width;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets line cap for strokes
|
||||
* @param {'butt'|'round'|'square'} style
|
||||
*/
|
||||
strokeCap(style) {
|
||||
this.lineCap = style;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
calcCoords(x, y, width, height) {
|
||||
return [
|
||||
x + this.offset.x * width,
|
||||
y + this.offset.y * height,
|
||||
];
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.fillColor) {
|
||||
this.ctx.fillStyle = this.fillColor;
|
||||
}
|
||||
if (this.strokeColor) {
|
||||
this.ctx.strokeStyle = this.strokeColor;
|
||||
this.ctx.lineWidth = this.lineWidth;
|
||||
this.ctx.lineCap = this.lineCap;
|
||||
}
|
||||
|
||||
this.ctx.rotate(this.rotation);
|
||||
this.ctx.translate(this.translation.x, this.translation.y);
|
||||
|
||||
switch (this.type) {
|
||||
case 'arc':
|
||||
drawArc(this.ctx, this.args, !!this.fillColor, !!this.strokeColor);
|
||||
break;
|
||||
case 'circle':
|
||||
drawCircle(this.ctx, this.args, !!this.fillColor, !!this.strokeColor);
|
||||
break;
|
||||
case 'line':
|
||||
drawShape(this.ctx, this.args.close, this.args.vertices, !!this.fillColor, !!this.strokeColor);
|
||||
break;
|
||||
case 'rect':
|
||||
/*if (this.fillColor) {
|
||||
this.ctx.fillRect(...this.args);
|
||||
}
|
||||
if (this.strokeColor) {
|
||||
this.ctx.strokeRect(...this.args);
|
||||
}*/
|
||||
break;
|
||||
case 'roundedRect':
|
||||
this.args.x = this.calcCoords(this.args.x, 0, this.args.width, 0)[0];
|
||||
this.args.y = this.calcCoords(0, this.args.y, 0, this.args.height)[1];
|
||||
|
||||
drawRoundedRect(this.ctx, this.args, !!this.fillColor, !!this.strokeColor);
|
||||
break;
|
||||
case 'shape':
|
||||
console.log('test');
|
||||
drawShape(this.ctx, true, this.args, !!this.fillColor, !!this.strokeColor);
|
||||
break;
|
||||
case 'text':
|
||||
this.ctx.font = this.textOptions.fontSize + 'px ' + this.textOptions.font;
|
||||
this.args.x = this.calcCoords(this.args.x, 0, 0)[0];
|
||||
this.args.y = this.calcCoords(0, this.args.y, 0, 0)[1];
|
||||
|
||||
drawText(this.ctx, this.args, !!this.fillColor, !!this.strokeColor, this.textOptions);
|
||||
break;
|
||||
case 'triangle':
|
||||
drawTriangle(this.ctx, this.args, !!this.fillColor, !!this.strokeColor);
|
||||
break;
|
||||
}
|
||||
|
||||
this.ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
function parseColor(colorMode, ...color) {
|
||||
if (color.length === 0) {
|
||||
return '#000000';
|
||||
}
|
||||
|
||||
if (color.length === 1 && typeof color[0] === 'string') {
|
||||
return color[0];
|
||||
}
|
||||
|
||||
if (color.length === 1 && typeof color[0] === 'number') {
|
||||
color.push(color[0]);
|
||||
}
|
||||
|
||||
if (color.length === 2 && typeof color[0] === 'number') {
|
||||
color.push(color[0]);
|
||||
}
|
||||
|
||||
if (color.length === 3) {
|
||||
return `${colorMode}(${color[0]}, ${color[1]}, ${color[2]})`;
|
||||
}
|
||||
|
||||
return '#000000';
|
||||
}
|
||||
|
||||
function drawArc(ctx, args, fill, stroke) {
|
||||
const {
|
||||
x,
|
||||
y,
|
||||
radius,
|
||||
startAngle,
|
||||
endAngle,
|
||||
close,
|
||||
} = args;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, startAngle, endAngle);
|
||||
|
||||
if (close) {
|
||||
ctx.lineTo(x, y);
|
||||
ctx.closePath();
|
||||
}
|
||||
|
||||
if (fill) {
|
||||
ctx.fill();
|
||||
}
|
||||
if (stroke) {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
function drawCircle(ctx, args, fill, stroke) {
|
||||
const {
|
||||
x,
|
||||
y,
|
||||
radius
|
||||
} = args;
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, y, radius, 0, 2 * Math.PI);
|
||||
|
||||
if (fill) {
|
||||
ctx.fill();
|
||||
}
|
||||
if (stroke) {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
function drawShape(ctx, close, vertices, fill, stroke) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(...vertices[0]);
|
||||
|
||||
for (let i = 1; i < vertices.length; i++) {
|
||||
ctx.lineTo(...vertices[i]);
|
||||
}
|
||||
|
||||
if (close) {
|
||||
ctx.closePath();
|
||||
}
|
||||
|
||||
if (fill) {
|
||||
ctx.fill();
|
||||
}
|
||||
if (stroke) {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
function drawRoundedRect(ctx, args, fill, stroke) {
|
||||
const {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
roundness
|
||||
} = args;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x + roundness, y);
|
||||
ctx.arc(x + width - roundness, y + roundness, roundness, -0.5 * Math.PI, 0);
|
||||
ctx.arc(x + width - roundness, y + height - roundness, roundness, 0, 0.5 * Math.PI);
|
||||
ctx.arc(x + roundness, y + height - roundness, roundness, 0.5 * Math.PI, Math.PI);
|
||||
ctx.arc(x + roundness, y + roundness, roundness, Math.PI, 1.5 * Math.PI);
|
||||
|
||||
if (fill) {
|
||||
ctx.fill();
|
||||
}
|
||||
if (stroke) {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
function drawText(ctx, args, fill, stroke, options) {
|
||||
const {
|
||||
x,
|
||||
y,
|
||||
string
|
||||
} = args;
|
||||
ctx.font = options.fontSize + 'px ' + options.font;
|
||||
ctx.textAlign = options.alignment;
|
||||
ctx.textBaseline = options.baseline;
|
||||
|
||||
if (fill) {
|
||||
ctx.fillText(string, x, y);
|
||||
}
|
||||
if (stroke) {
|
||||
ctx.strokeText(string, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
function drawTriangle(ctx, args, fill, stroke) {
|
||||
const {
|
||||
x1,
|
||||
y1,
|
||||
x2,
|
||||
y2,
|
||||
x3,
|
||||
y3
|
||||
} = args;
|
||||
drawShape(ctx, true, [
|
||||
[x1, y1],
|
||||
[x2, y2],
|
||||
[x3, y3]
|
||||
], fill, stroke)
|
||||
}
|
104
uno/client/socket.js
Normal file
104
uno/client/socket.js
Normal file
@@ -0,0 +1,104 @@
|
||||
export class SocketHandler {
|
||||
constructor(url) {
|
||||
this.url = url;
|
||||
this.socket = new WebSocket(url);
|
||||
|
||||
this.socket.addEventListener('open', (e) => {
|
||||
this.callbacks.open.forEach(cb => cb(e));
|
||||
})
|
||||
this.socket.addEventListener('message', (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
this.callbacks.message.forEach((listener, index) => {
|
||||
if (listener.type && listener.type !== data.type) {
|
||||
return;
|
||||
}
|
||||
listener.cb(data.type, data.data, e);
|
||||
if (listener.once) {
|
||||
this.callbacks.message.splice(index, 1);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.callbacks = {
|
||||
open: [],
|
||||
message: [],
|
||||
close: [],
|
||||
};
|
||||
|
||||
this.initialized = false;
|
||||
this.gameToken = null;
|
||||
}
|
||||
|
||||
async initGame() {
|
||||
this.initialized = false;
|
||||
this.gameToken = null;
|
||||
return this.sendAndWaitForResult('game.init', {
|
||||
playerCount: 4,
|
||||
});
|
||||
}
|
||||
|
||||
async joinGame(gameId) {
|
||||
this.initialized = false;
|
||||
this.gameToken = null;
|
||||
return this.sendAndWaitForResult('game.join', {
|
||||
gameID: gameId,
|
||||
});
|
||||
}
|
||||
|
||||
playCard(card) {
|
||||
if (!this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.send('card.play', {
|
||||
color: card.color,
|
||||
value: card.value,
|
||||
});
|
||||
}
|
||||
|
||||
drawCard() {
|
||||
if (!this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.send('card.draw', {})
|
||||
}
|
||||
|
||||
selectColor(color) {
|
||||
if (!this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.send('card.color', {
|
||||
color
|
||||
});
|
||||
}
|
||||
|
||||
send(type, data) {
|
||||
this.socket.send(JSON.stringify({
|
||||
type,
|
||||
data,
|
||||
}));
|
||||
}
|
||||
|
||||
async sendAndWaitForResult(type, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.send(type, data);
|
||||
this.callbacks.message.push({
|
||||
type: type + '.result',
|
||||
once: true,
|
||||
cb: (type, data) => {
|
||||
resolve(data);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onOpen(callback) {
|
||||
this.callbacks.open.push(callback);
|
||||
}
|
||||
|
||||
onMessage(callback) {
|
||||
this.callbacks.message.push(callback);
|
||||
}
|
||||
}
|
124
uno/client/styles.css
Normal file
124
uno/client/styles.css
Normal file
@@ -0,0 +1,124 @@
|
||||
* {
|
||||
font-family: Roboto;
|
||||
|
||||
--color-red: #f92f2f;
|
||||
--red-dark: #d01313;
|
||||
--color-orange: #e07535;
|
||||
--orange-dark: #bd5b20;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dialog-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.dialog-container.hidden {
|
||||
background: transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.dialog-container .container {
|
||||
transition: transform 200ms, opacity 200ms;
|
||||
}
|
||||
|
||||
.dialog-container.hidden .container {
|
||||
transform: translateY(-100%);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 250px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
background: rgba(0, 0, 0, .75);
|
||||
}
|
||||
|
||||
.dialog .container {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.title-screen {
|
||||
background: var(--color-red) radial-gradient(var(--color-orange), var(--color-red));
|
||||
transition: background-color 200ms;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 16px 32px;
|
||||
text-align: center;
|
||||
background: var(--color-orange);
|
||||
color: #ffffff;
|
||||
border-radius: 10px;
|
||||
border: 3px var(--orange-dark) solid;
|
||||
outline: 0;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 16px;
|
||||
transition: transform 200ms, box-shadow 200ms;
|
||||
text-shadow: 3px
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
input {
|
||||
border: 3px rgba(0, 0, 0, .15) solid;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 16px;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
.primary {
|
||||
background: var(--color-red);
|
||||
color: #ffffff;
|
||||
border-color: var(--red-dark);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 4em;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
border: 5px #fff solid;
|
||||
}
|
||||
|
||||
.margin-bottom {
|
||||
margin-bottom: 32px;
|
||||
}
|
137
uno/client/uno.js
Normal file
137
uno/client/uno.js
Normal file
@@ -0,0 +1,137 @@
|
||||
import {
|
||||
Renderer
|
||||
} from './render.js';
|
||||
import {
|
||||
EventHandler
|
||||
} from './EventHandler.js';
|
||||
import {
|
||||
SocketHandler
|
||||
} from './socket.js';
|
||||
import {
|
||||
Game
|
||||
} from './Game.js';
|
||||
import {
|
||||
Card
|
||||
} from './Card.js';
|
||||
import {
|
||||
Player
|
||||
} from './Player.js';
|
||||
|
||||
|
||||
const socketHandler = new SocketHandler('ws://localhost:8000');
|
||||
socketHandler.onMessage({
|
||||
cb: (type, data, e) => {
|
||||
console.log(type, data, e);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
export function triggerCreateGame() {
|
||||
socketHandler.initGame().then(result => {
|
||||
createGame(result);
|
||||
});
|
||||
}
|
||||
|
||||
export function triggerJoinGame(id) {
|
||||
socketHandler.joinGame(id).then(result => {
|
||||
createGame(result);
|
||||
});
|
||||
}
|
||||
|
||||
function createGame(data) {
|
||||
socketHandler.initialized = true;
|
||||
socketHandler.gameToken = data.gameID;
|
||||
|
||||
const game = new Game();
|
||||
game.player.id = data.playerID;
|
||||
game.activePlayerID = data.activePlayerID;
|
||||
game.onCardPlayed = (card) => {
|
||||
console.log(card);
|
||||
socketHandler.playCard(card);
|
||||
};
|
||||
game.onCardDrawn = () => {
|
||||
socketHandler.drawCard();
|
||||
};
|
||||
game.onColorSelected = (color) => {
|
||||
socketHandler.selectColor(color);
|
||||
};
|
||||
|
||||
if (data.players) {
|
||||
data.players.forEach(player => game.playerJoined(new Player(game, player.playerID, player.playerName), player.cardCount));
|
||||
}
|
||||
|
||||
game.init();
|
||||
console.log(game);
|
||||
|
||||
game.player.setHand(data.hand.map(card => new Card(card.Color, card.Value, true)));
|
||||
data.playingStack.forEach(card => {
|
||||
game.playingStack.cards.unshift(new Card(card.Color, card.Value, true));
|
||||
})
|
||||
|
||||
const canvas = document.getElementById('canvas');
|
||||
const renderer = new Renderer(canvas, game);
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
renderer.draw();
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
console.log('test');
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
});
|
||||
|
||||
const eventHandler = new EventHandler(canvas);
|
||||
game.registerEventHandler(eventHandler);
|
||||
|
||||
socketHandler.onMessage({
|
||||
type: 'game.joined',
|
||||
cb: (type, data) => {
|
||||
game.playerJoined(new Player(game, data.playerID, data.playerName), data.cardCount);
|
||||
},
|
||||
})
|
||||
socketHandler.onMessage({
|
||||
type: 'card.played',
|
||||
cb: (type, data) => {
|
||||
console.log(data);
|
||||
|
||||
game.playingStack.add(new Card(data.card.Color, data.card.Value, true));
|
||||
},
|
||||
});
|
||||
socketHandler.onMessage({
|
||||
type: 'card.color',
|
||||
cb: (type, data) => {
|
||||
console.log(data);
|
||||
|
||||
game.playingStack.setTopColor(data.color);
|
||||
},
|
||||
});
|
||||
socketHandler.onMessage({
|
||||
type: 'card.drawn',
|
||||
cb: (type, data) => {
|
||||
const player = game.players.find(p => p.id === data.playerID)
|
||||
for (let i = 0; i < data.count; i++) {
|
||||
player.addCard(new Card("", "", false));
|
||||
}
|
||||
},
|
||||
});
|
||||
socketHandler.onMessage({
|
||||
type: 'color.choose',
|
||||
cb: (type, data) => {
|
||||
game.choosingColor = true;
|
||||
},
|
||||
});
|
||||
socketHandler.onMessage({
|
||||
type: 'turn.completed',
|
||||
cb: (type, data) => {
|
||||
game.choosingColor = false;
|
||||
game.activePlayerID = data.activePlayerID;
|
||||
game.clockwise = data.directionClockwise;
|
||||
},
|
||||
});
|
||||
socketHandler.onMessage({
|
||||
type: 'player.hand',
|
||||
cb: (type, data) => {
|
||||
console.log(data);
|
||||
|
||||
game.player.setHand(data.cards.map(card => new Card(card.Color, card.Value, true)));
|
||||
},
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user