diff --git a/README.txt b/README.txt index 606bb0d..a2a70e0 100644 --- a/README.txt +++ b/README.txt @@ -9,23 +9,26 @@ Any movement, including initial locations, is represented by pushing or popping A victory state can be stored by taking a snapshot of the current stack. -## Icons +## Credit Icons from [https://game-icons.net](https://game-icons.net) ## TODO - win declare - replay stack -- countdown skip -- Extract loader from join.css and kill join.css, join.js +- countdown skip - slide arrows - chat box - no cancel from name prompt -- restore state on join +- clear out the trash bins of history (client) +- handle conn interrupt +- donate link heart fill in not underline +- Robots not resizing (shadows do though) - limit concurrent players, make sure connections are closed - move websocket server to /core - dynamic socket server resolution - walls and winstate algorithm +- restore name prompt - tutorial - donate link diff --git a/assets/gas-giant.png b/assets/gas-giant.png new file mode 100644 index 0000000..f6a4551 Binary files /dev/null and b/assets/gas-giant.png differ diff --git a/assets/stars.png b/assets/stars.png new file mode 100644 index 0000000..9fdb59c Binary files /dev/null and b/assets/stars.png differ diff --git a/client/connection.js b/client/connection.js index a877f8b..7bf511d 100644 --- a/client/connection.js +++ b/client/connection.js @@ -39,7 +39,8 @@ Connection.prototype.connect = function(){ const names = ["Biff", "Morty", "Herb", "Chester", "Lyle", "Cap", "Dale", "Ned", "Mindy", "Frankie", "Gabriel", "Mona", "Dolores", "Sepulveda", "Venus", "Blingbing", "Cyrpt"] const r = Math.floor(Math.random() * names.length); - const rawInput = names[r] //prompt("What is your name?"); + const rawInput = names[r] + // const rawInput = prompt("What is your name?"); this.ws = new WebSocket('ws://localhost:8080/ricochet?name=' + rawInput); @@ -74,14 +75,15 @@ Connection.prototype.onReceiveMessage = function({ data }) { let eventName; switch (msg.type) { - case 'connected': eventName = 'G-connected'; break; case 'countdown': eventName = 'G-countdown'; break; + case 'connected': eventName = 'G-connected'; break; case 'guess': eventName = 'G-guess'; break; case 'players': eventName = 'G-players'; break; case 'robots': eventName = 'G-robots'; break; case 'skip': eventName = 'G-skip'; break; case 'solve': eventName = 'G-solve'; break; case 'start': eventName = 'G-start'; break; + case 'state': eventName = 'G-state'; break; case 'stop': eventName = 'G-stop'; break; case 'walls': eventName = 'G-walls'; break; case 'winstate': eventName = 'G-winstate'; break; diff --git a/client/controls.js b/client/controls.js index 9f3ba5d..d7ffb14 100644 --- a/client/controls.js +++ b/client/controls.js @@ -4,35 +4,35 @@ const Controls = function() { - const evt = new Event('L-join'); - document.dispatchEvent(evt); - - // this.starts = []; // this.timers = {}; // // "Local" and "Global" messages -// document.addEventListener('L-connected', this.msgConnected.bind(this)); + document.addEventListener('L-conn-error', this.msgConnectionError.bind(this)); document.addEventListener('L-stack', this.msgStack.bind(this)); // document.addEventListener('L-reset', this.msgReset.bind(this)); // document.addEventListener('L-undo', this.msgUndo.bind(this)); // document.addEventListener('G-guess', this.msgGuess.bind(this)); - document.addEventListener('G-countdown', this.msgCountdown.bind(this)); + document.addEventListener('G-connected', this.msgCountdown.bind(this)); + document.addEventListener('G-countdown', this.msgConnected.bind(this)); document.addEventListener('G-players', this.msgPlayers.bind(this)); - document.addEventListener('G-skip', this.msgSkip.bind(this)); - document.addEventListener('G-start', this.msgStart.bind(this)); - document.addEventListener('G-stop', this.msgStop.bind(this)); + // document.addEventListener('G-skip', this.msgSkip.bind(this)); + // document.addEventListener('G-start', this.msgStart.bind(this)); + document.addEventListener('G-state', this.msgState.bind(this)); + // document.addEventListener('G-stop', this.msgStop.bind(this)); // Click handlers - document.getElementById('controls-reset').addEventListener('click', this.onClickReset.bind(this)); - document.getElementById('controls-robots').addEventListener('click', this.onClickRobots.bind(this)); - document.getElementById('controls-skip').addEventListener('click', this.onClickSkip.bind(this)); - document.getElementById('controls-start').addEventListener('click', this.onClickStart.bind(this)); - document.getElementById('controls-stop').addEventListener('click', this.onClickStop.bind(this)); - document.getElementById('controls-submit').addEventListener('click', this.onClickSubmit.bind(this)); - document.getElementById('controls-undo').addEventListener('click', this.onClickUndo.bind(this)); - document.getElementById('controls-walls').addEventListener('click', this.onClickWalls.bind(this)); + // document.getElementById('controls-reset').addEventListener('click', this.onClickReset.bind(this)); + // document.getElementById('controls-robots').addEventListener('click', this.onClickRobots.bind(this)); + // document.getElementById('controls-skip').addEventListener('click', this.onClickSkip.bind(this)); + // document.getElementById('controls-start').addEventListener('click', this.onClickStart.bind(this)); + // document.getElementById('controls-stop').addEventListener('click', this.onClickStop.bind(this)); + // document.getElementById('controls-submit').addEventListener('click', this.onClickSubmit.bind(this)); + // document.getElementById('controls-undo').addEventListener('click', this.onClickUndo.bind(this)); + // document.getElementById('controls-walls').addEventListener('click', this.onClickWalls.bind(this)); + + this.setState('CONNECTING'); } //===== Countdown @@ -84,62 +84,81 @@ const Controls = function() { // document.getElementById('controls-panic').style.display = ''; // }; +Controls.prototype.setState = function(state) { + const blocks = [ + 'controls-connection', + 'controls-countdown', + 'controls-moves', + 'controls-options', + 'controls-players', + 'controls-solution', + ]; + + blocks.forEach(id => document.getElementById(id).style.display = 'none'); + + // IDs to show for each state. + const STATE = { + CONNECTING: ['controls-connection'], + PLAY: ['controls-players', 'controls-moves', 'controls-options'], + COUNTDOWN: ['controls-players', 'controls-moves', 'controls-countdown'], + SOLUTION: ['controls-players', 'controls-solution'] + }; + + (STATE[state] || []).forEach(id => document.getElementById(id).style.display = 'block'); +}; + //===== Message handlers +Controls.prototype.msgConnected = function() { + +}; + +Controls.prototype.msgConnectionError = function() { + this.setState('CONNECTING'); +}; + Controls.prototype.msgCountdown = function(evt) { - // this.showWaiting(); - console.error(evt); - alert("COUNTDOWN RECEIVED"); + }; Controls.prototype.msgStack = function(evt) { const robots = evt.detail.reduce((acc, { id }) => acc.has(id) ? acc : acc.add(id), new Set()); const moveCount = evt.detail.length - robots.size; - document.getElementById('controls-moves').innerHTML = moveCount; - document.getElementById('controls-undo').style.display = moveCount > 0 ? 'block' : 'none'; + // document.getElementById('controls-moves').innerHTML = moveCount; + // document.getElementById('controls-undo').style.display = moveCount > 0 ? 'block' : 'none'; }; Controls.prototype.msgPlayers = function(evt) { - const container = document.getElementById('controls-players'); - const names = evt.detail.body; - const keys = Object.keys(names); - - if (keys.length > 0) { - const nobody = document.getElementById('controls-players-nobody'); - nobody && nobody.parentNode.removeChild(nobody); - } + this.names = evt.detail.body; - keys.forEach(connectionId => { - const id = `player-${connectionId}`; - - if (document.getElementById(id)) { - return; - } - - const n = document.createElement('div'); - n.id = id; - n.innerHTML = names[connectionId]; - n.className = 'controls-player'; - container.appendChild(n) + const container = document.getElementById('controls-players'); + container.querySelectorAll('.controls-player').forEach(el => { + container.removeChild(el); }); - this.names = names; + const keys = Object.keys(this.names); + if (keys.length > 1) { + keys.forEach(connectionId => { + const n = document.createElement('div'); + n.innerHTML = this.names[connectionId]; + n.className = 'controls-player'; + container.appendChild(n) + }); + } - container.querySelectorAll('.controls-player').forEach(el => { - const id = el.id.split('player-').pop(); - if (!this.names[id]) { - container.removeChild(el); - } - }); + const msg = container.querySelector('.controls-message'); + msg.innerHTML = (keys.length > 1 ? '' : 'Nobody is in the game yet.'); + msg.style.display = (keys.length > 1 ? 'none' : 'block'); }; Controls.prototype.msgSkip = function() { // this.coundownComplete(); }; -Controls.prototype.msgStart = function() { - // +Controls.prototype.msgState = function(evt) { + // this.setState(evt.detail.body); + this.setState('SOLUTION') }; Controls.prototype.msgStop = function() { diff --git a/client/grid.js b/client/grid.js index c716211..bec1af2 100644 --- a/client/grid.js +++ b/client/grid.js @@ -432,35 +432,3 @@ Grid.prototype.msgWinState = function(evt) { this.winstate = evt.detail.body; this.drawWinState(); }; - -//============ THE TRASH BIN OF HISTORY -// Content.prototype.drawRobot = function({ id, i, j }) { -// const robot = document.getElementById(`robot-${id}`); -// const arrows = document.getElementById(`arrows-${id}`); - -// const { x, y } = this.ijToXy({ i, j }); -// const s = this.squares.sideLength; - -// robot.style.display = 'block'; -// robot.style.left = x + 'px'; -// robot.style.top = y + 'px'; -// robot.dataset.i = i; -// robot.dataset.j = j; - -// this.robots[`${i}-${j}`] = id; - -// arrows.style.display = 'block'; -// arrows.style.left = (x - s) + 'px'; -// arrows.style.top = (y - s) + 'px'; -// }; - -// Content.prototype.ijToXy = function({ i, j }) { -// if ((i !== 0 && !i) || (j !== 0 && !j)) { -// return -// } - -// return { -// x: this.squares.x0 + i * this.squares.sideLength, -// y: this.squares.y0 + j * this.squares.sideLength -// } -// }; \ No newline at end of file diff --git a/client/join.js b/client/join.js deleted file mode 100644 index 6167789..0000000 --- a/client/join.js +++ /dev/null @@ -1,52 +0,0 @@ -const Join = function() { - document.addEventListener('L-conn-error', this.onConnectionError.bind(this)); - document.addEventListener('L-conn-open', this.onConnectionOpen.bind(this)); - - document.getElementById('join-setup-start').addEventListener('click', this.onClickStart.bind(this)); - document.getElementById('join-setup-go').addEventListener('click', this.onClickStart.bind(this)); - document.getElementById('join-setup-back').addEventListener('click', this.onClickBack.bind(this)); - - this.showSetup(); -}; - -Join.prototype.showSetup = function() { - document.getElementById('join-setup').style.display = 'block'; - document.getElementById('join-connect').style.display = 'none'; - document.getElementById('join-error').style.display = 'none'; -}; - -Join.prototype.showLoad = function() { - document.getElementById('join-setup').style.display = 'none'; - document.getElementById('join-connect').style.display = 'block'; - document.getElementById('join-error').style.display = 'none'; -}; - -Join.prototype.showError = function() { - document.getElementById('join-setup').style.display = 'none'; - document.getElementById('join-connect').style.display = 'none'; - document.getElementById('join-error').style.display = 'block'; -}; - -Join.prototype.onClickBack = function() { - this.showSetup(); -}; - -Join.prototype.onClickGo = function() { - this.showLoad(); -}; - -Join.prototype.onClickStart = function() { - this.showLoad(); - - const evt = new Event('L-join'); - document.dispatchEvent(evt); -}; - -Join.prototype.onConnectionError = function() { - this.showError(); -}; - -Join.prototype.onConnectionOpen = function() { - document.getElementById('join').style.display = 'none'; - this.showSetup(); -}; \ No newline at end of file diff --git a/controls.css b/controls.css deleted file mode 100644 index 3d48329..0000000 --- a/controls.css +++ /dev/null @@ -1,100 +0,0 @@ -#controls-container { - background: #e7e7e7; - border: solid #e7e7e7; - border-width: 0 10px; - bottom: 0; - left: 20px; - position: absolute; - top: 0; - width: 300px; - z-index: 1; -} - -.controls-title { - background-color: #639978; - background-image: url('sprite-robots.png'); - /*color: #fff;*/ - font-size: 12px; - line-height: 48px; - margin-bottom: 24px; - text-align: center; -} - -.controls-subtitle { - background-color: #4D3243; - background-image: linear-gradient(90deg, #4D3243, #3C2132); - color: #fff; - padding: 12px; - margin: 24px 0 12px 0; -} - -.controls-alert-info { - background: #21dfae; - color: #222; - padding: 12px; - margin: 24px 0 12px 0; -} - -.controls-alert-urgent { - background: #DE4F21; - color: #fff; - padding: 12px; - margin: 24px 0 12px 0; -} - -.controls-row { - align-items: center; - display: flex; - flex-direction: row; - padding: 8px; -} - -.controls-row :nth-child(2) { - flex: 1; - margin: 0 8px; - width: 100%; -} - -.controls-player { - padding: 8px; -} - -#controls-footer { - bottom: 0; - left: 0; - padding: 24px; - position: absolute; - right: 0; - text-align: center; -} - -#controls-footer a { - color: #639699; - font-size: 24px; - text-decoration: none; -} - -#controls-footer a:hover { - text-decoration: underline; -} - -.controls-button { - background: none; - border: 1px solid #888; - color: #444; - cursor: pointer; - font-size: 12px; - padding: 4px 8px; -} - -.controls-button:hover { - background: #21dfae; - border-color: #21dfae; - color: #222; -} - -#controls-start { - display: block; - margin: 0 auto; - padding: 4px 8px; -} \ No newline at end of file diff --git a/index.css b/index.css deleted file mode 100644 index 07e9fe9..0000000 --- a/index.css +++ /dev/null @@ -1,16 +0,0 @@ -* { - box-sizing: border-box; - font-family: Montserrat; -} - -body { - overflow: hidden; -} - -/* -PALETTE: -21DFAE Aquamarine -4D3243 Plum -DE4F21 Fire -639699 Cadet -*/ diff --git a/index.html b/index.html index fd6faca..df72504 100644 --- a/index.html +++ b/index.html @@ -1,115 +1,92 @@ - - - - Robots - - - - - - - - - - - - - - -
-
-
Global
+
+
Undo
+
Reset
+
-
-
Start Next Round
-
 
-
Do It
+
Solve the puzzle
-
-
Stop This Round
-
 
-
Yeah
+
+
Options
+
Move Robots
+
Move Walls
-
-
Move Robots
-
 
-
Reposition
+
+
Connecting...
+
Can't reach the server.
-
-
Move Walls
-
 
-
Regenerate
-
-
+
+
Aardvark Matthews Dunkirk has a solution!
+ +
+
11
+
19
+
-
-
Players
+
Skip
+
-
Nobody is in the game yet.
-
- +
+
Round over
+
Congrats Aardvark Matthews Dunkirk!
-
-
Local
-
-
Moves:
-
-
Undo
-
Reset
-
+
24
- +
Replay
-
-
+
Start next round
+
-
-
Countdown:
-
30 seconds!
-
Skip
-
+
- -
-
-
+ - + // Entry point. + const evt = new Event('L-join'); + document.dispatchEvent(evt); + }) + + \ No newline at end of file diff --git a/join.css b/join.css deleted file mode 100644 index 85f03dd..0000000 --- a/join.css +++ /dev/null @@ -1,102 +0,0 @@ -#join { - background-color: #28313b; - background-image: linear-gradient(315deg, #28313b 0%, #1A2026 74%); - bottom: 0; - color: #e7e7e7; - display: flex; - flex-direction: column; - justify-content: center; - left: 0; - position: absolute; - right: 0; - text-align: center; - top: 0; - z-index: 10; -} - -.join-button { - background: none; - border: 1px solid #888; - color: #ccc; - cursor: pointer; - font-size: 32px; - height: 72px; - line-height: 72px; - vertical-align: middle; -} - -.join-button:hover { - background: #21dfae; - border-color: #21dfae; - color: #222; -} - -.join-message { - font-size: 24px; - line-height: 72px; -} - -#join-setup-start, -#join-setup-back { - width: 500px; -} - -#join-setup-go { - width: 96px; -} - -#join-setup .join-message { - margin: 24px 0; -} - -#join-setup input { - border: 0; - font-size: 32px; - height: 72px; - line-height: 72px; - margin-right: 40px; - padding: 0 24px; - vertical-align: middle; - width: 360px; -} - -@keyframes LoadingBackground { - 0%{background-position:0% 50%} - 50%{background-position:100% 50%} - 100%{background-position:0% 50%} -} - -#join-connect { - animation: LoadingBackground 8s infinite; - background-size: 230% 230%; - background-image: linear-gradient(90deg, #21DFAE 600px, #FFF 600px, #FFF 605px, #21DFAE 605px); - color: #222; - display: none; - font-size: 32px; - height: 72px; - margin: 0 auto; - text-align: center; - width: 500px; -} - -#join-error { - display: none; - font-size: 32px; - height: 264px; - line-height: 264px; - margin: 0 auto; - position: relative; - text-align: center; - width: 500px; -} - -#join-error .join-message { - background-color: #DE4F21; - color: #fff; -} - -#join-error button { - position: absolute; - bottom: 0; - left: 0; -} diff --git a/server/ricochet.js b/server/ricochet.js index 1dec405..df60f41 100644 --- a/server/ricochet.js +++ b/server/ricochet.js @@ -1,39 +1,44 @@ const uuid = require('node-uuid'); +const UrlParser = require('url'); const DEBUG = (process.env.NODE_ENV !== "production"); +const STATE = { + COUNTDOWN: 'COUNTDOWN', + PLAY: 'PLAY', + SOLUTION: 'SOLUTION' +}; + const Ricochet = function({ messenger }) { this.messenger = messenger; + this.squaresPerSide = 20; + + // Properties that will be emitted this.players = {}; + this.robots = this.freshRobots(); + this.state = STATE.PLAY; + this.walls = this.freshWalls(); // this.id = uuid.v4(); // this.countdownTimer = null; // this.guess = Infinity; - // this.squaresPerSide = 20; // this.winningStack; - - // const robots = ['#E00000', '#00C000', '#0000FF', '#00C0C0', '#F000F0']; - // const icons = ['assets/comet.svg', 'assets/moon.svg', 'assets/planet.svg', 'assets/rocket.svg', 'assets/spacesuit.svg']; - // // spider.svg, ufo.svg - - // const gen = () => Math.floor(Math.random() * this.squaresPerSide); - // this.TEMP_ROBOTS = robots.map((color, idx) => ({ i: gen(), j: gen(), color, id: uuid.v4(), icon: icons[idx] })); }; Ricochet.prototype.onConnect = function(ws, req) { - // const query = url.query; - // const santizedName = (query.name || 'Unknown').replace(/[^\w ]/g, ''); - - // DEBUG && console.log("Connected:"); - // DEBUG && console.log (`${santizedName} ${ws.id} via ${req.url}`); + const url = UrlParser.parse(req.url, true); + const query = url.query; + const santizedName = (query.name || 'Unknown').replace(/[^\w ]/g, ''); - // this.addPlayer(ws.id, santizedName); + DEBUG && console.log(`Connected: ${santizedName} (${ws.id.substr(0, 8)}) via ${req.url}`); - // this.messenger.messageAll({ type: 'players', body: this.players }); + this.addPlayer(ws.id, santizedName); - // this.messenger.messageOne(ws, { type: 'walls', body: G.getWalls()}); - // this.messenger.messageOne(ws, { type: 'robots', body: G.getRobots()}); + this.messenger.messageAll({ type: 'players', body: this.players }); + this.messenger.messageOne(ws, { type: 'robots', body: this.robots}); + this.messenger.messageOne(ws, { type: 'connected', body: ws.id}); + this.messenger.messageOne(ws, { type: 'state', body: this.state}); + this.messenger.messageOne(ws, { type: 'walls', body: this.walls}); // this.messenger.messageOne(ws, { type: 'winstate', body: G.getWinState()}); - // this.messenger.messageOne(ws, { type: 'connected', body: ws.id}); } Ricochet.prototype.onMessage = function(ws, json) { @@ -41,12 +46,11 @@ Ricochet.prototype.onMessage = function(ws, json) { }; Ricochet.prototype.onDisconnect = function(ws) { - DEBUG && console.log("Disconnected:"); - DEBUG && console.log(ws.id); + DEBUG && console.log(`Disconnected: ${this.players[ws.id]} (${ws.id.substr(0, 8)})`); this.removePlayer(ws.id); - // this.messenger.messageAll({ type: 'players', body: this.players }); + this.messenger.messageAll({ type: 'players', body: this.players }); }; Ricochet.prototype.addPlayer = function(id, name) { @@ -60,65 +64,70 @@ Ricochet.prototype.removePlayer = function(id) { delete this.players[id]; }; -// Game.prototype.getRobots = function() { -// return this.TEMP_ROBOTS; -// } - -// Game.prototype.getWalls = function() { -// // Edge IDs are of the form [i1-j1-i2-j2]. Top left is 0, 0. +Ricochet.prototype.freshRobots = function() { + const robots = ['#E00000', '#00C000', '#0000FF', '#00C0C0', '#F000F0']; + const icons = ['assets/comet.svg', 'assets/moon.svg', 'assets/planet.svg', 'assets/rocket.svg', 'assets/spacesuit.svg']; + // // spider.svg, ufo.svg -// // Leave here for testing. -// // return [ -// // "1-9-1-10", -// // "9-1-10-1", -// // "9-19-10-19", -// // "19-9-19-10" -// // ]; - -// // console.log("Generating walls."); - -// // Squares per side has quadratic relationship with wall/corner requirements. -// const numberOfCorners = Math.ceil(Math.pow((this.squaresPerSide / 10), 2)); -// const numberOfWalls = Math.ceil(Math.pow((this.squaresPerSide / 5), 2)); - -// const gen = () => Math.floor(Math.random() * this.squaresPerSide); -// const edges = []; - -// // DO NUMBER OF CORNERS FIRST AFTER TESTING -// for (let n = 0; n < numberOfWalls; n++) { -// const ri = gen(); -// const rj = gen(); - -// const isHorizontal = Math.random() < 0.5; -// const isBackward = Math.random() < 0.5; - -// let i1, j1, i2, j2; - -// if (isHorizontal) { -// i1 = isBackward ? ri - 1 : ri; -// i2 = isBackward ? ri : ri + 1; - -// j1 = rj; -// j2 = rj; -// } else { -// i1 = ri; -// i2 = ri; - -// j1 = isBackward ? rj - 1 : rj; -// j2 = isBackward ? rj : rj + 1; -// } - -// const edge = `${i1}-${j1}-${i2}-${j2}`; + const gen = () => Math.floor(Math.random() * this.squaresPerSide); + return robots.map((color, idx) => ({ i: gen(), j: gen(), color, id: uuid.v4(), icon: icons[idx] })); +}; -// if (edges.includes(edge)) { -// n--; -// } else { -// edges.push(edge); -// } -// } +Ricochet.prototype.freshWalls = function() { + // Edge IDs are of the form [i1-j1-i2-j2]. Top left is 0, 0. + + // Leave here for testing. + // return [ + // "1-9-1-10", + // "9-1-10-1", + // "9-19-10-19", + // "19-9-19-10" + // ]; + + // console.log("Generating walls."); + + // Squares per side has quadratic relationship with wall/corner requirements. + const numberOfCorners = Math.ceil(Math.pow((this.squaresPerSide / 10), 2)); + const numberOfWalls = Math.ceil(Math.pow((this.squaresPerSide / 5), 2)); + + const gen = () => Math.floor(Math.random() * this.squaresPerSide); + const edges = []; + + // DO NUMBER OF CORNERS FIRST AFTER TESTING + for (let n = 0; n < numberOfWalls; n++) { + const ri = gen(); + const rj = gen(); + + const isHorizontal = Math.random() < 0.5; + const isBackward = Math.random() < 0.5; + + let i1, j1, i2, j2; + + if (isHorizontal) { + i1 = isBackward ? ri - 1 : ri; + i2 = isBackward ? ri : ri + 1; + + j1 = rj; + j2 = rj; + } else { + i1 = ri; + i2 = ri; + + j1 = isBackward ? rj - 1 : rj; + j2 = isBackward ? rj : rj + 1; + } + + const edge = `${i1}-${j1}-${i2}-${j2}`; + + if (edges.includes(edge)) { + n--; + } else { + edges.push(edge); + } + } -// return edges; -// }; + return edges; +}; // Game.prototype.getWinState = function() { // // const gen = () => Math.floor(Math.random() * this.squaresPerSide); diff --git a/socket/messenger.js b/socket/messenger.js index 79a0ce4..a1fa392 100644 --- a/socket/messenger.js +++ b/socket/messenger.js @@ -24,10 +24,12 @@ Messenger.prototype.messageOthers = function(ws, message) { }; Messenger.prototype.messageAll = function(message) { - DEBUG && console.log(`Sending to all ${wss.clients.size} client(s):`); + const clients = Object.values(this.clients); + + DEBUG && console.log(`Sending to all ${clients.length} client(s):`); DEBUG && console.log(message); - Object.values(this.clients).forEach((client) => { + clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.send(JSON.stringify(message)); } diff --git a/style/controls.css b/style/controls.css new file mode 100644 index 0000000..68f8690 --- /dev/null +++ b/style/controls.css @@ -0,0 +1,198 @@ +#controls-container { + bottom: 0; + left: 20px; + padding: 40px 0; + position: absolute; + top: 0; + width: 300px; + z-index: 1; +} + +.controls-block { + background: #23074d; + border: 6px solid #483c6c; + border-radius: 0 18px 0 18px; + color: #0cffe1; + font-size: 24px; + margin-bottom: 24px; + overflow: hidden; + padding-bottom: 24px; + position: relative; +} + +.controls-title { + background: #0cffe1; + color: #ff217c; + font-size: 14px; + margin-bottom: 16px; + padding: 8px 24px; +} + +.controls-message { + cursor: default; + font-size: 16px; + padding: 24px; +} + +.controls-button { + cursor: pointer; + padding: 8px 0; + text-align: center; +} + +.controls-button:hover { + background: yellow; + color: #23074d; +} + +/*===== PLAYERS BLOCK =====*/ +#controls-players { +} + +.controls-player { + cursor: default; + padding: 8px 24px; +} + +/*===== MOVES BLOCK =====*/ +#controls-moves { + background: #00dfd4; + border: 6px solid #483c6c; + border-radius: 0 18px 0 18px; + color: #ff217c; + position: relative; +} + +#controls-moves-count { + cursor: default; + font-size: 84px; + text-align: center; +} + +.controls-moves-buttons-layout { + display: flex; + flex-direction: row; +} + +#controls-reset, +#controls-undo { + flex: 1; + font-size: 12px; + padding: 12px; +} + +#controls-submit { + font-size: 24px; + padding: 8px 0; +} + +/*===== OPTIONS BLOCK =====*/ +#controls-options { +} + +#controls-options .controls-button { + font-size: 16px; + padding: 8px 24px; +} + +/*===== COUNTDOWN BLOCK =====*/ +#controls-countdown { + background: #F96600; + border-color: #ffec83; + color: #ffff00; + cursor: default; +} + +#controls-countdown .controls-title { + background: #CA1800; + color: #ffec83; +} + +.controls-countdown-values-layout { + display: flex; + flex-direction: row; +} + +#controls-countdown-value { + cursor: default; + flex: 1; + font-size: 64px; + text-align: center; +} + +#controls-countdown-value::after { + content: 'seconds'; + display: block; + font-size: 12px; + margin-top: -12px; +} + +#controls-solution-value { + cursor: default; + flex: 1; + font-size: 64px; + text-align: center; +} + +#controls-solution-value::after { + content: 'moves'; + display: block; + font-size: 12px; + margin-top: -12px; +} + +#controls-skip { + margin-top: 16px; +} + +/*===== SOLUTION BLOCK =====*/ +#controls-solution { + background: #483c6c; + border-width: 0; + color: #F96600; + text-align: center; +} + +#controls-solution-message { + color: #ffec83; + padding-bottom: 0; +} + +/* +0cffe1 CYAN 00dfd4 +ff217c PINK +483c6c PURPLE 23074d +fe5e78 SALMON +ffa283 PEACH +F96600 ORANGE +ffec83 YELLOW FFFF00 +*/ + +#controls-solution-count { + color: #ffec83; + cursor: default; + font-size: 84px; +} + +#controls-solution-replay { +} + +/*===== FOOTER BLOCK =====*/ +#controls-footer { + bottom: 0; + left: 0; + padding: 24px; + position: absolute; + right: 0; + text-align: center; +} + +#controls-footer a { + color: #639699; + font-size: 24px; + text-decoration: none; +} + +#controls-footer a:hover { + text-decoration: underline; +} \ No newline at end of file diff --git a/grid.css b/style/grid.css similarity index 92% rename from grid.css rename to style/grid.css index c94dc72..1e4c9fe 100644 --- a/grid.css +++ b/style/grid.css @@ -1,6 +1,4 @@ #content-container { - background-color: #28313b; - background-image: linear-gradient(315deg, #28313b 0%, #1A2026 74%); bottom: 0; left: 0; position: absolute; diff --git a/style/index.css b/style/index.css new file mode 100644 index 0000000..90484a1 --- /dev/null +++ b/style/index.css @@ -0,0 +1,26 @@ +* { + box-sizing: border-box; + font-family: Days One; +} + +body { + animation: MovingBackground 600s infinite; + background-image: url('../assets/stars.png'); + overflow: hidden; +} + +@keyframes MovingBackground { + 0%{background-position:0% 50%} + 50%{background-position:100% 50%} + 100%{background-position:0% 50%} +} + +/* +0cffe1 CYAN 00dfd4 +ff217c PINK +483c6c PURPLE 23074d +fe5e78 SALMON +ffa283 PEACH +F96600 ORANGE +ffec83 YELLOW FFFF00 +*/ \ No newline at end of file