diff --git a/client/board.js b/client/board.js new file mode 100644 index 0000000..b27b33c --- /dev/null +++ b/client/board.js @@ -0,0 +1,184 @@ +const Board = function({ parent, squares }) { + this.parent = parent; + this.squares = squares; + + this.blockers = {}; + this.listeners = {}; + + this.drawSquares(); + this.resetBlockers(); +}; + +Board.prototype.resetBlockers = function() { + this.blockers = { + negativeI: null, + negativeJ: null, + positiveI: null, + positiveJ: null + }; +}; + +Board.prototype.drawRobots = function(robots) { + robots.forEach(({ color, i, j }) => { + const { x, y } = this.squares.ijToXy({ i, j }); + const s = this.squares.sideLength; + const id = color.replace('#', '').toUpperCase(); + + const robot = document.createElement('div'); + robot.className = 'content-robot'; + robot.style.background = color; + robot.style.borderRadius = (s / 2) + 'px'; + robot.style.height = s + 'px'; + robot.style.width = s + 'px'; + robot.style.left = x + 'px'; + robot.style.top = y + 'px'; + robot.id = id + + // Find if a robot is on a square: document.querySelector('[data-robot=i-j]') + robot.dataset.robot = `${i}-${j}`; + + const shadow = document.createElement('div'); + shadow.className = 'content-shadow'; + shadow.style.background = color; + shadow.style.borderRadius = (s / 2) + 'px'; + shadow.style.height = s + 'px'; + shadow.style.width = s + 'px'; + shadow.style.left = x + 'px'; + shadow.style.top = y + 'px'; + shadow.dataset.parentRobot = id; + + this.parent.appendChild(robot); + this.parent.appendChild(shadow); + + shadow.addEventListener('mousedown', this.onRobotDragStart.bind(this)); + }); +}; + +Board.prototype.drawSquares = function() { + for (let i = 0; i < this.squares.perSide; i++) { + for (let j = 0; j < this.squares.perSide; j++) { + // All squares are absolutely positioned relative to the viewport. + const { x, y } = this.squares.ijToXy({ i, j }); + + const square = document.createElement('div'); + square.className = 'content-square'; + square.style.height = this.squares.sideLength + 'px'; + square.style.width = this.squares.sideLength + 'px'; + square.style.left = x + 'px'; + square.style.top = y + 'px'; + + this.parent.appendChild(square); + } + } +}; + +Board.prototype.drawWalls = function(edges) { + edges.forEach(edge => { + const id = `wall-${edge}`; + + if (document.getElementById(id)) { + return; + } + + const [i1, j1, i2, j2] = edge.split('-'); + + console.log(edge) + + const wall = document.createElement('div'); + wall.id = id; + wall.title = edge; + + const { x, y } = this.squares.ijToXy({ i: i1, j: j1 }); + wall.style.left = x + 'px'; + wall.style.top = y + 'px'; + + // Find if edge has a wall: document.querySelector('[data-wall=i1-j1-i2-j2]') + wall.dataset.wall = edge; + + if (i1 === i2) { + wall.className = 'content-wall-y'; + wall.style.height = this.squares.sideLength + 'px'; + } else { + wall.className = 'content-wall-x'; + wall.style.width = this.squares.sideLength + 'px'; + } + + this.parent.appendChild(wall) + }) +}; + +Board.prototype.getBlockers = function({ i1, j1, i2, j2 }) { + // const upperEdge = `${i1}-${j1}-${i2}-${j2}`; + // const lowerEdge = `${i1}-${j1}-${i2}-${j2}`; + + if (i1 === i2 && j1 < j2) { + + + } else if (i1 === i2 && j1 > j2) { + + + } else if (j1 === j2 && i1 <= i2) { + this.blockers.positiveI && console.log(this.blockers.positiveI.i, i1) + + if (this.blockers.positiveI && this.blockers.positiveI.i <= i1) { + return this.blockers.positiveI.blockage; + } + + const rightEdge = `${i1 + 1}-${j1}-${i1 + 1}-${j1 + 1}`; + + const wall = document.querySelector(`[data-wall='${rightEdge}']`); + + if (wall) { + this.blockers.positiveI = { i: i1 + 1, blockage: wall }; + return wall; + } + } else { + // const leftEdge = `${i1}-${j1}-${i2}-${j2}`; + // console.log("Left edge: ", leftEdge) + } + + return null; +}; + +Board.prototype.onRobotDragStart = function(evt) { + evt.stopPropagation(); + evt.preventDefault(); + + this.listeners.onRobotDragStop = this.onRobotDragStop.bind(this); + this.listeners.onRobotDrag = this.onRobotDrag.bind(this, evt.currentTarget); + + document.body.addEventListener('mouseup', this.listeners.onRobotDragStop); + document.body.addEventListener('mousemove', this.listeners.onRobotDrag); + + const { x, y } = this.squares.ijToXy(this.squares.xyToIj(evt)); + + evt.currentTarget.style.left = x + 'px'; + evt.currentTarget.style.top = y + 'px'; +}; + +Board.prototype.onRobotDrag = function(dragTarget, evt) { + const { i: i1, j: j1 } = this.squares.xyToIj({ x: evt.x - evt.movementX, y: evt.y - evt.movementY }); + const { i: i2, j: j2 } = this.squares.xyToIj({ x: evt.x, y: evt.y }); + + const blockage = this.getBlockers({ i1, j1, i2, j2 }); + // console.warn(blockers) + + if (blockage) { + console.log(blockage) + console.log("NOPE") + // this.onRobotDragStop(); works but i don't like it + return; + } + + const { x, y } = this.squares.ijToXy({ i: i2, j: j2 }); + + dragTarget.style.left = x + 'px'; + dragTarget.style.top = y + 'px'; +}; + +Board.prototype.onRobotDragStop = function(_) { + this.resetBlockers(); + + document.body.removeEventListener('mouseup', this.listeners.onRobotDragStop); + document.body.removeEventListener('mousemove', this.listeners.onRobotDrag); +}; \ No newline at end of file diff --git a/client/controls.js b/client/controls.js new file mode 100644 index 0000000..fb6971b --- /dev/null +++ b/client/controls.js @@ -0,0 +1,53 @@ +const Controls = { + guessBuild: () => { + const container = document.getElementById('controls-guesses'); + container.querySelectorAll('.controls-guess').forEach(el => el.parentNode.removeChild(el)); + + for (let i = 1; i <= 30; i++) { + const guess = document.createElement('div'); + guess.className = 'controls-guess'; + guess.innerHTML = i; + guess.setAttribute('data-value', i); + guess.addEventListener('click', Controls.guessClick) + container.appendChild(guess); + } + }, + + guessClick: (evt) => { + alert(evt.currentTarget.dataset.value) + }, + + playerAdd: () => { + const rawInput = prompt("What is your name?"); + connection.send(JSON.stringify({ head: { type: 'playerAdd' }, body: rawInput })) + }, + + playerRemove: (rawInput) => { + connection.send(JSON.stringify({ head: { type: 'playerRemove' }, body: rawInput })) + }, + + playersUpdate: (names) => { + const container = document.getElementById('controls-players'); + console.log(names) + + Object.keys(names).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) + }); + + // container.querySelectorAll('.controls-player').forEach(el => { + // if (!names[el.id]) { + // container.removeChild(el); + // } + // }) + }, +}; \ No newline at end of file diff --git a/client/cookie.js b/client/cookie.js new file mode 100644 index 0000000..9b87af3 --- /dev/null +++ b/client/cookie.js @@ -0,0 +1,16 @@ +const Cookie = { + getCookie: function(name) { + var v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)'); + return v ? decodeURI(v[2]) : null; + }, + + setCookie: function(name, value, days) { + var d = new Date; + d.setTime(d.getTime() + 24*60*60*1000*days); + document.cookie = name + "=" + encodeURI(value) + ";path=/;expires=" + d.toGMTString(); + }, + + deleteCookie: function(name) { + Util.setCookie(name, '', -1); + } +}; \ No newline at end of file diff --git a/client/settings.js b/client/settings.js new file mode 100644 index 0000000..fed6df2 --- /dev/null +++ b/client/settings.js @@ -0,0 +1,3 @@ +const Settings = function() { + this.squaresPerSide = 20; +} \ No newline at end of file diff --git a/client/squares.js b/client/squares.js new file mode 100644 index 0000000..11b9377 --- /dev/null +++ b/client/squares.js @@ -0,0 +1,52 @@ +// This layer acts to convert squares and their edges to pixel coords and back. +// i and j are notations for the grid of squares. +// x and y are notations for absolute pixels. +// +// Grid: XY pair. 0-i on x and 0-j on y. +// Absolute: XY pair. 0-width and 0-height, relative to viewport +// Edge: A string. i1-j1-i1-j2. + +const Squares = function() { + const controlBounds = document.getElementById('controls-container').getBoundingClientRect(); + const contentBounds = document.getElementById('content-container').getBoundingClientRect(); + + const h = contentBounds.height - 40; + const w = contentBounds.width - controlBounds.right - 40; + const min = Math.min(h, w); + + // Centering + const offsetX = (min === h) ? ((w - min) / 2) : 0; + const offsetY = (min === h) ? 0 : ((h - min) / 2); + + this.perSide = 20; + this.sideLength = Math.floor(min / this.perSide); + + // Origin (top left) + this.x0 = controlBounds.left + controlBounds.width + 40 + offsetX; + this.y0 = contentBounds.top + 40 + offsetY; +} + +Squares.prototype.ijToXy = function({ i, j }) { + if ((i !== 0 && !i) || (j !== 0 && !j)) { + return + } + + return { + x: this.x0 + i * this.sideLength, + y: this.y0 + j * this.sideLength + } +} + +Squares.prototype.xyToIj = function({ x, y }) { + const i = Math.floor((x - this.x0) / this.sideLength); + const j = Math.floor((y - this.y0) / this.sideLength); + + const min = 0; + const max = this.perSide - 1; + + // Coalesce outlying results to grid edges. + return { + i: Math.min(Math.max(min, i), max), + j: Math.min(Math.max(min, j), max), + } +} \ No newline at end of file diff --git a/ricochet.css b/index.css similarity index 59% rename from ricochet.css rename to index.css index 423caaa..70f64da 100644 --- a/ricochet.css +++ b/index.css @@ -3,32 +3,44 @@ font-family: Montserrat; } -.controls-container { +body { + overflow: hidden; +} + +/* +4D3243 +9A748C +639699 +364B4D +*/ + +#controls-container { background: #e7e7e7; bottom: 0; - left: 0; + left: 20px; position: absolute; top: 0; width: 300px; + z-index: 1; } .controls-subtitle { - background-color: #555; + background-color: #4D3243; color: #fff; + padding: 12px; } .controls-title { - background-color: #222; + background-color: #639978; background-image: url('sprite-robots.png'); - color: #fff; + /*color: #fff;*/ font-size: 12px; line-height: 48px; + margin-bottom: 24px; text-align: center; } .controls-room { - line-height: 48px; - text-align: center; } .controls-room span { @@ -61,61 +73,44 @@ background: orange; } -.board-container { - background: conic-gradient(lime 0 25%, yellow 25% 50%, red 50% 75%, blue 75% 100%); +#content-container { + background-color: #28313b; + background-image: linear-gradient(315deg, #28313b 0%, #1A2026 74%); bottom: 0; - left: 300px; + left: 0; position: absolute; right: 0; top: 0; + z-index: 0; } -#board-squares { - border-color: #aaa; - border-style: solid; - border-width: 1px 0 0 1px; - position: relative; -} - -.board-square{ +.content-square { background: #ddd; border-style: solid; border-color: #aaa; border-width: 0 1px 1px 0; - float: left; - height: 40px; - width: 40px; + position: absolute; } -.board-wall-x { +.content-wall-x { background: #222; height: 8px; margin-top: -4px; position: absolute; - width: 40px; } -.board-wall-y { +.content-wall-y { background: #222; - height: 40px; margin-left: -4px; position: absolute; width: 8px; } -.board-robot { - /*border-radius: 20px;*/ - border: 2px solid transparent; - height: 36px; - margin: 2px; +.content-robot { + opacity: 0.25; position: absolute; - width: 36px; } -.board-robot-shadow { - background: red; - height: 36px; - opacity: 0.75; +.content-shadow { position: absolute; - width: 36px; } \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..f211b90 --- /dev/null +++ b/index.html @@ -0,0 +1,89 @@ + + +
+ + +