Major change
1. seperated view 2. use object data to communicate with room 3. changed main view and room view
This commit is contained in:
parent
0d31d4e570
commit
e45bc45ff4
85
src/index.js
85
src/index.js
@ -1,8 +1,7 @@
|
|||||||
// index.js
|
// index.js
|
||||||
|
|
||||||
/* Class */
|
/* Class */
|
||||||
import BasicCanvas from './js/basic_canvas.js';
|
import View from './js/view.js';
|
||||||
import Room from './js/room.js';
|
|
||||||
/* SCSS */
|
/* SCSS */
|
||||||
import './styles/uno-game.scss';
|
import './styles/uno-game.scss';
|
||||||
/* Image */
|
/* Image */
|
||||||
@ -16,84 +15,18 @@ global.uno_game_div;
|
|||||||
global.uno_game_w = window.innerWidth-1;
|
global.uno_game_w = window.innerWidth-1;
|
||||||
global.uno_game_h = window.innerHeight-1;
|
global.uno_game_h = window.innerHeight-1;
|
||||||
|
|
||||||
|
const view = new View();
|
||||||
|
|
||||||
/* Main view */
|
/* Main view */
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
// Get uno-game-div
|
console.log('UNO GAME')
|
||||||
|
// Get uno-game div
|
||||||
global.uno_game_div = document.getElementById('uno-game');
|
global.uno_game_div = document.getElementById('uno-game');
|
||||||
global.uno_game_div.classList.add('uno-game-div');
|
global.uno_game_div.classList.add('uno-game-main-div');
|
||||||
|
global.uno_game_div.style.height = global.uno_game_h +'px';
|
||||||
// Create background canv
|
|
||||||
const bkg = new BasicCanvas(0, 0, global.uno_game_w, global.uno_game_h);
|
|
||||||
bkg.canvas.style.backgroundImage = 'url(' + green_table +')';
|
|
||||||
|
|
||||||
// Create UNO title
|
|
||||||
const title = document.createElement('h1');
|
|
||||||
title.innerHTML = 'UNO Game';
|
|
||||||
title.classList.add('uno-game-title');
|
|
||||||
title.style.fontSize = global.uno_game_h/6 + 'px';
|
|
||||||
title.style.top = global.uno_game_h/3 + 'px';
|
|
||||||
title.style.zIndex = global.canvas_count;
|
|
||||||
global.canvas_count++;
|
|
||||||
global.uno_game_div.appendChild( title );
|
|
||||||
|
|
||||||
// Create div for button group
|
|
||||||
const btn_group = document.createElement('div');
|
|
||||||
btn_group.classList.add('uno-game-btn-div');
|
|
||||||
btn_group.style.top = global.uno_game_h*2/3 + 'px';
|
|
||||||
btn_group.style.zIndex = global.canvas_count;
|
|
||||||
global.canvas_count++;
|
|
||||||
global.uno_game_div.appendChild( btn_group );
|
|
||||||
|
|
||||||
// Create single player button
|
|
||||||
const sp_btn = document.createElement('button');
|
|
||||||
sp_btn.classList.add('uno-game-btn');
|
|
||||||
sp_btn.innerHTML = 'Single Player';
|
|
||||||
sp_btn.style.fontSize = global.uno_game_h/16 + 'px';
|
|
||||||
btn_group.appendChild( sp_btn );
|
|
||||||
|
|
||||||
// Create multi player button
|
|
||||||
const mp_btn = document.createElement('button');
|
|
||||||
mp_btn.classList.add('uno-game-btn');
|
|
||||||
mp_btn.innerHTML = 'Multi Player (not work)';
|
|
||||||
mp_btn.style.fontSize = global.uno_game_h/16 + 'px';
|
|
||||||
btn_group.appendChild( mp_btn );
|
|
||||||
|
|
||||||
// Add event
|
|
||||||
sp_btn.addEventListener("click", e => {
|
|
||||||
// Remove elements
|
|
||||||
title.remove();
|
|
||||||
sp_btn.remove();
|
|
||||||
btn_group.remove();
|
|
||||||
createRoom();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// Set background to div
|
||||||
|
global.uno_game_div.style.backgroundImage = 'url(' + green_table +')';
|
||||||
|
|
||||||
|
view.showMainMenu();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/* Game start */
|
|
||||||
async function createRoom() {
|
|
||||||
console.info('Game start');
|
|
||||||
|
|
||||||
const room = new Room('room1');
|
|
||||||
|
|
||||||
room.addHuman('newini');
|
|
||||||
room.addBot();
|
|
||||||
room.addBot();
|
|
||||||
room.addBot();
|
|
||||||
|
|
||||||
await( room.initCards() );
|
|
||||||
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
||||||
|
|
||||||
await( room.dealCards() );
|
|
||||||
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 500));
|
|
||||||
|
|
||||||
await( room.startGame() );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
console.log('Uno End')
|
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
import Player from './player.js';
|
|
||||||
|
|
||||||
export default class Bot extends Player {
|
|
||||||
constructor(name, id) {
|
|
||||||
super(name+id, id);
|
|
||||||
this._type = 'bot';
|
|
||||||
}
|
|
||||||
|
|
||||||
changeColor() {
|
|
||||||
return Math.floor( Math.random() * 4 );
|
|
||||||
}
|
|
||||||
|
|
||||||
playCard(top_card) {
|
|
||||||
for (let i=0; i<this._cards.length; i++) {
|
|
||||||
const card = this._cards[i];
|
|
||||||
if ( top_card.isMatch(card) ) {
|
|
||||||
this._cards.splice(i, 1)[0];
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -31,17 +31,26 @@ export default class Card extends BasicCanvas {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set num(num) {
|
||||||
|
this._num = num;
|
||||||
|
}
|
||||||
get num() {
|
get num() {
|
||||||
return this._num;
|
return this._num;
|
||||||
}
|
}
|
||||||
set num(num) {
|
|
||||||
this._num = num;
|
set color_n(color_n) {
|
||||||
|
this._color_n = color_n;
|
||||||
}
|
}
|
||||||
get color_n() {
|
get color_n() {
|
||||||
return this._color_n;
|
return this._color_n;
|
||||||
}
|
}
|
||||||
set color_n(color_n) {
|
|
||||||
this._color_n = color_n;
|
set data(data) {
|
||||||
|
this._num = data.num;
|
||||||
|
this._color_n = data.color_n;
|
||||||
|
}
|
||||||
|
get data() {
|
||||||
|
return {num: this._num, color_n: this._color_n};
|
||||||
}
|
}
|
||||||
|
|
||||||
isMatch(card) {
|
isMatch(card) {
|
||||||
|
|||||||
@ -1,16 +0,0 @@
|
|||||||
import Player from './player.js';
|
|
||||||
import BasicCanvas from './basic_canvas.js';
|
|
||||||
|
|
||||||
export default class Human extends Player {
|
|
||||||
constructor(name, id) {
|
|
||||||
super(name, id);
|
|
||||||
|
|
||||||
this._type = 'human';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
playCard(top_card) {
|
|
||||||
return this._cards.splice(0, 1)[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,81 +0,0 @@
|
|||||||
import BasicCanvas from './basic_canvas.js';
|
|
||||||
|
|
||||||
export default class Player extends BasicCanvas {
|
|
||||||
constructor(name, id) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this._name = name;
|
|
||||||
this._id = id;
|
|
||||||
this._cards = [];
|
|
||||||
this._type = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
get id() {
|
|
||||||
return this._id;
|
|
||||||
}
|
|
||||||
set id(id) {
|
|
||||||
this._id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return this._name;
|
|
||||||
}
|
|
||||||
set name(name) {
|
|
||||||
this._name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
get type() {
|
|
||||||
return this._type;
|
|
||||||
}
|
|
||||||
set type(type) {
|
|
||||||
this._type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
get cards() {
|
|
||||||
return this._cards;
|
|
||||||
}
|
|
||||||
set cards(cards) {
|
|
||||||
this._cards = cards;
|
|
||||||
}
|
|
||||||
|
|
||||||
addCard(card) {
|
|
||||||
this._cards.push(card);
|
|
||||||
this.sortCards();
|
|
||||||
if (this.type === 'human') {
|
|
||||||
card.drawImageFront();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeCard(card) {
|
|
||||||
this._cards.splice(this._cards.indexOf(card), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
isEmpty() {
|
|
||||||
return (this._cards.length === 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ellipticalFormula(x, a, b) {
|
|
||||||
return b * ( 1 - Math.sqrt( 1 - (x/a - 1)**2 ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
reDeployCards() {
|
|
||||||
for (let i=0; i<this._cards.length; i++) {
|
|
||||||
if (this._id === 0) {
|
|
||||||
const x0 = global.uno_game_w/4;
|
|
||||||
const dx = global.uno_game_w/2 / (this._cards.length+1);
|
|
||||||
const y0 = global.uno_game_h*4/5;
|
|
||||||
this._cards[i].move(x0 + dx*(i+1), y0);
|
|
||||||
} else {
|
|
||||||
const x0 = global.uno_game_w*(this._id-1)/3;
|
|
||||||
const dx = global.uno_game_w/3 / (this._cards.length+2);
|
|
||||||
this._cards[i].move(x0 + dx*(i+1), this.ellipticalFormula(x0 + dx*(i+1), global.uno_game_w/2, global.uno_game_h));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sortCards() {
|
|
||||||
this._cards.sort( (a, b) => (a.num > b.num) ? 1 : -1 );
|
|
||||||
this.reDeployCards();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
268
src/js/room.js
268
src/js/room.js
@ -1,110 +1,135 @@
|
|||||||
import BasicCanvas from './basic_canvas.js';
|
import Rule from './rule.js';
|
||||||
import Human from './human.js';
|
|
||||||
import Bot from './bot.js';
|
|
||||||
import Card from './card.js';
|
|
||||||
/* Images */
|
|
||||||
|
|
||||||
export default class Room extends BasicCanvas {
|
export default class Room {
|
||||||
constructor(name) {
|
constructor(name) {
|
||||||
super(0, 0, global.uno_game_w, global.uno_game_h/10);
|
this._rule = new Rule();
|
||||||
|
|
||||||
this._name = name;
|
this._name = name;
|
||||||
this._players = [];
|
this._players = [];
|
||||||
this._cards = [];
|
this._remain_cards = [];
|
||||||
this._used_cards = [];
|
this._discard_piles = [];
|
||||||
|
this._discard_pile_top = {};
|
||||||
|
this._current_player = {};
|
||||||
|
this._game_status = 'ready';
|
||||||
|
}
|
||||||
|
|
||||||
// Fill room name
|
receive(data) {
|
||||||
this._ctx.font = Math.floor(this._h/3) + "px Arial";
|
if (data.ctrl === 'addHuman') this.addHuman(data.name);
|
||||||
this._ctx.fillText(name, 10, 50);
|
else if (data.ctrl === 'addBot') this.addBot();
|
||||||
|
else if (data.ctrl === 'startGame') this.startGame();
|
||||||
|
else if (data.ctrl === 'playCard') this.playCard(data.card);
|
||||||
|
else if (data.ctrl === 'changeColor') this.changeColor(data.color_n);
|
||||||
|
else if (data.ctrl === 'drawCard') this.drawCard();
|
||||||
}
|
}
|
||||||
|
|
||||||
addHuman(name) {
|
addHuman(name) {
|
||||||
this._players.push( new Human(name, this._players.length) );
|
this._players.push( {type: 'human', name: name, id: this._players.length } );
|
||||||
console.log(this._players);
|
console.log(this._players);
|
||||||
}
|
}
|
||||||
|
|
||||||
addBot() {
|
addBot() {
|
||||||
this._players.push( new Bot('bot', this._players.length) );
|
this._players.push( {type: 'bot', name: 'bot'+this._players.length, id: this._players.length} );
|
||||||
console.log(this._players);
|
console.log(this._players);
|
||||||
}
|
}
|
||||||
|
|
||||||
initCards() {
|
respondData() {
|
||||||
console.log('Initialize cards')
|
return JSON.stringify( {
|
||||||
const index_arr = [...Array(108).keys()];
|
room: {
|
||||||
for (let num=0; num<14; num++) {
|
name: this._name
|
||||||
for (let color_n=0; color_n<8; color_n++) {
|
},
|
||||||
if ( (num === 0) && (color_n >= 4) ) { // Skip blank card
|
players: this._players,
|
||||||
continue;
|
remain_cards: this._remain_cards,
|
||||||
}
|
discard_piles: this._discard_piles,
|
||||||
if ( (num === 13) && (color_n >= 4) ) { // +4 cards
|
discard_pile_top: this._discard_pile_top,
|
||||||
num = 14;
|
current_player: this._current_player,
|
||||||
}
|
game_status: this._game_status
|
||||||
const card_index = index_arr.splice(Math.floor( Math.random() * index_arr.length), 1)[0];
|
} );
|
||||||
this._cards[ card_index ] = new Card(global.uno_game_w*6/16+card_index, global.uno_game_h/2, num, color_n%4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// re-order z-index
|
|
||||||
for (let i=0; i<this._cards.length; i++) {
|
|
||||||
this._cards[i].refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dealCards() {
|
|
||||||
console.log('Deal cards')
|
|
||||||
this._players.forEach( (player) => {
|
|
||||||
for (let i=0; i<7; i++) {
|
|
||||||
player.addCard( this._cards.pop() );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async startGame() {
|
async startGame() {
|
||||||
console.log('Game start');
|
console.log('[Room] Game start');
|
||||||
|
|
||||||
// Init
|
// Init
|
||||||
this._turn_count = 0;
|
this._game_status = 'init';
|
||||||
this._skip = false;
|
this._skip = false;
|
||||||
this._reverse = false;
|
this._reverse = false;
|
||||||
this._draw2 = false;
|
this._draw2 = false;
|
||||||
this._draw4 = false;
|
this._draw4 = false;
|
||||||
this._current_player = this._players[0];
|
this._wild = false;
|
||||||
|
this._turn_count = 0,
|
||||||
|
|
||||||
await( this.initDiscardPile() );
|
await ( this.initCards() );
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
await ( this.dealCards() );
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
await ( this.initDiscardPile() );
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
|
||||||
|
this._current_player = this._players[0];
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
this._game_status = 'start';
|
||||||
|
|
||||||
this.initTurn();
|
this.initTurn();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initCards() {
|
||||||
|
console.log('[Room] Initialize cards')
|
||||||
|
|
||||||
|
const index_arr = [...Array(108).keys()]; // Create index pool
|
||||||
|
for (let x=0; x<14; x++) {
|
||||||
|
let num = x;
|
||||||
|
for (let y=0; y<8; y++) {
|
||||||
|
const color_n = y%4;
|
||||||
|
if ( (x === 0) && (y >= 4) ) continue; // Skip blank card
|
||||||
|
if ( (x === 13) && (y >= 4) ) num = 14; // +4 cards
|
||||||
|
// Get random index
|
||||||
|
const card_index = index_arr.splice(Math.floor( Math.random() * index_arr.length), 1)[0];
|
||||||
|
this._remain_cards[ card_index ] = {num: num, color_n: color_n, x: x, y: y};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dealCards(n_deal_cards=7) {
|
||||||
|
console.log('[Room] Deal cards')
|
||||||
|
this._players.forEach( (player) => {
|
||||||
|
player.cards = [];
|
||||||
|
for (let i=0; i<n_deal_cards; i++) player.cards.push( this._remain_cards.pop() );
|
||||||
|
player.cards.sort( (a, b) => (a.num > b.num) ? 1 : -1 );
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Not allow special card to be first discard pile
|
// Not allow special card to be first discard pile
|
||||||
initDiscardPile() {
|
initDiscardPile() {
|
||||||
while (true) {
|
while (true) {
|
||||||
const card = this._cards.pop();
|
const card = this._remain_cards.pop();
|
||||||
if (card.num <= 9) {
|
if (card.num <= 9) {
|
||||||
this.changeTopCard( card );
|
this.changeTopCard( card );
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
this._cards.splice( Math.floor(Math.random()*this._cards.length), 0, card);
|
// push back to a random position
|
||||||
|
this._remain_cards.splice( Math.floor(Math.random()*this._remain_cards.length), 0, card);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async initTurn() {
|
async initTurn() {
|
||||||
console.log('Turn count: ' + this._turn_count + ', current player: ' + this._current_player.name);
|
console.log('[Room] Turn count: ' + this._turn_count + ', current player: ' + this._current_player.name);
|
||||||
await new Promise(resolve => setTimeout(resolve, 500));
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
|
||||||
// check draw cards
|
// check draw cards
|
||||||
if (this._draw2) {
|
if (this._draw2) {
|
||||||
this._draw2 = false;
|
this._draw2 = false;
|
||||||
for (let i=0; i<2; i++) this._current_player.addCard( this._cards.pop() );
|
for (let i=0; i<2; i++) this._current_player.cards.push( this._remain_cards.pop() );
|
||||||
|
this._current_player.cards.sort( (a, b) => (a.num > b.num) ? 1 : -1 );
|
||||||
this.finishTurn();
|
this.finishTurn();
|
||||||
} else if (this._draw4) {
|
} else if (this._draw4) {
|
||||||
this._draw4 = false;
|
this._draw4 = false;
|
||||||
for (let i=0; i<4; i++) this._current_player.addCard( this._cards.pop() );
|
for (let i=0; i<4; i++) this._current_player.cards.push( this._remain_cards.pop() );
|
||||||
|
this._current_player.cards.sort( (a, b) => (a.num > b.num) ? 1 : -1 );
|
||||||
this.finishTurn();
|
this.finishTurn();
|
||||||
} else {
|
} else {
|
||||||
if (this._current_player.type === 'bot') {
|
if (this._current_player.type === 'bot') {
|
||||||
this.botTurn();
|
this.botTurn();
|
||||||
} else {
|
|
||||||
this.humanTurn();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,88 +137,61 @@ export default class Room extends BasicCanvas {
|
|||||||
async botTurn() {
|
async botTurn() {
|
||||||
await new Promise(resolve => setTimeout(resolve, 500));
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
|
||||||
const card = await( this._current_player.playCard(this._top_discard_pile) );
|
const card = await ( this.botChooseCard() );
|
||||||
if (card) {
|
|
||||||
console.log('played card num: ' + card.num + ', color: ' + card.color_n);
|
|
||||||
this.changeTopCard(card);
|
|
||||||
} else {
|
|
||||||
const card = this._cards.pop();
|
|
||||||
console.log('drawed card num: ' + card.num + ', color: ' + card.color_n);
|
|
||||||
this._current_player.addCard(card);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (card) {
|
||||||
|
this.playCard(card);
|
||||||
|
} else {
|
||||||
|
this.drawCard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
botChooseCard() {
|
||||||
|
for (let i=0; i<this._current_player.cards.length; i++) {
|
||||||
|
const card = this._current_player.cards[i];
|
||||||
|
if ( this._rule.checkCardsMatch(this._discard_pile_top, card) ) {
|
||||||
|
if (card.num >= 13) { // Change color
|
||||||
|
this.changeColor( Math.floor( Math.random() * 4 ) );
|
||||||
|
}
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
playCard(card) {
|
||||||
|
console.log('[Room] played card num: ' + card.num + ', color: ' + card.color_n);
|
||||||
|
const player = this._current_player;
|
||||||
|
// Remove card from player cards
|
||||||
|
player.cards.splice(player.cards.findIndex(c => (c.x === card.x && c.y === card.y)), 1);
|
||||||
|
|
||||||
|
this.changeTopCard(card);
|
||||||
this.finishTurn();
|
this.finishTurn();
|
||||||
}
|
}
|
||||||
|
|
||||||
humanTurn() {
|
changeColor(color_n) {
|
||||||
this._top_draw_card = this._cards[ this._cards.length-1 ];
|
this._wild = true;
|
||||||
this._top_draw_card.mouseEffect();
|
this._wild_color_n = color_n;
|
||||||
|
}
|
||||||
|
|
||||||
// Select card event
|
drawCard() {
|
||||||
this._current_player.cards.forEach( (card) => {
|
const card = this._remain_cards.pop();
|
||||||
if (this._top_discard_pile.isMatch(card)) {
|
console.log('[Room] drawed card num: ' + card.num + ', color: ' + card.color_n);
|
||||||
card.mouseEffect();
|
|
||||||
|
|
||||||
card.canvas.addEventListener('click', () => {
|
this._current_player.cards.push(card);
|
||||||
console.log('played card num: ' + card.num + ', color: ' + card.color_n);
|
this._current_player.cards.sort( (a, b) => (a.num > b.num) ? 1 : -1 );
|
||||||
this._current_player.removeCard(card);
|
this.finishTurn();
|
||||||
|
|
||||||
// Remove event listener
|
|
||||||
this._top_draw_card.resetEventListener();
|
|
||||||
this._current_player.cards.forEach( (card) => {
|
|
||||||
card.resetEventListener();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Show color change blocks
|
|
||||||
if (card.num === 13 || card.num === 14) {
|
|
||||||
const bc_colors = [];
|
|
||||||
for (let i=0; i<4; i++) {
|
|
||||||
const w = global.uno_game_w;
|
|
||||||
const bc = new BasicCanvas(w/2+w*i/16, global.uno_game_h*3/4, w/16, w/16);
|
|
||||||
bc.fillColor(i);
|
|
||||||
bc.canvas.addEventListener('click', () => {
|
|
||||||
bc_colors.forEach( bc_color => bc_color.remove() );
|
|
||||||
// Change card color
|
|
||||||
card.color_n = i;
|
|
||||||
// Process
|
|
||||||
this.changeTopCard(card);
|
|
||||||
this.finishTurn();
|
|
||||||
});
|
|
||||||
bc_colors.push(bc);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.changeTopCard(card);
|
|
||||||
this.finishTurn();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Draw card event
|
|
||||||
this._top_draw_card.canvas.addEventListener('click', () => {
|
|
||||||
const card = this._cards.pop();
|
|
||||||
console.log('drawed card num: ' + card.num + ', color: ' + card.color_n);
|
|
||||||
|
|
||||||
// Remove event listener
|
|
||||||
this._top_draw_card.resetEventListener();
|
|
||||||
this._current_player.cards.forEach( (card) => {
|
|
||||||
card.resetEventListener();
|
|
||||||
});
|
|
||||||
|
|
||||||
this._current_player.addCard(card);
|
|
||||||
this.finishTurn();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async finishTurn() {
|
async finishTurn() {
|
||||||
console.log('finish turn')
|
console.log('[Room] finish turn')
|
||||||
|
|
||||||
// re-deploy player's cards
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
await( this._current_player.reDeployCards() );
|
|
||||||
|
|
||||||
// Check empty
|
// Check empty
|
||||||
if (this._current_player.isEmpty()) {
|
if (this._current_player.cards.length === 0) {
|
||||||
console.log('player: ' + this._current_player.name + ' has no card left. Game end');
|
console.log('[Room] player: ' + this._current_player.name + ' has no card left. Game end');
|
||||||
|
this._game_status = 'finish';
|
||||||
} else {
|
} else {
|
||||||
this._turn_count++;
|
this._turn_count++;
|
||||||
await ( this.decideNextPlayer() );
|
await ( this.decideNextPlayer() );
|
||||||
@ -221,15 +219,18 @@ export default class Room extends BasicCanvas {
|
|||||||
}
|
}
|
||||||
|
|
||||||
changeTopCard(card) {
|
changeTopCard(card) {
|
||||||
if (this._top_discard_pile) this._used_cards.push(this._top_discard_pile);
|
console.log('[Room] change top card num: ' + card.num + ', color_n: ' + card.color_n)
|
||||||
this._top_discard_pile = card;
|
if (this._discard_pile_top.x) this._discard_piles.push(this._discard_pile_top);
|
||||||
this._top_discard_pile.drawImageFront(global.uno_game_w*8/16+this._turn_count, global.uno_game_h/2);
|
this._discard_pile_top = card;
|
||||||
this._top_discard_pile.refresh();
|
if (this._wild) {
|
||||||
|
this._wild = false;
|
||||||
|
this._discard_pile_top.color_n = this._wild_color_n;
|
||||||
|
}
|
||||||
this.treatCard();
|
this.treatCard();
|
||||||
}
|
}
|
||||||
|
|
||||||
async treatCard() {
|
async treatCard() {
|
||||||
switch (this._top_discard_pile.num) {
|
switch (this._discard_pile_top.num) {
|
||||||
case 10: // skip card
|
case 10: // skip card
|
||||||
this._skip = true;
|
this._skip = true;
|
||||||
console.log('skip');
|
console.log('skip');
|
||||||
@ -243,23 +244,16 @@ export default class Room extends BasicCanvas {
|
|||||||
console.log('next player draw 2 cards');
|
console.log('next player draw 2 cards');
|
||||||
break;
|
break;
|
||||||
case 13: // wild card (change color)
|
case 13: // wild card (change color)
|
||||||
await ( this.changeColor() );
|
console.log('change color: ' + this._discard_pile_top.color_n);
|
||||||
console.log('change color: ' + this._top_discard_pile.color_n);
|
|
||||||
break;
|
break;
|
||||||
case 14: // wild draw 4 card (change color)
|
case 14: // wild draw 4 card (change color)
|
||||||
this._draw4 = true;
|
this._draw4 = true;
|
||||||
await ( this.changeColor() );
|
|
||||||
console.log('next player draw 4 cards');
|
console.log('next player draw 4 cards');
|
||||||
console.log('change color: ' + this._top_discard_pile.color_n);
|
console.log('change color: ' + this._discard_pile_top.color_n);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changeColor() {
|
|
||||||
if (this._current_player.type === 'bot') this._top_discard_pile.color_n = this._current_player.changeColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/js/rule.js
Normal file
15
src/js/rule.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export default class Rule {
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
checkCardsMatch(card1, card2) {
|
||||||
|
if ( (card2.num <= 12 && card1.num === card2.num) // Normal card
|
||||||
|
|| (card2.num >= 13) // Change color card
|
||||||
|
|| (card2.color_n === card1.color_n) ) { // Color match
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
372
src/js/view.js
Normal file
372
src/js/view.js
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
// view.js
|
||||||
|
|
||||||
|
/* Class */
|
||||||
|
import BasicCanvas from './basic_canvas.js';
|
||||||
|
import Room from './room.js';
|
||||||
|
import Card from './card.js';
|
||||||
|
import Rule from './rule.js';
|
||||||
|
|
||||||
|
export default class View {
|
||||||
|
constructor() {
|
||||||
|
this._data = {};
|
||||||
|
this._rule = new Rule();
|
||||||
|
this._is_initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
showMainMenu() {
|
||||||
|
// Create div
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.classList.add('uno-game-div');
|
||||||
|
div.style.top = global.uno_game_h/4 + 'px';
|
||||||
|
global.uno_game_div.appendChild( div );
|
||||||
|
|
||||||
|
// Create UNO title
|
||||||
|
const title = document.createElement('h1');
|
||||||
|
title.innerHTML = 'UNO Game';
|
||||||
|
title.classList.add('uno-game-main-title');
|
||||||
|
div.appendChild( title );
|
||||||
|
|
||||||
|
// label for input name
|
||||||
|
const label_n = document.createElement('label');
|
||||||
|
label_n.innerHTML = 'Input My Name';
|
||||||
|
label_n.classList.add('uno-game-label');
|
||||||
|
div.appendChild( label_n );
|
||||||
|
|
||||||
|
// Input name
|
||||||
|
const input_n = document.createElement('input');
|
||||||
|
input_n.type = 'text';
|
||||||
|
input_n.value = (this._my_name) ? this._my_name : '';
|
||||||
|
input_n.className = 'uno-game-input';
|
||||||
|
div.appendChild( input_n );
|
||||||
|
|
||||||
|
// Create find room button
|
||||||
|
const fr_btn = document.createElement('button');
|
||||||
|
fr_btn.classList.add('uno-game-btn');
|
||||||
|
fr_btn.innerHTML = 'Find Room';
|
||||||
|
div.appendChild( fr_btn );
|
||||||
|
|
||||||
|
// Add event
|
||||||
|
//this._fr_btn.addEventListener("click", e => {
|
||||||
|
// this._my_name = input_n.value;
|
||||||
|
// this.clearMainMenu();
|
||||||
|
// this.findRoom();
|
||||||
|
// this._is_host = false;
|
||||||
|
//});
|
||||||
|
|
||||||
|
// Create create room button
|
||||||
|
const cr_btn = document.createElement('button');
|
||||||
|
cr_btn.classList.add('uno-game-btn');
|
||||||
|
cr_btn.innerHTML = 'Create Room';
|
||||||
|
div.appendChild( cr_btn );
|
||||||
|
|
||||||
|
// Add event
|
||||||
|
cr_btn.addEventListener("click", e => {
|
||||||
|
this._my_name = input_n.value;
|
||||||
|
div.remove();
|
||||||
|
this.showCreateRoomMenu();
|
||||||
|
this._is_host = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
showCreateRoomMenu() {
|
||||||
|
// Create div
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.classList.add('uno-game-div');
|
||||||
|
div.style.top = global.uno_game_h/4 + 'px';
|
||||||
|
global.uno_game_div.appendChild( div );
|
||||||
|
|
||||||
|
// Menu title
|
||||||
|
const title = document.createElement('h2');
|
||||||
|
title.innerHTML = 'Create Room';
|
||||||
|
title.classList.add('uno-game-menu-title');
|
||||||
|
div.appendChild( title );
|
||||||
|
|
||||||
|
// label for input room name
|
||||||
|
const label_rn = document.createElement('label');
|
||||||
|
label_rn.innerHTML = 'Input Room Name';
|
||||||
|
label_rn.classList.add('uno-game-label');
|
||||||
|
div.appendChild( label_rn );
|
||||||
|
|
||||||
|
// Input room name
|
||||||
|
const input_rn = document.createElement('input');
|
||||||
|
input_rn.type = 'text';
|
||||||
|
input_rn.className = 'uno-game-input';
|
||||||
|
div.appendChild( input_rn );
|
||||||
|
|
||||||
|
// Confirm button
|
||||||
|
const cfm_btn = document.createElement('button');
|
||||||
|
cfm_btn.classList.add('uno-game-btn');
|
||||||
|
cfm_btn.innerHTML = 'Confirm';
|
||||||
|
div.appendChild( cfm_btn );
|
||||||
|
|
||||||
|
cfm_btn.addEventListener('click', e => {
|
||||||
|
this._room = new Room( input_rn.value );
|
||||||
|
this.send({ctrl: 'addHuman', name: this._my_name});
|
||||||
|
this._data = JSON.parse( this._room.respondData() );
|
||||||
|
this.showRoom();
|
||||||
|
div.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Back button
|
||||||
|
const back_btn = document.createElement('button');
|
||||||
|
back_btn.classList.add('uno-game-btn');
|
||||||
|
back_btn.innerHTML = 'Back';
|
||||||
|
div.appendChild( back_btn );
|
||||||
|
|
||||||
|
back_btn.addEventListener('click', e => {
|
||||||
|
div.remove();
|
||||||
|
this.showMainMenu();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async showRoom() {
|
||||||
|
// Create div
|
||||||
|
this._div = document.createElement('div');
|
||||||
|
this._div.classList.add('uno-game-div');
|
||||||
|
this._div.style.top = global.uno_game_h/5 + 'px';
|
||||||
|
global.uno_game_div.appendChild( this._div );
|
||||||
|
|
||||||
|
// Menu title
|
||||||
|
const title = document.createElement('h2');
|
||||||
|
title.innerHTML = this._data.room.name;
|
||||||
|
title.classList.add('uno-game-menu-title');
|
||||||
|
this._div.appendChild( title );
|
||||||
|
|
||||||
|
if (this._is_host) {
|
||||||
|
// Add bot button
|
||||||
|
const ab_btn = document.createElement('button');
|
||||||
|
ab_btn.classList.add('uno-game-btn');
|
||||||
|
ab_btn.innerHTML = 'Add Bot';
|
||||||
|
this._div.appendChild( ab_btn );
|
||||||
|
|
||||||
|
ab_btn.addEventListener('click', e => {
|
||||||
|
this.send({ctrl: 'addBot'});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start game button
|
||||||
|
const sg_btn = document.createElement('button');
|
||||||
|
sg_btn.classList.add('uno-game-btn');
|
||||||
|
sg_btn.innerHTML = 'Star Game';
|
||||||
|
this._div.appendChild( sg_btn );
|
||||||
|
|
||||||
|
sg_btn.addEventListener('click', e => {
|
||||||
|
this.send({ctrl: 'startGame'});
|
||||||
|
this._div.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// List players
|
||||||
|
const ul_pl = document.createElement('ul');
|
||||||
|
ul_pl.classList.add('uno-game-ul');
|
||||||
|
this._div.appendChild( ul_pl );
|
||||||
|
|
||||||
|
this._data.players.forEach( (player) => {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.classList.add('uno-game-li');
|
||||||
|
li.innerHTML = player.name;
|
||||||
|
ul_pl.appendChild( li );
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!this._loop_started) {
|
||||||
|
this._loop_started = true;
|
||||||
|
this.loopReceiveData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async loopReceiveData() {
|
||||||
|
let is_finished = false;
|
||||||
|
while(true) {
|
||||||
|
const data = await ( JSON.parse( this._room.respondData() ) );
|
||||||
|
|
||||||
|
switch (data.game_status) {
|
||||||
|
case 'ready':
|
||||||
|
if ( JSON.stringify(this._data.players) != JSON.stringify(data.players) ) {
|
||||||
|
this._data = data;
|
||||||
|
this._div.remove();
|
||||||
|
this.showRoom();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'start':
|
||||||
|
if (!this._is_initialized) {
|
||||||
|
await ( this.initCards() );
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500)); // Sleep .5 s
|
||||||
|
this._is_initialized = true;
|
||||||
|
}
|
||||||
|
if ( JSON.stringify(this._data.current_player) != JSON.stringify(data.current_player) ) {
|
||||||
|
console.log('current_player changed')
|
||||||
|
this._data = data;
|
||||||
|
await ( this.updateCards() );
|
||||||
|
if (data.current_player.name === this._my_name) this.myTurn();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'finish':
|
||||||
|
this._data = data;
|
||||||
|
await ( this.updateCards() );
|
||||||
|
is_finished = true;
|
||||||
|
this.gameFinish();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (is_finished) break;
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500)); // Sleep .5 s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
send(data) {
|
||||||
|
this._room.receive(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async initCards() {
|
||||||
|
console.log('init cards')
|
||||||
|
this._cards = [];
|
||||||
|
let count = 0
|
||||||
|
for (let x=0; x<14; x++) {
|
||||||
|
this._cards[x] = [];
|
||||||
|
}
|
||||||
|
for (let x=0; x<14; x++) {
|
||||||
|
let num = x;
|
||||||
|
for (let y=0; y<8; y++) {
|
||||||
|
const color_n = y%4;
|
||||||
|
if ( (x === 0) && (y >= 4) ) { // Skip blank card
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( (x === 13) && (y >= 4) ) { // +4 cards
|
||||||
|
num = 14;
|
||||||
|
}
|
||||||
|
this._cards[x][y] = new Card(global.uno_game_w*6/16+count, global.uno_game_h/2, num, color_n);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCards() {
|
||||||
|
console.log('update cards view')
|
||||||
|
// Player Cards
|
||||||
|
const p_l = this._data.players.length;
|
||||||
|
let p_cnt = 0;
|
||||||
|
|
||||||
|
this._data.players.forEach( player => {
|
||||||
|
const c_l = player.cards.length;
|
||||||
|
|
||||||
|
for (let c=0; c<c_l; c++) {
|
||||||
|
const x = player.cards[c].x;
|
||||||
|
const y = player.cards[c].y;
|
||||||
|
|
||||||
|
if (player.name === this._my_name) {
|
||||||
|
const x0 = global.uno_game_w/4;
|
||||||
|
const dx = global.uno_game_w/2 / (c_l+1);
|
||||||
|
const y0 = global.uno_game_h*4/5;
|
||||||
|
this._cards[x][y].drawImageFront(x0 + dx*(c+1), y0);
|
||||||
|
} else {
|
||||||
|
const x0 = global.uno_game_w*p_cnt/(p_l-1);
|
||||||
|
const dx = global.uno_game_w/(p_l-1) / (c_l+2);
|
||||||
|
const y0 = this.ellipticalFormula(x0 + dx*(c+1), global.uno_game_w/2, global.uno_game_h);
|
||||||
|
this._cards[x][y].drawImageBack(x0 + dx*(c+1), y0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(player.name === this._my_name)) p_cnt++;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remain cards
|
||||||
|
for (let i=0; i<this._data.remain_cards.length; i++) {
|
||||||
|
const x = this._data.remain_cards[i].x;
|
||||||
|
const y = this._data.remain_cards[i].y;
|
||||||
|
this._cards[x][y].drawImageBack(global.uno_game_w*6/16+i, global.uno_game_h/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discard piles
|
||||||
|
for (let i=0; i<this._data.discard_piles.length; i++) {
|
||||||
|
const x = this._data.discard_piles[i].x;
|
||||||
|
const y = this._data.discard_piles[i].y;
|
||||||
|
this._cards[x][y].drawImageFront(global.uno_game_w*8/16+i, global.uno_game_h/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discard pile top
|
||||||
|
const discard_pile_top = this._cards[this._data.discard_pile_top.x][this._data.discard_pile_top.y];
|
||||||
|
discard_pile_top.drawImageFront(global.uno_game_w*8/16+this._data.discard_piles.length+1, global.uno_game_h/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
ellipticalFormula(x, a, b) {
|
||||||
|
return b * ( 1 - Math.sqrt( 1 - (x/a - 1)**2 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
myTurn() {
|
||||||
|
console.log('my turn');
|
||||||
|
|
||||||
|
const top_draw_card_data = this._data.remain_cards[ this._data.remain_cards.length-1 ];
|
||||||
|
this._top_draw_card = this._cards[top_draw_card_data.x][top_draw_card_data.y];
|
||||||
|
this._top_draw_card.mouseEffect();
|
||||||
|
|
||||||
|
// Select card event
|
||||||
|
const bc_colors = [];
|
||||||
|
this._data.current_player.cards.forEach( card_data => {
|
||||||
|
const card = this._cards[card_data.x][card_data.y];
|
||||||
|
if (this._rule.checkCardsMatch(this._data.discard_pile_top, card.data)) {
|
||||||
|
card.mouseEffect();
|
||||||
|
|
||||||
|
card.canvas.addEventListener('click', () => {
|
||||||
|
// Show color change blocks
|
||||||
|
if (card.num >= 13) {
|
||||||
|
for (let i=0; i<4; i++) {
|
||||||
|
const w = global.uno_game_w;
|
||||||
|
const bc = new BasicCanvas(w/2+w*i/16, global.uno_game_h*3/4, w/16, w/16);
|
||||||
|
bc.fillColor(i);
|
||||||
|
bc.canvas.addEventListener('click', () => {
|
||||||
|
console.log('played card num: ' + card.num + ', color: ' + card.color_n);
|
||||||
|
|
||||||
|
// Remove event listeners
|
||||||
|
this._top_draw_card.resetEventListener();
|
||||||
|
this._data.current_player.cards.forEach( (card_data) => {
|
||||||
|
const card = this._cards[card_data.x][card_data.y];
|
||||||
|
card.resetEventListener();
|
||||||
|
});
|
||||||
|
bc_colors.forEach( bc_color => bc_color.remove() );
|
||||||
|
|
||||||
|
this.send({ctrl: 'changeColor', color_n: i});
|
||||||
|
this.send({
|
||||||
|
ctrl: 'playCard',
|
||||||
|
card: {num: card.num, color_n: card.color_n, x: card_data.x, y: card_data.y}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
bc_colors.push(bc);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('played card num: ' + card.num + ', color_n: ' + card.color_n);
|
||||||
|
|
||||||
|
// Remove event listener
|
||||||
|
this._top_draw_card.resetEventListener();
|
||||||
|
this._data.current_player.cards.forEach( (card_data) => {
|
||||||
|
const card = this._cards[card_data.x][card_data.y];
|
||||||
|
card.resetEventListener();
|
||||||
|
});
|
||||||
|
bc_colors.forEach( bc_color => bc_color.remove() );
|
||||||
|
|
||||||
|
this.send({
|
||||||
|
ctrl: 'playCard',
|
||||||
|
card: {num: card.num, color_n: card.color_n, x: card_data.x, y: card_data.y}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Draw card event
|
||||||
|
this._top_draw_card.canvas.addEventListener('click', () => {
|
||||||
|
console.log('drawed card');
|
||||||
|
|
||||||
|
// Remove event listener
|
||||||
|
this._top_draw_card.resetEventListener();
|
||||||
|
this._data.current_player.cards.forEach( (card_data) => {
|
||||||
|
const card = this._cards[card_data.x][card_data.y];
|
||||||
|
card.resetEventListener();
|
||||||
|
});
|
||||||
|
bc_colors.forEach( bc_color => bc_color.remove() );
|
||||||
|
|
||||||
|
this.send({ctrl: 'drawCard'});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
gameFinish() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,9 @@
|
|||||||
.uno-game-div {
|
.uno-game-main-div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Common */
|
/* Common */
|
||||||
@ -10,15 +12,16 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Main view */
|
/* View */
|
||||||
.uno-game-title {
|
// Title
|
||||||
position: absolute;
|
.uno-game-main-title {
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|
||||||
font-family: 'Waiting for the Sunrise', cursive;
|
font-family: 'Waiting for the Sunrise', cursive;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
font-size: calc(16vh);
|
||||||
|
|
||||||
overflow: hidden; /* Ensures the content is not revealed until the animation */
|
overflow: hidden; /* Ensures the content is not revealed until the animation */
|
||||||
border-right: .15em solid orange; /* The typwriter cursor */
|
border-right: .15em solid orange; /* The typwriter cursor */
|
||||||
@ -39,6 +42,25 @@
|
|||||||
from, to { border-color: transparent }
|
from, to { border-color: transparent }
|
||||||
50% { border-color: orange; }
|
50% { border-color: orange; }
|
||||||
}
|
}
|
||||||
|
.uno-game-menu-title {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
font-family: 'Waiting for the Sunrise', cursive;
|
||||||
|
text-align: center;
|
||||||
|
font-size: calc(14vh);
|
||||||
|
|
||||||
|
overflow: hidden; /* Ensures the content is not revealed until the animation */
|
||||||
|
white-space: nowrap; /* Keeps the content on a single line */
|
||||||
|
margin: 0 auto; /* Gives that scrolling effect as the typing happens */
|
||||||
|
}
|
||||||
|
// Div
|
||||||
|
.uno-game-div {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
// Button
|
||||||
.uno-game-btn-div {
|
.uno-game-btn-div {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -53,8 +75,36 @@
|
|||||||
color: white;
|
color: white;
|
||||||
cursor: pointer; /* Pointer/hand icon */
|
cursor: pointer; /* Pointer/hand icon */
|
||||||
display: block; /* Make the buttons appear below each other */
|
display: block; /* Make the buttons appear below each other */
|
||||||
|
font-size: calc(6vh);
|
||||||
}
|
}
|
||||||
.uno-game-btn:hover {
|
.uno-game-btn:hover {
|
||||||
background-color: olive;
|
background-color: olive;
|
||||||
}
|
}
|
||||||
|
// Label
|
||||||
|
.uno-game-label {
|
||||||
|
padding: 15px 32px;
|
||||||
|
margin: auto;
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
font-size: calc(6vh);
|
||||||
|
}
|
||||||
|
// Input
|
||||||
|
.uno-game-input {
|
||||||
|
padding: 15px 32px;
|
||||||
|
margin: auto;
|
||||||
|
display: block;
|
||||||
|
font-size: calc(6vh);
|
||||||
|
}
|
||||||
|
// UL
|
||||||
|
.uno-game-ul {
|
||||||
|
padding: 15px 32px;
|
||||||
|
margin: auto;
|
||||||
|
//display: block;
|
||||||
|
//font-size: calc(6vh);
|
||||||
|
}
|
||||||
|
.uno-game-li {
|
||||||
|
padding: 15px 32px;
|
||||||
|
margin: auto;
|
||||||
|
display: block;
|
||||||
|
font-size: calc(4vh);
|
||||||
|
}
|
||||||
|
|||||||
58
uno_game.js
58
uno_game.js
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user