UI overhaul. Centralized game state.

master
Ben Burlingham 5 years ago
parent e8d1fec9b6
commit c20f9d1534
  1. 11
      README.txt
  2. BIN
      assets/gas-giant.png
  3. BIN
      assets/stars.png
  4. 6
      client/connection.js
  5. 121
      client/controls.js
  6. 32
      client/grid.js
  7. 52
      client/join.js
  8. 100
      controls.css
  9. 16
      index.css
  10. 159
      index.html
  11. 102
      join.css
  12. 163
      server/ricochet.js
  13. 6
      socket/messenger.js
  14. 198
      style/controls.css
  15. 2
      style/grid.css
  16. 26
      style/index.css

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

@ -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;

121
client/controls.js vendored

@ -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() {

@ -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
// }
// };

@ -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();
};

@ -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;
}

@ -1,16 +0,0 @@
* {
box-sizing: border-box;
font-family: Montserrat;
}
body {
overflow: hidden;
}
/*
PALETTE:
21DFAE Aquamarine
4D3243 Plum
DE4F21 Fire
639699 Cadet
*/

@ -1,115 +1,92 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Robots</title>
<link rel="stylesheet" href="index.css">
<link rel="stylesheet" href="controls.css">
<link rel="stylesheet" href="grid.css">
<link rel="stylesheet" href="join.css">
<link href="https://fonts.googleapis.com/css2?family=Montserrat&display=swap" rel="stylesheet">
<script type='text/javascript' src='client/connection.js'></script>
<script type='text/javascript' src='client/controls.js'></script>
<script type='text/javascript' src='client/cookie.js'></script>
<script type='text/javascript' src='client/grid.js'></script>
<script type='text/javascript' src='client/icons.js'></script>
<script type='text/javascript' src='client/stack.js'></script>
</head>
<body>
<!-- <div id="join">
<div id='join-setup'>
<button type='button' class='join-button' id='join-setup-start'>Start a new game</button>
<div class='join-message'>- or -</div>
<input type="text" placeholder="Join a game" />
<button type='text' class='join-button' id='join-setup-go'>Go</button>
</div>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Robots</title>
<link rel="stylesheet" href="style/index.css">
<link rel="stylesheet" href="style/controls.css">
<link rel="stylesheet" href="style/grid.css">
<link href="https://fonts.googleapis.com/css2?family=Days+One&display=swap" rel="stylesheet">
<script type='text/javascript' src='client/connection.js'></script>
<script type='text/javascript' src='client/controls.js'></script>
<script type='text/javascript' src='client/cookie.js'></script>
<script type='text/javascript' src='client/grid.js'></script>
<script type='text/javascript' src='client/stack.js'></script>
</head>
<body>
<div id="controls-container">
<div class='controls-block' id="controls-players">
<div class="controls-title">Players</div>
<div class="controls-message"></div>
</div>
<div id="join-connect">
<div class='join-message'>Contacting Server</div>
</div>
<div class='controls-block' id="controls-moves">
<div class="controls-title">Moves</div>
<div id='controls-moves-count'>24</div>
<div id="join-error">
<div class='join-message'>Can't reach the server.</div>
<button type='text' class='join-button' id='join-setup-back'>Back</button>
</div>
</div>
-->
<div id="controls-container">
<div id="controls-manage">
<div class='controls-subtitle'>Global</div>
<div class="controls-moves-buttons-layout">
<div class='controls-button' id='controls-undo'>Undo</div>
<div class='controls-button' id='controls-reset'>Reset</div>
</div>
<div class="controls-row">
<div>Start Next Round</div>
<div>&nbsp;</div>
<div class='controls-button' id='controls-start'>Do It</div>
<div class='controls-button' id='controls-submit'>Solve the puzzle</div>
</div>
<div class="controls-row">
<div>Stop This Round</div>
<div>&nbsp;</div>
<div class='controls-button' id='controls-stop'>Yeah</div>
<div class='controls-block' id="controls-options">
<div class="controls-title">Options</div>
<div class='controls-button' id='controls-robots'>Move Robots</div>
<div class='controls-button' id='controls-walls'>Move Walls</div>
</div>
<div class="controls-row">
<div>Move Robots</div>
<div>&nbsp;</div>
<div class='controls-button' id='controls-robots'>Reposition</div>
<div class='controls-block' id='controls-connection'>
<div class="controls-message" id="controls-connecting">Connecting...</div>
<div class="controls-message" id='controls-error'>Can't reach the server.</div>
</div>
<div class="controls-row">
<div>Move Walls</div>
<div>&nbsp;</div>
<div class='controls-button' id='controls-walls'>Regenerate</div>
</div>
</div>
<div class='controls-block' id="controls-countdown">
<div class='controls-title'>Aardvark Matthews Dunkirk has a solution!</div>
<div class="controls-countdown-values-layout">
<div id="controls-solution-value">11</div>
<div id="controls-countdown-value">19</div>
</div>
<div id="controls-players">
<div class='controls-subtitle'>Players</div>
<div class='controls-button' id='controls-skip'>Skip</div>
</div>
<div class="controls-player" id='controls-players-nobody'>Nobody is in the game yet.</div>
</div>
<div class='controls-block' id="controls-solution">
<div class='controls-title'>Round over</div>
<div class="controls-message" id='controls-solution-message'>Congrats Aardvark Matthews Dunkirk!</div>
<div id="controls-local">
<div class='controls-subtitle'>Local</div>
<div class="controls-row">
<div>Moves:</div>
<div id="controls-moves"></div>
<div class='controls-button' id='controls-undo'>Undo</div>
<div class='controls-button' id='controls-reset'>Reset</div>
</div>
<div id='controls-solution-count'>24</div>
<button id='controls-submit'>Submit</button>
<div class='controls-button' id='controls-solution-replay'>Replay</div>
<div id="controls-panic">
<div class='controls-alert-urgent'></div>
<div class='controls-button' id='controls-next'>Start next round</div>
</div>
<div class="controls-row">
<div>Countdown:</div>
<div id="controls-countdown">30 seconds!</div>
<div class='controls-button' id='controls-skip'>Skip</div>
</div>
<div id="controls-footer">
<a href='http://buymeacoff.ee/5EGitAV' target='_blank'>&#9825;</a>
</div>
</div>
<div id="controls-footer">
<a href='http://buymeacoff.ee/5EGitAV' target='_blank'>&#9825;</a>
<div id="content-container">
<div id="content-grid"></div>
</div>
</div>
<div id="content-container">
<div id="content-grid"></div>
</div>
<script>
window.addEventListener('load', () => {
// Order-independent. Communication via local events.
new Connection();
new Controls();
new Grid();
new Stack();
<script>
window.addEventListener('load', () => {
// Order-independent. Communication via local events.
new Connection();
new Controls();
new Grid();
new Stack();
})
</script>
</body>
// Entry point.
const evt = new Event('L-join');
document.dispatchEvent(evt);
})
</script>
</body>
</html>

@ -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;
}

@ -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);

@ -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));
}

@ -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;
}

@ -1,6 +1,4 @@
#content-container {
background-color: #28313b;
background-image: linear-gradient(315deg, #28313b 0%, #1A2026 74%);
bottom: 0;
left: 0;
position: absolute;

@ -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
*/
Loading…
Cancel
Save